Merge pull request 'chore(tools): drop one-shot PSR-4 migrators after apply' (#193) from worktree-psr4-quantify into main
Reviewed-on: CYL/wordpress-theme-aksrt#193
This commit is contained in:
@@ -53,8 +53,8 @@ Domain folders were frozen after the root→domain move. Theme types then migrat
|
||||
|
||||
### Map tooling
|
||||
|
||||
- `php tools/build-psr4-map.php --write` — inventory / map under `tools/psr4-map/`
|
||||
- Migration helpers under `tools/psr4-*.php` (historical; re-run only with care)
|
||||
- `php tools/build-psr4-map.php --write` — read-only inventory / map under `tools/psr4-map/`
|
||||
- One-shot mass-rewrite migrators are **not** kept in-tree (completed migration; AV false positives on file-tree rewriters)
|
||||
|
||||
## Out of scope
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Robust multi-file splitter for PSR-4.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
$map = json_decode((string) file_get_contents($root . '/tools/psr4-map/map.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
$by_old = [];
|
||||
foreach ($map['entries'] as $e) {
|
||||
$by_old[$e['old']] = $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all top-level type declarations with start offsets.
|
||||
*
|
||||
* @return list<array{name:string,kind:string,start:int,decl:int}>
|
||||
*/
|
||||
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('/(?<!\\\\)\b' . $g . '\b/', '\\' . $g, $chunk) ?? $chunk;
|
||||
}
|
||||
$chunk = str_replace('\\\\', '\\', $chunk);
|
||||
|
||||
$out = "<?php\n/**\n * @package AKSRT\n */\n\nnamespace {$ns};\n\ndefined('ABSPATH') || exit;\n\n" . rtrim($chunk) . "\n";
|
||||
// Verify
|
||||
if (!preg_match('/\b' . preg_quote($info['kind'], '/') . '\s+' . preg_quote($entry['short'], '/') . '\b/', $out)) {
|
||||
fwrite(STDERR, " VERIFY FAIL $old -> {$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('/(?<!\\\\|[\w\\\\])' . preg_quote($g, '/') . '\b/', '\\' . $g, $t) ?? $t;
|
||||
}
|
||||
if ($t !== $orig) {
|
||||
file_put_contents($p, $t);
|
||||
}
|
||||
}
|
||||
|
||||
echo "done\n";
|
||||
@@ -1,671 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* One-shot PSR-4 migrator for AKSRT theme.
|
||||
*
|
||||
* Prerequisites: php tools/build-psr4-map.php --write
|
||||
*
|
||||
* Usage:
|
||||
* php tools/psr4-migrate.php --dry-run
|
||||
* php tools/psr4-migrate.php --apply
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
$apply = in_array('--apply', $argv, true);
|
||||
$dry = !$apply;
|
||||
|
||||
$map_file = $root . '/tools/psr4-map/map.json';
|
||||
if (!is_file($map_file)) {
|
||||
fwrite(STDERR, "Missing map. Run: php tools/build-psr4-map.php --write\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** @var array{entries: list<array<string,mixed>>, procedural: list<string>} $map */
|
||||
$map = json_decode((string) file_get_contents($map_file), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
const PRESERVE_TOKENS = [
|
||||
'AKSRT_DIR',
|
||||
'AKSRT_URI',
|
||||
'AKSRT_VERSION',
|
||||
'AKSRT_NO_EMAIL_GATE',
|
||||
];
|
||||
|
||||
/**
|
||||
* Find declaration start offset for a type name.
|
||||
*/
|
||||
function aksrt_find_decl_start(string $text, string $old_name): ?int {
|
||||
if (!preg_match(
|
||||
'/(?:abstract\s+|final\s+)?(?:class|trait|interface)\s+' . preg_quote($old_name, '/') . '\b/',
|
||||
$text,
|
||||
$m,
|
||||
PREG_OFFSET_CAPTURE
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
$decl = (int) $m[0][1];
|
||||
// Include immediately preceding docblock if present.
|
||||
$before = substr($text, 0, $decl);
|
||||
if (preg_match('/(?:\n|^)\s*\/\*\*[\s\S]*?\*\/\s*\z/', $before, $dm, PREG_OFFSET_CAPTURE)) {
|
||||
return (int) $dm[0][1];
|
||||
}
|
||||
return $decl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract one class/trait/interface by brace matching from its declaration.
|
||||
*/
|
||||
function aksrt_extract_type(string $text, string $old_name): ?string {
|
||||
$start = aksrt_find_decl_start($text, $old_name);
|
||||
if ($start === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$brace = strpos($text, '{', $start);
|
||||
if ($brace === false) {
|
||||
// Possible empty interface? require body.
|
||||
return null;
|
||||
}
|
||||
|
||||
$len = strlen($text);
|
||||
$depth = 0;
|
||||
$in_str = false;
|
||||
$str_q = '';
|
||||
$in_line_comment = false;
|
||||
$in_block_comment = false;
|
||||
$escape = false;
|
||||
|
||||
for ($i = $brace; $i < $len; $i++) {
|
||||
$ch = $text[$i];
|
||||
$next = $i + 1 < $len ? $text[$i + 1] : '';
|
||||
|
||||
if ($in_line_comment) {
|
||||
if ($ch === "\n") {
|
||||
$in_line_comment = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($in_block_comment) {
|
||||
if ($ch === '*' && $next === '/') {
|
||||
$in_block_comment = 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_comment = true;
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '/' && $next === '*') {
|
||||
$in_block_comment = true;
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '"' || $ch === "'") {
|
||||
$in_str = true;
|
||||
$str_q = $ch;
|
||||
continue;
|
||||
}
|
||||
// Heredoc/nowdoc not handled fully — rare in this codebase.
|
||||
|
||||
if ($ch === '{') {
|
||||
$depth++;
|
||||
} elseif ($ch === '}') {
|
||||
$depth--;
|
||||
if ($depth === 0) {
|
||||
return substr($text, $start, $i - $start + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string,mixed>> $entries
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function aksrt_replacements(array $entries): array {
|
||||
$rep = [];
|
||||
foreach ($entries as $e) {
|
||||
$rep[$e['old']] = '\\' . $e['new_fqcn'];
|
||||
}
|
||||
uksort($rep, static fn($a, $b) => strlen($b) <=> strlen($a));
|
||||
return $rep;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $rep
|
||||
*/
|
||||
function aksrt_rewrite_refs(string $code, array $rep): string {
|
||||
$protected = [];
|
||||
$i = 0;
|
||||
foreach (PRESERVE_TOKENS as $tok) {
|
||||
$ph = "\0PRESERVE{$i}\0";
|
||||
$protected[$ph] = $tok;
|
||||
$code = str_replace($tok, $ph, $code);
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($rep) {
|
||||
$pattern = '/\b(' . implode('|', array_map(static fn($k) => preg_quote($k, '/'), array_keys($rep))) . ')\b/';
|
||||
$code = preg_replace_callback($pattern, static function ($m) use ($rep) {
|
||||
return $rep[$m[1]] ?? $m[1];
|
||||
}, $code) ?? $code;
|
||||
}
|
||||
|
||||
foreach ($protected as $ph => $tok) {
|
||||
$code = str_replace($ph, $tok, $code);
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $entry
|
||||
* @param array<string,string> $rep
|
||||
*/
|
||||
function aksrt_build_namespaced_file(array $entry, string $body, array $rep): string {
|
||||
$kind = $entry['kind'];
|
||||
$old = $entry['old'];
|
||||
$short = $entry['short'];
|
||||
$ns = $entry['namespace'];
|
||||
|
||||
$body = preg_replace(
|
||||
'/(abstract\s+|final\s+)?(' . preg_quote($kind, '/') . ')\s+' . preg_quote($old, '/') . '\b/',
|
||||
'$1$2 ' . $short,
|
||||
$body,
|
||||
1
|
||||
) ?? $body;
|
||||
|
||||
$body = aksrt_rewrite_refs($body, $rep);
|
||||
|
||||
// Same-namespace FQCN → short name.
|
||||
$body = preg_replace(
|
||||
'/\\\\' . preg_quote($ns, '/') . '\\\\(\w+)/',
|
||||
'$1',
|
||||
$body
|
||||
) ?? $body;
|
||||
|
||||
$out = "<?php\n/**\n * @package AKSRT\n */\n\nnamespace {$ns};\n\ndefined('ABSPATH') || exit;\n\n";
|
||||
$out .= rtrim($body) . "\n";
|
||||
return $out;
|
||||
}
|
||||
|
||||
function aksrt_ensure_dir(string $file): void {
|
||||
$dir = dirname($file);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
throw new RuntimeException("mkdir failed: {$dir}");
|
||||
}
|
||||
}
|
||||
|
||||
function aksrt_write(string $abs, string $contents, bool $dry): void {
|
||||
if ($dry) {
|
||||
echo '[dry] write ' . $abs . ' (' . strlen($contents) . " bytes)\n";
|
||||
return;
|
||||
}
|
||||
aksrt_ensure_dir($abs);
|
||||
if (file_put_contents($abs, $contents) === false) {
|
||||
throw new RuntimeException("write failed: {$abs}");
|
||||
}
|
||||
}
|
||||
|
||||
$rep = aksrt_replacements($map['entries']);
|
||||
$entries = $map['entries'];
|
||||
|
||||
echo ($dry ? 'DRY-RUN' : 'APPLY') . ' — ' . count($entries) . " types\n";
|
||||
|
||||
$old_type_paths = [];
|
||||
$failed = [];
|
||||
$written = 0;
|
||||
foreach ($entries as $entry) {
|
||||
$old_abs = $root . '/' . $entry['old_path'];
|
||||
$new_abs = $root . '/' . $entry['new_path'];
|
||||
$old_type_paths[$entry['old_path']] = true;
|
||||
|
||||
if (!is_file($old_abs)) {
|
||||
if (is_file($new_abs)) {
|
||||
continue;
|
||||
}
|
||||
$failed[] = "missing source {$entry['old_path']} for {$entry['old']}";
|
||||
continue;
|
||||
}
|
||||
|
||||
$text = (string) file_get_contents($old_abs);
|
||||
$chunk = aksrt_extract_type($text, $entry['old']);
|
||||
if ($chunk === null) {
|
||||
$failed[] = "cannot extract {$entry['old']} from {$entry['old_path']}";
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = aksrt_build_namespaced_file($entry, $chunk, $rep);
|
||||
aksrt_write($new_abs, $contents, $dry);
|
||||
$written++;
|
||||
}
|
||||
echo "Type files: {$written}\n";
|
||||
|
||||
if ($failed) {
|
||||
fwrite(STDERR, "Extraction failures (" . count($failed) . "):\n");
|
||||
foreach (array_slice($failed, 0, 30) as $f) {
|
||||
fwrite(STDERR, " {$f}\n");
|
||||
}
|
||||
if (!$dry) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$proc_moves = [
|
||||
'inc/classes/auth/class-aksrt-captcha.php' => 'inc/modules/captcha.php',
|
||||
'inc/classes/payment/class-aksrt-creator-revenue.php' => 'inc/modules/creator-revenue.php',
|
||||
'inc/classes/user/class-aksrt-user-notifications.php' => 'inc/modules/user-notifications.php',
|
||||
'inc/classes/user/class-aksrt-user.php' => 'inc/modules/user-hub.php',
|
||||
'inc/classes/user/user-parts/aksrt-user-account-ajax.php' => 'inc/modules/user-parts/account-ajax.php',
|
||||
'inc/classes/user/user-parts/aksrt-user-routing.php' => 'inc/modules/user-parts/routing.php',
|
||||
'inc/classes/user/user-parts/aksrt-user-template-registry.php' => 'inc/modules/user-parts/template-registry.php',
|
||||
'inc/classes/user/user-parts/aksrt-user-urls.php' => 'inc/modules/user-parts/urls.php',
|
||||
'inc/classes/widgets/class-aksrt-widgets.php' => 'inc/modules/widgets-register.php',
|
||||
];
|
||||
|
||||
foreach ($proc_moves as $from => $to) {
|
||||
$from_abs = $root . '/' . $from;
|
||||
$to_abs = $root . '/' . $to;
|
||||
if (!is_file($from_abs) && is_file($to_abs)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_file($from_abs)) {
|
||||
echo "warn: procedural missing {$from}\n";
|
||||
continue;
|
||||
}
|
||||
$text = (string) file_get_contents($from_abs);
|
||||
if ($from === 'inc/classes/user/class-aksrt-user.php') {
|
||||
$text = str_replace(
|
||||
[
|
||||
"__DIR__ . '/user-parts/aksrt-user-template-registry.php'",
|
||||
"__DIR__ . '/user-parts/aksrt-user-urls.php'",
|
||||
"__DIR__ . '/user-parts/aksrt-user-routing.php'",
|
||||
"__DIR__ . '/user-parts/aksrt-user-account-ajax.php'",
|
||||
],
|
||||
[
|
||||
"__DIR__ . '/user-parts/template-registry.php'",
|
||||
"__DIR__ . '/user-parts/urls.php'",
|
||||
"__DIR__ . '/user-parts/routing.php'",
|
||||
"__DIR__ . '/user-parts/account-ajax.php'",
|
||||
],
|
||||
$text
|
||||
);
|
||||
}
|
||||
$text = aksrt_rewrite_refs($text, $rep);
|
||||
aksrt_write($to_abs, $text, $dry);
|
||||
}
|
||||
|
||||
$scan_roots = [
|
||||
$root . '/inc/helpers',
|
||||
$root . '/inc/core-hooks',
|
||||
$root . '/inc/modules',
|
||||
$root . '/templates',
|
||||
$root . '/tools',
|
||||
];
|
||||
$scan_files = [
|
||||
$root . '/functions.php',
|
||||
$root . '/inc/bootstrap.php',
|
||||
$root . '/inc/core-hooks.php',
|
||||
$root . '/uninstall.php',
|
||||
$root . '/header.php',
|
||||
$root . '/footer.php',
|
||||
$root . '/index.php',
|
||||
$root . '/single.php',
|
||||
$root . '/page.php',
|
||||
$root . '/archive.php',
|
||||
$root . '/search.php',
|
||||
$root . '/category.php',
|
||||
$root . '/tag.php',
|
||||
$root . '/author.php',
|
||||
$root . '/404.php',
|
||||
$root . '/home.php',
|
||||
$root . '/front-page.php',
|
||||
$root . '/comments.php',
|
||||
$root . '/sidebar.php',
|
||||
$root . '/image.php',
|
||||
$root . '/go.php',
|
||||
$root . '/slim-forum.php',
|
||||
$root . '/archive-resources.php',
|
||||
];
|
||||
|
||||
$php_files = $scan_files;
|
||||
foreach ($scan_roots as $dir) {
|
||||
if (!is_dir($dir)) {
|
||||
continue;
|
||||
}
|
||||
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
|
||||
foreach ($rii as $file) {
|
||||
if ($file->isFile() && str_ends_with($file->getFilename(), '.php')) {
|
||||
$php_files[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
$php_files = array_values(array_unique($php_files));
|
||||
$rewritten = 0;
|
||||
foreach ($php_files as $abs) {
|
||||
if (!is_file($abs)) {
|
||||
continue;
|
||||
}
|
||||
$rel = str_replace('\\', '/', substr($abs, strlen($root) + 1));
|
||||
if (str_starts_with($rel, 'tools/build-psr4-map.php')
|
||||
|| str_starts_with($rel, 'tools/psr4-migrate.php')
|
||||
|| str_starts_with($rel, 'tools/psr4-map/')) {
|
||||
continue;
|
||||
}
|
||||
if (str_starts_with($rel, 'src/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orig = (string) file_get_contents($abs);
|
||||
$next = aksrt_rewrite_refs($orig, $rep);
|
||||
if ($next !== $orig) {
|
||||
aksrt_write($abs, $next, $dry);
|
||||
$rewritten++;
|
||||
}
|
||||
}
|
||||
echo "Reference rewrites in non-src PHP: {$rewritten} files\n";
|
||||
|
||||
// Helper pure-class files become stubs after class move — rewrite domain-model/shortcodes if they still have class bodies (migrator already wrote src copies).
|
||||
// After apply, delete old type sources and helper class-only file.
|
||||
$helper_pure = [
|
||||
'inc/helpers/class-aksrt-nav-walker.php',
|
||||
];
|
||||
|
||||
if (!$dry) {
|
||||
foreach (array_keys($old_type_paths) as $rel) {
|
||||
$abs = $root . '/' . $rel;
|
||||
if (is_file($abs)) {
|
||||
unlink($abs);
|
||||
}
|
||||
}
|
||||
foreach (array_keys($proc_moves) as $rel) {
|
||||
$abs = $root . '/' . $rel;
|
||||
if (is_file($abs)) {
|
||||
unlink($abs);
|
||||
}
|
||||
}
|
||||
foreach ($helper_pure as $rel) {
|
||||
$abs = $root . '/' . $rel;
|
||||
if (is_file($abs)) {
|
||||
unlink($abs);
|
||||
}
|
||||
}
|
||||
// helpers that still contain only moved classes: strip by rewriting shortcodes/domain-model to empty require-safe stubs if class was extracted.
|
||||
foreach (['inc/helpers/aksrt-shortcodes.php', 'inc/helpers/aksrt-domain-model.php'] as $rel) {
|
||||
$abs = $root . '/' . $rel;
|
||||
if (!is_file($abs)) {
|
||||
continue;
|
||||
}
|
||||
$t = (string) file_get_contents($abs);
|
||||
// If file still has class keyword for types we moved, replace with comment stub.
|
||||
if (preg_match('/\b(class|trait|interface)\s+/', $t)) {
|
||||
file_put_contents(
|
||||
$abs,
|
||||
"<?php\n/**\n * Procedural helpers previously co-located with classes (now under src/AKSRT).\n *\n * @package AKSRT\n */\n\ndefined('ABSPATH') || exit;\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Prune empty dirs under inc/classes.
|
||||
$classes = $root . '/inc/classes';
|
||||
if (is_dir($classes)) {
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($classes, FilesystemIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isDir()) {
|
||||
@rmdir($file->getPathname());
|
||||
} else {
|
||||
echo 'leftover file: ' . $file->getPathname() . "\n";
|
||||
}
|
||||
}
|
||||
@rmdir($classes);
|
||||
}
|
||||
} else {
|
||||
echo '[dry] would delete ' . count($old_type_paths) . " old type paths + procedural + prune inc/classes\n";
|
||||
}
|
||||
|
||||
$lookup = [];
|
||||
foreach ($entries as $e) {
|
||||
$lookup[$e['old']] = $e['new_fqcn'];
|
||||
}
|
||||
$auth_fq = $lookup['AKSRT_Auth_Service'] ?? 'AKSRT\\Auth\\Service';
|
||||
$settings_fq = $lookup['AKSRT_Settings'] ?? 'AKSRT\\Settings\\Settings';
|
||||
|
||||
$bootstrap = <<<PHP
|
||||
<?php
|
||||
/**
|
||||
* AKSRT Theme Bootstrap Loader
|
||||
*
|
||||
* Helpers + procedural modules + core hooks.
|
||||
* Domain classes load via Composer PSR-4 (src/AKSRT → AKSRT\\).
|
||||
*
|
||||
* @package AKSRT
|
||||
* @version 3.4.0
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
// ============================================================
|
||||
// Stage 1: helpers (must load before runtime that calls them)
|
||||
// ============================================================
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-icons.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-domain-model.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-template-tags.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-frontend-tags.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-flow.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-homepage.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-single-components.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-single.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-yiyan.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-single-render.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-archive.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-archive-filters.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-comments.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-forum.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-slider.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-vip.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-auth-ui.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-i18n-switch.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-posts-renderer.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-resource.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-pay.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-coupon-order.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-rewards.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-transactions.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-commerce.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-user-dashboard.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-user-center.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-user-home.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-shortcodes.php';
|
||||
|
||||
// ============================================================
|
||||
// Stage 2: procedural modules (not PSR-4 types)
|
||||
// ============================================================
|
||||
require_once AKSRT_DIR . '/inc/modules/captcha.php';
|
||||
require_once AKSRT_DIR . '/inc/modules/user-hub.php';
|
||||
require_once AKSRT_DIR . '/inc/modules/user-notifications.php';
|
||||
require_once AKSRT_DIR . '/inc/modules/creator-revenue.php';
|
||||
require_once AKSRT_DIR . '/inc/modules/widgets-register.php';
|
||||
|
||||
// ============================================================
|
||||
// Stage 3: core hooks
|
||||
// ============================================================
|
||||
require_once AKSRT_DIR . '/inc/core-hooks.php';
|
||||
|
||||
// ============================================================
|
||||
// Stage 4: auth service global hooks
|
||||
// ============================================================
|
||||
if (class_exists(\\{$auth_fq}::class)) {
|
||||
\\\\{$auth_fq}::init();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Stage 5: email verification backfill
|
||||
// ============================================================
|
||||
add_action('init', function () {
|
||||
if (defined('AKSRT_NO_EMAIL_GATE') && AKSRT_NO_EMAIL_GATE) {
|
||||
return;
|
||||
}
|
||||
if (!class_exists(\\{$auth_fq}::class) || !class_exists(\\{$settings_fq}::class)) {
|
||||
return;
|
||||
}
|
||||
if (!\\\\{$settings_fq}::registration_requires_email_verification()) {
|
||||
return;
|
||||
}
|
||||
if (get_option('aksrt_email_verification_backfill_done')) {
|
||||
return;
|
||||
}
|
||||
if (!is_admin() && !(defined('DOING_CRON') && DOING_CRON)) {
|
||||
return;
|
||||
}
|
||||
\\\\{$auth_fq}::backfill_legacy_users_as_verified();
|
||||
}, 5);
|
||||
|
||||
add_action('aksrt_email_verification_backfill_batch', function (\$offset = 0) {
|
||||
if (class_exists(\\{$auth_fq}::class)) {
|
||||
\\\\{$auth_fq}::run_async_backfill_batch((int) \$offset);
|
||||
}
|
||||
}, 10, 1);
|
||||
|
||||
// ============================================================
|
||||
// Stage 6: settings migration stub (admin only)
|
||||
// ============================================================
|
||||
add_action('admin_init', function () {
|
||||
}, 5);
|
||||
|
||||
PHP;
|
||||
|
||||
// Fix accidental double backslashes from heredoc interpolation mess.
|
||||
$bootstrap = str_replace(['\\\\' . $auth_fq, '\\\\' . $settings_fq, '\\{' ], ['\\' . $auth_fq, '\\' . $settings_fq, '{'], $bootstrap);
|
||||
// Cleaner: rebuild bootstrap without broken escapes.
|
||||
$bootstrap = "<?php\n"
|
||||
. "/**\n * AKSRT Theme Bootstrap Loader\n *\n"
|
||||
. " * Helpers + procedural modules + core hooks.\n"
|
||||
. " * Domain classes load via Composer PSR-4 (src/AKSRT → AKSRT\\).\n"
|
||||
. " *\n * @package AKSRT\n * @version 3.4.0\n */\n\n"
|
||||
. "defined('ABSPATH') || exit;\n\n"
|
||||
. "// Stage 1: helpers\n";
|
||||
|
||||
$helpers = [
|
||||
'aksrt-icons.php',
|
||||
'aksrt-domain-model.php',
|
||||
'aksrt-template-tags.php',
|
||||
'aksrt-frontend-tags.php',
|
||||
'aksrt-flow.php',
|
||||
'aksrt-homepage.php',
|
||||
'aksrt-single-components.php',
|
||||
'aksrt-single.php',
|
||||
'aksrt-yiyan.php',
|
||||
'aksrt-single-render.php',
|
||||
'aksrt-archive.php',
|
||||
'aksrt-archive-filters.php',
|
||||
'aksrt-comments.php',
|
||||
'aksrt-forum.php',
|
||||
'aksrt-slider.php',
|
||||
'aksrt-vip.php',
|
||||
'aksrt-auth-ui.php',
|
||||
'aksrt-i18n-switch.php',
|
||||
'aksrt-posts-renderer.php',
|
||||
'aksrt-resource.php',
|
||||
'aksrt-pay.php',
|
||||
'aksrt-coupon-order.php',
|
||||
'aksrt-credits.php',
|
||||
'aksrt-null.php',
|
||||
'aksrt-identity.php',
|
||||
'aksrt-rewards.php',
|
||||
'menu-decoration.php',
|
||||
'aksrt-transactions.php',
|
||||
'aksrt-commerce.php',
|
||||
'aksrt-user-dashboard.php',
|
||||
'aksrt-user-center.php',
|
||||
'aksrt-user-home.php',
|
||||
'aksrt-shortcodes.php',
|
||||
];
|
||||
foreach ($helpers as $h) {
|
||||
$bootstrap .= "require_once AKSRT_DIR . '/inc/helpers/{$h}';\n";
|
||||
}
|
||||
$bootstrap .= "\n// Stage 2: procedural modules\n";
|
||||
foreach (['captcha.php', 'user-hub.php', 'user-notifications.php', 'creator-revenue.php', 'widgets-register.php'] as $m) {
|
||||
$bootstrap .= "require_once AKSRT_DIR . '/inc/modules/{$m}';\n";
|
||||
}
|
||||
$bootstrap .= "\n// Stage 3: core hooks\n";
|
||||
$bootstrap .= "require_once AKSRT_DIR . '/inc/core-hooks.php';\n\n";
|
||||
$bootstrap .= "// Stage 4: auth service hooks\n";
|
||||
$bootstrap .= "if (class_exists(\\{$auth_fq}::class)) {\n\t\\{$auth_fq}::init();\n}\n\n";
|
||||
// The above still has escape issues - build with concatenation only:
|
||||
$bootstrap = "<?php\n/**\n * AKSRT Theme Bootstrap Loader\n *\n * Helpers + procedural modules + core hooks.\n * Domain classes load via Composer PSR-4 (src/AKSRT).\n *\n * @package AKSRT\n * @version 3.4.0\n */\n\ndefined('ABSPATH') || exit;\n\n";
|
||||
$bootstrap .= "// Stage 1: helpers\n";
|
||||
foreach ($helpers as $h) {
|
||||
$bootstrap .= "require_once AKSRT_DIR . '/inc/helpers/{$h}';\n";
|
||||
}
|
||||
$bootstrap .= "\n// Stage 2: procedural modules\n";
|
||||
foreach (['captcha.php', 'user-hub.php', 'user-notifications.php', 'creator-revenue.php', 'widgets-register.php'] as $mod) {
|
||||
$bootstrap .= "require_once AKSRT_DIR . '/inc/modules/{$mod}';\n";
|
||||
}
|
||||
$bootstrap .= "\n// Stage 3: core hooks\nrequire_once AKSRT_DIR . '/inc/core-hooks.php';\n\n";
|
||||
$bootstrap .= "// Stage 4: auth service hooks\n";
|
||||
$bootstrap .= 'if (class_exists(\\' . $auth_fq . '::class)) {' . "\n";
|
||||
$bootstrap .= "\t\\" . $auth_fq . "::init();\n}\n\n";
|
||||
$bootstrap .= "// Stage 5: email verification backfill\n";
|
||||
$bootstrap .= "add_action('init', function () {\n";
|
||||
$bootstrap .= "\tif (defined('AKSRT_NO_EMAIL_GATE') && AKSRT_NO_EMAIL_GATE) {\n\t\treturn;\n\t}\n";
|
||||
$bootstrap .= "\tif (!class_exists(\\" . $auth_fq . '::class) || !class_exists(\\' . $settings_fq . "::class)) {\n\t\treturn;\n\t}\n";
|
||||
$bootstrap .= "\tif (!\\" . $settings_fq . "::registration_requires_email_verification()) {\n\t\treturn;\n\t}\n";
|
||||
$bootstrap .= "\tif (get_option('aksrt_email_verification_backfill_done')) {\n\t\treturn;\n\t}\n";
|
||||
$bootstrap .= "\tif (!is_admin() && !(defined('DOING_CRON') && DOING_CRON)) {\n\t\treturn;\n\t}\n";
|
||||
$bootstrap .= "\t\\" . $auth_fq . "::backfill_legacy_users_as_verified();\n}, 5);\n\n";
|
||||
$bootstrap .= "add_action('aksrt_email_verification_backfill_batch', function (\$offset = 0) {\n";
|
||||
$bootstrap .= "\tif (class_exists(\\" . $auth_fq . "::class)) {\n";
|
||||
$bootstrap .= "\t\t\\" . $auth_fq . "::run_async_backfill_batch((int) \$offset);\n\t}\n}, 10, 1);\n\n";
|
||||
$bootstrap .= "add_action('admin_init', function () {\n}, 5);\n";
|
||||
|
||||
aksrt_write($root . '/inc/bootstrap.php', $bootstrap, $dry);
|
||||
|
||||
$composer_path = $root . '/composer.json';
|
||||
$composer = json_decode((string) file_get_contents($composer_path), true, 512, JSON_THROW_ON_ERROR);
|
||||
$composer['autoload'] = [
|
||||
'psr-4' => [
|
||||
'AKSRT\\' => 'src/AKSRT/',
|
||||
],
|
||||
];
|
||||
$composer['description'] = 'AKSRT WordPress Theme';
|
||||
$json = json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
|
||||
aksrt_write($composer_path, $json, $dry);
|
||||
|
||||
// functions.php must require composer autoload (already does) — ensure fatal if missing for theme classes.
|
||||
if (!$dry) {
|
||||
$fn = (string) file_get_contents($root . '/functions.php');
|
||||
// Already rewritten class names. Optionally strengthen vendor require message.
|
||||
if (!str_contains($fn, 'vendor/autoload.php')) {
|
||||
fwrite(STDERR, "functions.php missing vendor/autoload.php require\n");
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
exit(($failed && !$dry) ? 1 : 0);
|
||||
Reference in New Issue
Block a user