Mercurial > hg > rc1
comparison vendor/composer/ClassLoader.php @ 43:771f6803cc4b default tip
somehow lost the correctly updated metadata so e.g. 'mail' package wasn't being imported
author | Charlie Root |
---|---|
date | Sun, 26 Jan 2025 13:13:49 -0500 |
parents | 1e000243b222 |
children |
comparison
equal
deleted
inserted
replaced
42:db1e51c59ddc | 43:771f6803cc4b |
---|---|
35 * | 35 * |
36 * This class is loosely based on the Symfony UniversalClassLoader. | 36 * This class is loosely based on the Symfony UniversalClassLoader. |
37 * | 37 * |
38 * @author Fabien Potencier <fabien@symfony.com> | 38 * @author Fabien Potencier <fabien@symfony.com> |
39 * @author Jordi Boggiano <j.boggiano@seld.be> | 39 * @author Jordi Boggiano <j.boggiano@seld.be> |
40 * @see http://www.php-fig.org/psr/psr-0/ | 40 * @see https://www.php-fig.org/psr/psr-0/ |
41 * @see http://www.php-fig.org/psr/psr-4/ | 41 * @see https://www.php-fig.org/psr/psr-4/ |
42 */ | 42 */ |
43 class ClassLoader | 43 class ClassLoader |
44 { | 44 { |
45 /** @var \Closure(string):void */ | |
46 private static $includeFile; | |
47 | |
48 /** @var string|null */ | |
49 private $vendorDir; | |
50 | |
45 // PSR-4 | 51 // PSR-4 |
52 /** | |
53 * @var array<string, array<string, int>> | |
54 */ | |
46 private $prefixLengthsPsr4 = array(); | 55 private $prefixLengthsPsr4 = array(); |
56 /** | |
57 * @var array<string, list<string>> | |
58 */ | |
47 private $prefixDirsPsr4 = array(); | 59 private $prefixDirsPsr4 = array(); |
60 /** | |
61 * @var list<string> | |
62 */ | |
48 private $fallbackDirsPsr4 = array(); | 63 private $fallbackDirsPsr4 = array(); |
49 | 64 |
50 // PSR-0 | 65 // PSR-0 |
66 /** | |
67 * List of PSR-0 prefixes | |
68 * | |
69 * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) | |
70 * | |
71 * @var array<string, array<string, list<string>>> | |
72 */ | |
51 private $prefixesPsr0 = array(); | 73 private $prefixesPsr0 = array(); |
74 /** | |
75 * @var list<string> | |
76 */ | |
52 private $fallbackDirsPsr0 = array(); | 77 private $fallbackDirsPsr0 = array(); |
53 | 78 |
79 /** @var bool */ | |
54 private $useIncludePath = false; | 80 private $useIncludePath = false; |
81 | |
82 /** | |
83 * @var array<string, string> | |
84 */ | |
55 private $classMap = array(); | 85 private $classMap = array(); |
86 | |
87 /** @var bool */ | |
56 private $classMapAuthoritative = false; | 88 private $classMapAuthoritative = false; |
89 | |
90 /** | |
91 * @var array<string, bool> | |
92 */ | |
57 private $missingClasses = array(); | 93 private $missingClasses = array(); |
94 | |
95 /** @var string|null */ | |
58 private $apcuPrefix; | 96 private $apcuPrefix; |
59 | 97 |
98 /** | |
99 * @var array<string, self> | |
100 */ | |
101 private static $registeredLoaders = array(); | |
102 | |
103 /** | |
104 * @param string|null $vendorDir | |
105 */ | |
106 public function __construct($vendorDir = null) | |
107 { | |
108 $this->vendorDir = $vendorDir; | |
109 self::initializeIncludeClosure(); | |
110 } | |
111 | |
112 /** | |
113 * @return array<string, list<string>> | |
114 */ | |
60 public function getPrefixes() | 115 public function getPrefixes() |
61 { | 116 { |
62 if (!empty($this->prefixesPsr0)) { | 117 if (!empty($this->prefixesPsr0)) { |
63 return call_user_func_array('array_merge', $this->prefixesPsr0); | 118 return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); |
64 } | 119 } |
65 | 120 |
66 return array(); | 121 return array(); |
67 } | 122 } |
68 | 123 |
124 /** | |
125 * @return array<string, list<string>> | |
126 */ | |
69 public function getPrefixesPsr4() | 127 public function getPrefixesPsr4() |
70 { | 128 { |
71 return $this->prefixDirsPsr4; | 129 return $this->prefixDirsPsr4; |
72 } | 130 } |
73 | 131 |
132 /** | |
133 * @return list<string> | |
134 */ | |
74 public function getFallbackDirs() | 135 public function getFallbackDirs() |
75 { | 136 { |
76 return $this->fallbackDirsPsr0; | 137 return $this->fallbackDirsPsr0; |
77 } | 138 } |
78 | 139 |
140 /** | |
141 * @return list<string> | |
142 */ | |
79 public function getFallbackDirsPsr4() | 143 public function getFallbackDirsPsr4() |
80 { | 144 { |
81 return $this->fallbackDirsPsr4; | 145 return $this->fallbackDirsPsr4; |
82 } | 146 } |
83 | 147 |
148 /** | |
149 * @return array<string, string> Array of classname => path | |
150 */ | |
84 public function getClassMap() | 151 public function getClassMap() |
85 { | 152 { |
86 return $this->classMap; | 153 return $this->classMap; |
87 } | 154 } |
88 | 155 |
89 /** | 156 /** |
90 * @param array $classMap Class to filename map | 157 * @param array<string, string> $classMap Class to filename map |
158 * | |
159 * @return void | |
91 */ | 160 */ |
92 public function addClassMap(array $classMap) | 161 public function addClassMap(array $classMap) |
93 { | 162 { |
94 if ($this->classMap) { | 163 if ($this->classMap) { |
95 $this->classMap = array_merge($this->classMap, $classMap); | 164 $this->classMap = array_merge($this->classMap, $classMap); |
100 | 169 |
101 /** | 170 /** |
102 * Registers a set of PSR-0 directories for a given prefix, either | 171 * Registers a set of PSR-0 directories for a given prefix, either |
103 * appending or prepending to the ones previously set for this prefix. | 172 * appending or prepending to the ones previously set for this prefix. |
104 * | 173 * |
105 * @param string $prefix The prefix | 174 * @param string $prefix The prefix |
106 * @param array|string $paths The PSR-0 root directories | 175 * @param list<string>|string $paths The PSR-0 root directories |
107 * @param bool $prepend Whether to prepend the directories | 176 * @param bool $prepend Whether to prepend the directories |
177 * | |
178 * @return void | |
108 */ | 179 */ |
109 public function add($prefix, $paths, $prepend = false) | 180 public function add($prefix, $paths, $prepend = false) |
110 { | 181 { |
182 $paths = (array) $paths; | |
111 if (!$prefix) { | 183 if (!$prefix) { |
112 if ($prepend) { | 184 if ($prepend) { |
113 $this->fallbackDirsPsr0 = array_merge( | 185 $this->fallbackDirsPsr0 = array_merge( |
114 (array) $paths, | 186 $paths, |
115 $this->fallbackDirsPsr0 | 187 $this->fallbackDirsPsr0 |
116 ); | 188 ); |
117 } else { | 189 } else { |
118 $this->fallbackDirsPsr0 = array_merge( | 190 $this->fallbackDirsPsr0 = array_merge( |
119 $this->fallbackDirsPsr0, | 191 $this->fallbackDirsPsr0, |
120 (array) $paths | 192 $paths |
121 ); | 193 ); |
122 } | 194 } |
123 | 195 |
124 return; | 196 return; |
125 } | 197 } |
126 | 198 |
127 $first = $prefix[0]; | 199 $first = $prefix[0]; |
128 if (!isset($this->prefixesPsr0[$first][$prefix])) { | 200 if (!isset($this->prefixesPsr0[$first][$prefix])) { |
129 $this->prefixesPsr0[$first][$prefix] = (array) $paths; | 201 $this->prefixesPsr0[$first][$prefix] = $paths; |
130 | 202 |
131 return; | 203 return; |
132 } | 204 } |
133 if ($prepend) { | 205 if ($prepend) { |
134 $this->prefixesPsr0[$first][$prefix] = array_merge( | 206 $this->prefixesPsr0[$first][$prefix] = array_merge( |
135 (array) $paths, | 207 $paths, |
136 $this->prefixesPsr0[$first][$prefix] | 208 $this->prefixesPsr0[$first][$prefix] |
137 ); | 209 ); |
138 } else { | 210 } else { |
139 $this->prefixesPsr0[$first][$prefix] = array_merge( | 211 $this->prefixesPsr0[$first][$prefix] = array_merge( |
140 $this->prefixesPsr0[$first][$prefix], | 212 $this->prefixesPsr0[$first][$prefix], |
141 (array) $paths | 213 $paths |
142 ); | 214 ); |
143 } | 215 } |
144 } | 216 } |
145 | 217 |
146 /** | 218 /** |
147 * Registers a set of PSR-4 directories for a given namespace, either | 219 * Registers a set of PSR-4 directories for a given namespace, either |
148 * appending or prepending to the ones previously set for this namespace. | 220 * appending or prepending to the ones previously set for this namespace. |
149 * | 221 * |
150 * @param string $prefix The prefix/namespace, with trailing '\\' | 222 * @param string $prefix The prefix/namespace, with trailing '\\' |
151 * @param array|string $paths The PSR-4 base directories | 223 * @param list<string>|string $paths The PSR-4 base directories |
152 * @param bool $prepend Whether to prepend the directories | 224 * @param bool $prepend Whether to prepend the directories |
153 * | 225 * |
154 * @throws \InvalidArgumentException | 226 * @throws \InvalidArgumentException |
227 * | |
228 * @return void | |
155 */ | 229 */ |
156 public function addPsr4($prefix, $paths, $prepend = false) | 230 public function addPsr4($prefix, $paths, $prepend = false) |
157 { | 231 { |
232 $paths = (array) $paths; | |
158 if (!$prefix) { | 233 if (!$prefix) { |
159 // Register directories for the root namespace. | 234 // Register directories for the root namespace. |
160 if ($prepend) { | 235 if ($prepend) { |
161 $this->fallbackDirsPsr4 = array_merge( | 236 $this->fallbackDirsPsr4 = array_merge( |
162 (array) $paths, | 237 $paths, |
163 $this->fallbackDirsPsr4 | 238 $this->fallbackDirsPsr4 |
164 ); | 239 ); |
165 } else { | 240 } else { |
166 $this->fallbackDirsPsr4 = array_merge( | 241 $this->fallbackDirsPsr4 = array_merge( |
167 $this->fallbackDirsPsr4, | 242 $this->fallbackDirsPsr4, |
168 (array) $paths | 243 $paths |
169 ); | 244 ); |
170 } | 245 } |
171 } elseif (!isset($this->prefixDirsPsr4[$prefix])) { | 246 } elseif (!isset($this->prefixDirsPsr4[$prefix])) { |
172 // Register directories for a new namespace. | 247 // Register directories for a new namespace. |
173 $length = strlen($prefix); | 248 $length = strlen($prefix); |
174 if ('\\' !== $prefix[$length - 1]) { | 249 if ('\\' !== $prefix[$length - 1]) { |
175 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); | 250 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
176 } | 251 } |
177 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; | 252 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
178 $this->prefixDirsPsr4[$prefix] = (array) $paths; | 253 $this->prefixDirsPsr4[$prefix] = $paths; |
179 } elseif ($prepend) { | 254 } elseif ($prepend) { |
180 // Prepend directories for an already registered namespace. | 255 // Prepend directories for an already registered namespace. |
181 $this->prefixDirsPsr4[$prefix] = array_merge( | 256 $this->prefixDirsPsr4[$prefix] = array_merge( |
182 (array) $paths, | 257 $paths, |
183 $this->prefixDirsPsr4[$prefix] | 258 $this->prefixDirsPsr4[$prefix] |
184 ); | 259 ); |
185 } else { | 260 } else { |
186 // Append directories for an already registered namespace. | 261 // Append directories for an already registered namespace. |
187 $this->prefixDirsPsr4[$prefix] = array_merge( | 262 $this->prefixDirsPsr4[$prefix] = array_merge( |
188 $this->prefixDirsPsr4[$prefix], | 263 $this->prefixDirsPsr4[$prefix], |
189 (array) $paths | 264 $paths |
190 ); | 265 ); |
191 } | 266 } |
192 } | 267 } |
193 | 268 |
194 /** | 269 /** |
195 * Registers a set of PSR-0 directories for a given prefix, | 270 * Registers a set of PSR-0 directories for a given prefix, |
196 * replacing any others previously set for this prefix. | 271 * replacing any others previously set for this prefix. |
197 * | 272 * |
198 * @param string $prefix The prefix | 273 * @param string $prefix The prefix |
199 * @param array|string $paths The PSR-0 base directories | 274 * @param list<string>|string $paths The PSR-0 base directories |
275 * | |
276 * @return void | |
200 */ | 277 */ |
201 public function set($prefix, $paths) | 278 public function set($prefix, $paths) |
202 { | 279 { |
203 if (!$prefix) { | 280 if (!$prefix) { |
204 $this->fallbackDirsPsr0 = (array) $paths; | 281 $this->fallbackDirsPsr0 = (array) $paths; |
209 | 286 |
210 /** | 287 /** |
211 * Registers a set of PSR-4 directories for a given namespace, | 288 * Registers a set of PSR-4 directories for a given namespace, |
212 * replacing any others previously set for this namespace. | 289 * replacing any others previously set for this namespace. |
213 * | 290 * |
214 * @param string $prefix The prefix/namespace, with trailing '\\' | 291 * @param string $prefix The prefix/namespace, with trailing '\\' |
215 * @param array|string $paths The PSR-4 base directories | 292 * @param list<string>|string $paths The PSR-4 base directories |
216 * | 293 * |
217 * @throws \InvalidArgumentException | 294 * @throws \InvalidArgumentException |
295 * | |
296 * @return void | |
218 */ | 297 */ |
219 public function setPsr4($prefix, $paths) | 298 public function setPsr4($prefix, $paths) |
220 { | 299 { |
221 if (!$prefix) { | 300 if (!$prefix) { |
222 $this->fallbackDirsPsr4 = (array) $paths; | 301 $this->fallbackDirsPsr4 = (array) $paths; |
232 | 311 |
233 /** | 312 /** |
234 * Turns on searching the include path for class files. | 313 * Turns on searching the include path for class files. |
235 * | 314 * |
236 * @param bool $useIncludePath | 315 * @param bool $useIncludePath |
316 * | |
317 * @return void | |
237 */ | 318 */ |
238 public function setUseIncludePath($useIncludePath) | 319 public function setUseIncludePath($useIncludePath) |
239 { | 320 { |
240 $this->useIncludePath = $useIncludePath; | 321 $this->useIncludePath = $useIncludePath; |
241 } | 322 } |
254 /** | 335 /** |
255 * Turns off searching the prefix and fallback directories for classes | 336 * Turns off searching the prefix and fallback directories for classes |
256 * that have not been registered with the class map. | 337 * that have not been registered with the class map. |
257 * | 338 * |
258 * @param bool $classMapAuthoritative | 339 * @param bool $classMapAuthoritative |
340 * | |
341 * @return void | |
259 */ | 342 */ |
260 public function setClassMapAuthoritative($classMapAuthoritative) | 343 public function setClassMapAuthoritative($classMapAuthoritative) |
261 { | 344 { |
262 $this->classMapAuthoritative = $classMapAuthoritative; | 345 $this->classMapAuthoritative = $classMapAuthoritative; |
263 } | 346 } |
274 | 357 |
275 /** | 358 /** |
276 * APCu prefix to use to cache found/not-found classes, if the extension is enabled. | 359 * APCu prefix to use to cache found/not-found classes, if the extension is enabled. |
277 * | 360 * |
278 * @param string|null $apcuPrefix | 361 * @param string|null $apcuPrefix |
362 * | |
363 * @return void | |
279 */ | 364 */ |
280 public function setApcuPrefix($apcuPrefix) | 365 public function setApcuPrefix($apcuPrefix) |
281 { | 366 { |
282 $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; | 367 $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; |
283 } | 368 } |
284 | 369 |
285 /** | 370 /** |
286 * The APCu prefix in use, or null if APCu caching is not enabled. | 371 * The APCu prefix in use, or null if APCu caching is not enabled. |
287 * | 372 * |
294 | 379 |
295 /** | 380 /** |
296 * Registers this instance as an autoloader. | 381 * Registers this instance as an autoloader. |
297 * | 382 * |
298 * @param bool $prepend Whether to prepend the autoloader or not | 383 * @param bool $prepend Whether to prepend the autoloader or not |
384 * | |
385 * @return void | |
299 */ | 386 */ |
300 public function register($prepend = false) | 387 public function register($prepend = false) |
301 { | 388 { |
302 spl_autoload_register(array($this, 'loadClass'), true, $prepend); | 389 spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
390 | |
391 if (null === $this->vendorDir) { | |
392 return; | |
393 } | |
394 | |
395 if ($prepend) { | |
396 self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; | |
397 } else { | |
398 unset(self::$registeredLoaders[$this->vendorDir]); | |
399 self::$registeredLoaders[$this->vendorDir] = $this; | |
400 } | |
303 } | 401 } |
304 | 402 |
305 /** | 403 /** |
306 * Unregisters this instance as an autoloader. | 404 * Unregisters this instance as an autoloader. |
405 * | |
406 * @return void | |
307 */ | 407 */ |
308 public function unregister() | 408 public function unregister() |
309 { | 409 { |
310 spl_autoload_unregister(array($this, 'loadClass')); | 410 spl_autoload_unregister(array($this, 'loadClass')); |
411 | |
412 if (null !== $this->vendorDir) { | |
413 unset(self::$registeredLoaders[$this->vendorDir]); | |
414 } | |
311 } | 415 } |
312 | 416 |
313 /** | 417 /** |
314 * Loads the given class or interface. | 418 * Loads the given class or interface. |
315 * | 419 * |
316 * @param string $class The name of the class | 420 * @param string $class The name of the class |
317 * @return bool|null True if loaded, null otherwise | 421 * @return true|null True if loaded, null otherwise |
318 */ | 422 */ |
319 public function loadClass($class) | 423 public function loadClass($class) |
320 { | 424 { |
321 if ($file = $this->findFile($class)) { | 425 if ($file = $this->findFile($class)) { |
322 includeFile($file); | 426 $includeFile = self::$includeFile; |
427 $includeFile($file); | |
323 | 428 |
324 return true; | 429 return true; |
325 } | 430 } |
431 | |
432 return null; | |
326 } | 433 } |
327 | 434 |
328 /** | 435 /** |
329 * Finds the path to the file where the class is defined. | 436 * Finds the path to the file where the class is defined. |
330 * | 437 * |
365 } | 472 } |
366 | 473 |
367 return $file; | 474 return $file; |
368 } | 475 } |
369 | 476 |
477 /** | |
478 * Returns the currently registered loaders keyed by their corresponding vendor directories. | |
479 * | |
480 * @return array<string, self> | |
481 */ | |
482 public static function getRegisteredLoaders() | |
483 { | |
484 return self::$registeredLoaders; | |
485 } | |
486 | |
487 /** | |
488 * @param string $class | |
489 * @param string $ext | |
490 * @return string|false | |
491 */ | |
370 private function findFileWithExtension($class, $ext) | 492 private function findFileWithExtension($class, $ext) |
371 { | 493 { |
372 // PSR-4 lookup | 494 // PSR-4 lookup |
373 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; | 495 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; |
374 | 496 |
375 $first = $class[0]; | 497 $first = $class[0]; |
376 if (isset($this->prefixLengthsPsr4[$first])) { | 498 if (isset($this->prefixLengthsPsr4[$first])) { |
377 $subPath = $class; | 499 $subPath = $class; |
378 while (false !== $lastPos = strrpos($subPath, '\\')) { | 500 while (false !== $lastPos = strrpos($subPath, '\\')) { |
379 $subPath = substr($subPath, 0, $lastPos); | 501 $subPath = substr($subPath, 0, $lastPos); |
380 $search = $subPath.'\\'; | 502 $search = $subPath . '\\'; |
381 if (isset($this->prefixDirsPsr4[$search])) { | 503 if (isset($this->prefixDirsPsr4[$search])) { |
504 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); | |
382 foreach ($this->prefixDirsPsr4[$search] as $dir) { | 505 foreach ($this->prefixDirsPsr4[$search] as $dir) { |
383 $length = $this->prefixLengthsPsr4[$first][$search]; | 506 if (file_exists($file = $dir . $pathEnd)) { |
384 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { | |
385 return $file; | 507 return $file; |
386 } | 508 } |
387 } | 509 } |
388 } | 510 } |
389 } | 511 } |
430 return $file; | 552 return $file; |
431 } | 553 } |
432 | 554 |
433 return false; | 555 return false; |
434 } | 556 } |
557 | |
558 /** | |
559 * @return void | |
560 */ | |
561 private static function initializeIncludeClosure() | |
562 { | |
563 if (self::$includeFile !== null) { | |
564 return; | |
565 } | |
566 | |
567 /** | |
568 * Scope isolated include. | |
569 * | |
570 * Prevents access to $this/self from included files. | |
571 * | |
572 * @param string $file | |
573 * @return void | |
574 */ | |
575 self::$includeFile = \Closure::bind(static function($file) { | |
576 include $file; | |
577 }, null, null); | |
578 } | |
435 } | 579 } |
436 | |
437 /** | |
438 * Scope isolated include. | |
439 * | |
440 * Prevents access to $this/self from included files. | |
441 */ | |
442 function includeFile($file) | |
443 { | |
444 include $file; | |
445 } |