1 | <?php |
||
24 | * AopComposerLoader class is responsible to use a weaver for classes instead of original one |
||
25 | */ |
||
26 | class AopComposerLoader |
||
27 | { |
||
28 | /** |
||
29 | * Instance of original autoloader |
||
30 | */ |
||
31 | protected ClassLoader $original; |
||
|
|||
32 | |||
33 | /** |
||
34 | * AOP kernel options |
||
35 | */ |
||
36 | protected array $options = []; |
||
37 | |||
38 | /** |
||
39 | * File enumerator |
||
40 | */ |
||
41 | protected Enumerator $fileEnumerator; |
||
42 | |||
43 | /** |
||
44 | * Cache state |
||
45 | */ |
||
46 | private array $cacheState; |
||
47 | |||
48 | /** |
||
49 | * Was initialization successful or not |
||
50 | */ |
||
51 | private static bool $wasInitialized = false; |
||
52 | |||
53 | /** |
||
54 | * Constructs an wrapper for the composer loader |
||
55 | * |
||
56 | * @param array $options Configuration options |
||
57 | */ |
||
58 | 1 | public function __construct(ClassLoader $original, AspectContainer $container, array $options = []) |
|
59 | { |
||
60 | 1 | $this->options = $options; |
|
61 | 1 | $this->original = $original; |
|
62 | |||
63 | 1 | $prefixes = $original->getPrefixes(); |
|
64 | 1 | $excludePaths = $options['excludePaths']; |
|
65 | |||
66 | 1 | if (!empty($prefixes)) { |
|
67 | // Let's exclude core dependencies from that list |
||
68 | 1 | if (isset($prefixes['Dissect'])) { |
|
69 | 1 | $excludePaths[] = $prefixes['Dissect'][0]; |
|
70 | } |
||
71 | 1 | if (isset($prefixes['Doctrine\\Common\\Annotations\\'])) { |
|
72 | $excludePaths[] = substr($prefixes['Doctrine\\Common\\Annotations\\'][0], 0, -16); |
||
73 | } |
||
74 | } |
||
75 | |||
76 | 1 | $fileEnumerator = new Enumerator($options['appDir'], $options['includePaths'], $excludePaths); |
|
77 | 1 | $this->fileEnumerator = $fileEnumerator; |
|
78 | 1 | $this->cacheState = $container->get('aspect.cache.path.manager')->queryCacheState(); |
|
79 | 1 | } |
|
80 | |||
81 | /** |
||
82 | * Initialize aspect autoloader and returns status whether initialization was successful or not |
||
83 | * |
||
84 | * Replaces original composer autoloader with wrapper |
||
85 | * |
||
86 | * @param array $options Aspect kernel options |
||
87 | */ |
||
88 | 1 | public static function init(array $options, AspectContainer $container): bool |
|
89 | { |
||
90 | 1 | $loaders = spl_autoload_functions(); |
|
91 | |||
92 | 1 | foreach ($loaders as &$loader) { |
|
93 | 1 | $loaderToUnregister = $loader; |
|
94 | 1 | if (is_array($loader) && ($loader[0] instanceof ClassLoader)) { |
|
95 | 1 | $originalLoader = $loader[0]; |
|
96 | // Configure library loader for doctrine annotation loader |
||
97 | AnnotationRegistry::registerLoader(function($class) use ($originalLoader) { |
||
98 | 1 | $originalLoader->loadClass($class); |
|
99 | |||
100 | 1 | return class_exists($class, false); |
|
101 | 1 | }); |
|
102 | 1 | $loader[0] = new AopComposerLoader($loader[0], $container, $options); |
|
103 | 1 | self::$wasInitialized = true; |
|
104 | } |
||
105 | 1 | spl_autoload_unregister($loaderToUnregister); |
|
106 | } |
||
107 | 1 | unset($loader); |
|
108 | |||
109 | 1 | foreach ($loaders as $loader) { |
|
110 | 1 | spl_autoload_register($loader); |
|
111 | } |
||
112 | |||
113 | 1 | return self::$wasInitialized; |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * Autoload a class by it's name |
||
118 | */ |
||
119 | 18 | public function loadClass(string $class): void |
|
120 | { |
||
121 | 18 | $file = $this->findFile($class); |
|
122 | |||
123 | 18 | if ($file !== false) { |
|
124 | 18 | include $file; |
|
125 | } |
||
126 | 18 | } |
|
127 | |||
128 | /** |
||
129 | * Finds either the path to the file where the class is defined, |
||
130 | * or gets the appropriate php://filter stream for the given class. |
||
131 | * |
||
132 | * @return string|false The path/resource if found, false otherwise. |
||
133 | */ |
||
134 | 18 | public function findFile(string $class) |
|
135 | { |
||
136 | 18 | static $isAllowedFilter = null, $isProduction = false; |
|
137 | 18 | if (!$isAllowedFilter) { |
|
138 | 1 | $isAllowedFilter = $this->fileEnumerator->getFilter(); |
|
139 | 1 | $isProduction = !$this->options['debug']; |
|
140 | } |
||
141 | |||
142 | 18 | $file = $this->original->findFile($class); |
|
143 | |||
144 | 18 | if ($file !== false) { |
|
145 | 18 | $file = PathResolver::realpath($file)?:$file; |
|
146 | 18 | $cacheState = $this->cacheState[$file] ?? null; |
|
147 | 18 | if ($cacheState && $isProduction) { |
|
148 | $file = $cacheState['cacheUri'] ?: $file; |
||
149 | 18 | } elseif ($isAllowedFilter(new SplFileInfo($file))) { |
|
150 | // can be optimized here with $cacheState even for debug mode, but no needed right now |
||
151 | 1 | $file = FilterInjectorTransformer::rewrite($file); |
|
152 | } |
||
153 | } |
||
154 | |||
155 | 18 | return $file; |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * Whether or not loader was initialized |
||
160 | */ |
||
161 | public static function wasInitialized(): bool |
||
162 | { |
||
163 | return self::$wasInitialized; |
||
164 | } |
||
165 | } |
||
166 |