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.
672 lines
21 KiB
PHP
672 lines
21 KiB
PHP
#!/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);
|