Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
216 lines
6.3 KiB
PHP
216 lines
6.3 KiB
PHP
<?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";
|