*/ function list_types(string $text): array { $out = []; if (!preg_match_all( '/^(?:abstract\s+|final\s+)?(class|trait|interface)\s+(\w+)/m', $text, $m, PREG_SET_ORDER | PREG_OFFSET_CAPTURE )) { return []; } foreach ($m as $match) { $decl = (int) $match[0][1]; $kind = $match[1][0]; $name = $match[2][0]; // Include preceding docblock. $before = substr($text, 0, $decl); $start = $decl; if (preg_match('/(?:\n|^)(\s*\/\*\*[\s\S]*?\*\/\s*)\z/', $before, $dm, PREG_OFFSET_CAPTURE)) { $start = (int) $dm[1][1]; } $out[] = ['name' => $name, 'kind' => $kind, 'start' => $start, 'decl' => $decl]; } return $out; } function extract_by_index(string $text, array $types, int $idx): string { $start = $types[$idx]['start']; // End is start of next type or EOF (but need end of braces of this type). $brace = strpos($text, '{', $types[$idx]['decl']); $len = strlen($text); $depth = 0; $in_str = false; $str_q = ''; $in_line = false; $in_block = false; $escape = false; for ($i = $brace; $i < $len; $i++) { $ch = $text[$i]; $next = $i + 1 < $len ? $text[$i + 1] : ''; if ($in_line) { if ($ch === "\n") { $in_line = false; } continue; } if ($in_block) { if ($ch === '*' && $next === '/') { $in_block = false; $i++; } continue; } if ($in_str) { if ($escape) { $escape = false; continue; } if ($ch === '\\') { $escape = true; continue; } if ($ch === $str_q) { $in_str = false; } continue; } if ($ch === '/' && $next === '/') { $in_line = true; $i++; continue; } if ($ch === '/' && $next === '*') { $in_block = true; $i++; continue; } if ($ch === '"' || $ch === "'") { $in_str = true; $str_q = $ch; continue; } if ($ch === '{') { $depth++; } elseif ($ch === '}') { $depth--; if ($depth === 0) { return substr($text, $start, $i - $start + 1); } } } throw new RuntimeException('brace match failed for ' . $types[$idx]['name']); } $rep = []; foreach ($map['entries'] as $e) { $rep[$e['old']] = '\\' . $e['new_fqcn']; } $rep['AKSRT_Creator_Revenue'] = '\\AKSRT\\Payment\\CreatorRevenue'; uksort($rep, static fn($a, $b) => strlen($b) <=> strlen($a)); function rewrite_refs(string $code, array $rep): string { $preserve = [ 'AKSRT_DIR', 'AKSRT_URI', 'AKSRT_VERSION', 'AKSRT_NO_EMAIL_GATE', 'AKSRT_PAY_TYPE_NO', 'AKSRT_PAY_TYPE_READING', 'AKSRT_PAY_TYPE_DOWNLOAD', 'AKSRT_PAY_TYPE_GALLERY', 'AKSRT_PAY_TYPE_VIDEO', 'AKSRT_PAY_TYPE_VIP', ]; foreach ($preserve as $i => $tok) { $code = str_replace($tok, "\0P{$i}\0", $code); } $pattern = '/\b(' . implode('|', array_map(static fn($k) => preg_quote($k, '/'), array_keys($rep))) . ')\b/'; $code = preg_replace_callback($pattern, static fn($m) => $rep[$m[1]] ?? $m[1], $code) ?? $code; foreach ($preserve as $i => $tok) { $code = str_replace("\0P{$i}\0", $tok, $code); } return $code; } $files = [ 'inc/classes/payment/class-aksrt-payment-gateway.php', 'inc/classes/widgets/class-aksrt-forum-widgets.php', 'inc/classes/widgets/class-aksrt-home-widgets.php', 'inc/classes/widgets/class-aksrt-shop-widgets.php', 'inc/classes/widgets/class-aksrt-widgets-batch1.php', 'inc/classes/widgets/class-aksrt-widgets-new.php', 'inc/classes/widgets/class-aksrt-widgets-original.php', ]; foreach ($files as $git_path) { $text = shell_exec('git -C ' . escapeshellarg($root) . ' show HEAD:' . escapeshellarg($git_path)); if (!$text) { fwrite(STDERR, "git show fail $git_path\n"); continue; } $types = list_types($text); echo $git_path, ' has ', count($types), " types: ", implode(', ', array_column($types, 'name')), "\n"; foreach ($types as $idx => $info) { $old = $info['name']; if (!isset($by_old[$old])) { fwrite(STDERR, " unmapped $old\n"); continue; } $entry = $by_old[$old]; $chunk = extract_by_index($text, $types, $idx); // Rename: only the declaration line's name. $chunk = preg_replace( '/^((?:abstract\s+|final\s+)?' . preg_quote($info['kind'], '/') . '\s+)' . preg_quote($old, '/') . '\b/m', '$1' . $entry['short'], $chunk, 1 ); $chunk = rewrite_refs($chunk, $rep); $ns = $entry['namespace']; // Shorten same-namespace refs BUT not if it would re-break declaration - declaration already short. $chunk = preg_replace('/\\\\' . preg_quote($ns, '/') . '\\\\(\w+)/', '$1', $chunk) ?? $chunk; // Global WP types. foreach (['WP_Widget', 'WP_REST_Request', 'WP_REST_Response', 'WP_Error', 'WP_Post', 'WP_User', 'WP_Query'] as $g) { $chunk = preg_replace('/(? {$entry['short']}\n"); fwrite(STDERR, " head: " . substr($out, 0, 250) . "\n"); continue; } $path = $root . '/' . $entry['new_path']; if (!is_dir(dirname($path))) { mkdir(dirname($path), 0777, true); } file_put_contents($path, $out); echo " OK {$old} => {$entry['new_path']} (", strlen($out), " bytes)\n"; } } // Globalize WP_* in all src files once more carefully $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root . '/src/AKSRT')); foreach ($rii as $f) { if (!$f->isFile() || !str_ends_with($f->getFilename(), '.php')) { continue; } $p = $f->getPathname(); $t = (string) file_get_contents($p); $orig = $t; foreach (['WP_REST_Request', 'WP_REST_Response', 'WP_Error', 'WP_Post', 'WP_User', 'WP_Query', 'WP_Widget', 'WP_Admin_Bar', 'WP_HTTP_Response', 'WP_Term'] as $g) { // only when used as type/extends/new/catch - simple: bare WP_ not already escaped $t = preg_replace('/(?