| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) { |
| 3 |
exit; |
| 4 |
} |
| 5 |
|
| 6 |
/** |
| 7 |
* Plugin class autoloader. |
| 8 |
* |
| 9 |
* Resolves ABJ_404_Solution_* classes via a deterministic classmap |
| 10 |
* (includes/classmap.php) to avoid runtime glob() scans on real sites. Before |
| 11 |
* loading a "host" facade class it verifies that the collaborator files it |
| 12 |
* depends on are present, using the composition-dependency map stored in |
| 13 |
* includes/data/autoloader-trait-dependencies.php; a missing collaborator is |
| 14 |
* recorded in $GLOBALS['abj404_missing_files'] and the host class load is |
| 15 |
* skipped so a corrupt install surfaces a degraded admin page instead of an |
| 16 |
* uncatchable compile-time fatal. |
| 17 |
* |
| 18 |
* The spl_autoload_register() call stays in 404-solution.php (the boot |
| 19 |
* sequence); this file only defines the function. It is required with a plain |
| 20 |
* require FIRST in 404-solution.php, before any class use. |
| 21 |
*/ |
| 22 |
// allow-no-test-found: boot-time global function (abj404_autoloader) wired via spl_autoload_register in 404-solution.php; no isolated unit-file seam. The classmap+missing-collaborator resolution behavior is exercised in BootResilienceTest (which references abj404_autoloader directly). |
| 23 |
|
| 24 |
if (!function_exists('abj404_autoloader')) { |
| 25 |
/** |
| 26 |
* @param string $class |
| 27 |
* @return void |
| 28 |
*/ |
| 29 |
function abj404_autoloader($class) { |
| 30 |
// some people were having issues with possibly parent classes not being loaded before their children. |
| 31 |
$childParentMap = [ |
| 32 |
'ABJ_404_Solution_FunctionsMBString' => 'ABJ_404_Solution_Functions', |
| 33 |
'ABJ_404_Solution_FunctionsPreg' => 'ABJ_404_Solution_Functions', |
| 34 |
'ABJ_404_Solution_MbStringAdapterMb' => 'ABJ_404_Solution_MbStringAdapter', |
| 35 |
'ABJ_404_Solution_MbStringAdapterPreg' => 'ABJ_404_Solution_MbStringAdapter', |
| 36 |
'ABJ_404_Solution_RegexHelperMb' => 'ABJ_404_Solution_RegexHelper', |
| 37 |
'ABJ_404_Solution_RegexHelperPreg' => 'ABJ_404_Solution_RegexHelper', |
| 38 |
]; |
| 39 |
|
| 40 |
// only pay attention if it's for us. don't bother for other things. |
| 41 |
if (substr($class, 0, 16) !== 'ABJ_404_Solution') { |
| 42 |
return; |
| 43 |
} |
| 44 |
|
| 45 |
// Use a deterministic classmap to avoid runtime glob() scans on real sites. |
| 46 |
/** @var array<string, string>|null $abj404_autoLoaderClassMap */ |
| 47 |
static $abj404_autoLoaderClassMap = null; |
| 48 |
if ($abj404_autoLoaderClassMap === null) { |
| 49 |
$mapFile = dirname(__DIR__) . '/classmap.php'; |
| 50 |
$loadedMap = file_exists($mapFile) ? require $mapFile : array(); |
| 51 |
$normalizedMap = array(); |
| 52 |
if (is_array($loadedMap)) { |
| 53 |
foreach ($loadedMap as $mappedClass => $mappedFile) { |
| 54 |
if (is_string($mappedClass) && is_string($mappedFile)) { |
| 55 |
$normalizedMap[$mappedClass] = $mappedFile; |
| 56 |
} |
| 57 |
} |
| 58 |
} |
| 59 |
$abj404_autoLoaderClassMap = $normalizedMap; |
| 60 |
} |
| 61 |
|
| 62 |
if (!array_key_exists($class, $abj404_autoLoaderClassMap)) { |
| 63 |
return; |
| 64 |
} |
| 65 |
|
| 66 |
// Composition dependency pre-check: parent facade classes depend on files |
| 67 |
// being present before they are loaded. Verify those files exist first so |
| 68 |
// a corrupt install can surface a degraded admin page instead of a fatal. |
| 69 |
// The class->collaborators map is DATA, loaded from an external file. |
| 70 |
/** @var array<string, array<int, string>>|null $traitDependencies */ |
| 71 |
static $traitDependencies = null; |
| 72 |
if ($traitDependencies === null) { |
| 73 |
$mapDataFile = dirname(__DIR__) . '/data/autoloader-trait-dependencies.php'; |
| 74 |
$traitDependencyClasses = file_exists($mapDataFile) ? require $mapDataFile : array(); |
| 75 |
if (!is_array($traitDependencyClasses)) { |
| 76 |
$traitDependencyClasses = array(); |
| 77 |
} |
| 78 |
$traitDependencies = array(); |
| 79 |
foreach ($traitDependencyClasses as $hostClass => $dependencyClasses) { |
| 80 |
$traitDependencies[$hostClass] = array(); |
| 81 |
if (!is_array($dependencyClasses)) { |
| 82 |
continue; |
| 83 |
} |
| 84 |
foreach ($dependencyClasses as $dependencyClass) { |
| 85 |
if (is_string($dependencyClass) && isset($abj404_autoLoaderClassMap[$dependencyClass])) { |
| 86 |
$traitDependencies[$hostClass][] = $abj404_autoLoaderClassMap[$dependencyClass]; |
| 87 |
} |
| 88 |
} |
| 89 |
} |
| 90 |
} |
| 91 |
|
| 92 |
if (isset($traitDependencies[$class])) { |
| 93 |
foreach ($traitDependencies[$class] as $dependencyFile) { |
| 94 |
if (!file_exists($dependencyFile)) { |
| 95 |
abj404_record_missing_file($dependencyFile); |
| 96 |
// Don't load the parent class: the compile-time fatal is uncatchable. |
| 97 |
return; |
| 98 |
} |
| 99 |
} |
| 100 |
} |
| 101 |
|
| 102 |
// Ensure the parent class is loaded first. |
| 103 |
if (array_key_exists($class, $childParentMap)) { |
| 104 |
$parentClass = $childParentMap[$class]; |
| 105 |
if (!class_exists($parentClass, false) && array_key_exists($parentClass, $abj404_autoLoaderClassMap)) { |
| 106 |
$parentFile = $abj404_autoLoaderClassMap[$parentClass]; |
| 107 |
if (!file_exists($parentFile)) { |
| 108 |
abj404_record_missing_file($parentFile); |
| 109 |
return; |
| 110 |
} |
| 111 |
require_once $parentFile; |
| 112 |
} |
| 113 |
} |
| 114 |
|
| 115 |
$classFile = $abj404_autoLoaderClassMap[$class]; |
| 116 |
if (!file_exists($classFile)) { |
| 117 |
abj404_record_missing_file($classFile); |
| 118 |
return; |
| 119 |
} |
| 120 |
|
| 121 |
require_once $classFile; |
| 122 |
} |
| 123 |
} |
| 124 |
|