PluginProbe
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress / 3.2.0
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress v3.2.0
4.1.2 4.1.1 4.1.0 4.0.1 4.0.0 3.3.1 3.3.0 trunk 1.0.0 2.0.0 2.1.0 3.0.0 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8
quickwebp / includes / vendor / composer / ClassLoader.php

ClassLoader.php in QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress 3.2.0, at includes/vendor/composer/ClassLoader.php

582 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of Composer.
5 *
6 * (c) Nils Adermann <naderman@naderman.de>
7 * Jordi Boggiano <j.boggiano@seld.be>
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13 namespace Composer\Autoload;
14
15 /**
16 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 *
18 * $loader = new \Composer\Autoload\ClassLoader();
19 *
20 * // register classes with namespaces
21 * $loader->add('Symfony\Component', __DIR__.'/component');
22 * $loader->add('Symfony', __DIR__.'/framework');
23 *
24 * // activate the autoloader
25 * $loader->register();
26 *
27 * // to enable searching the include path (eg. for PEAR packages)
28 * $loader->setUseIncludePath(true);
29 *
30 * In this example, if you try to use a class in the Symfony\Component
31 * namespace or one of its children (Symfony\Component\Console for instance),
32 * the autoloader will first look for the class under the component/
33 * directory, and it will then fallback to the framework/ directory if not
34 * found before giving up.
35 *
36 * This class is loosely based on the Symfony UniversalClassLoader.
37 *
38 * @author Fabien Potencier <fabien@symfony.com>
39 * @author Jordi Boggiano <j.boggiano@seld.be>
40 * @see https://www.php-fig.org/psr/psr-0/
41 * @see https://www.php-fig.org/psr/psr-4/
42 */
43 class ClassLoader
44 {
45 /** @var \Closure(string):void */
46 private static $includeFile;
47
48 /** @var ?string */
49 private $vendorDir;
50
51 // PSR-4
52 /**
53 * @var array[]
54 * @psalm-var array<string, array<string, int>>
55 */
56 private $prefixLengthsPsr4 = array();
57 /**
58 * @var array[]
59 * @psalm-var array<string, array<int, string>>
60 */
61 private $prefixDirsPsr4 = array();
62 /**
63 * @var array[]
64 * @psalm-var array<string, string>
65 */
66 private $fallbackDirsPsr4 = array();
67
68 // PSR-0
69 /**
70 * @var array[]
71 * @psalm-var array<string, array<string, string[]>>
72 */
73 private $prefixesPsr0 = array();
74 /**
75 * @var array[]
76 * @psalm-var array<string, string>
77 */
78 private $fallbackDirsPsr0 = array();
79
80 /** @var bool */
81 private $useIncludePath = false;
82
83 /**
84 * @var string[]
85 * @psalm-var array<string, string>
86 */
87 private $classMap = array();
88
89 /** @var bool */
90 private $classMapAuthoritative = false;
91
92 /**
93 * @var bool[]
94 * @psalm-var array<string, bool>
95 */
96 private $missingClasses = array();
97
98 /** @var ?string */
99 private $apcuPrefix;
100
101 /**
102 * @var self[]
103 */
104 private static $registeredLoaders = array();
105
106 /**
107 * @param ?string $vendorDir
108 */
109 public function __construct($vendorDir = null)
110 {
111 $this->vendorDir = $vendorDir;
112 self::initializeIncludeClosure();
113 }
114
115 /**
116 * @return string[]
117 */
118 public function getPrefixes()
119 {
120 if (!empty($this->prefixesPsr0)) {
121 return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
122 }
123
124 return array();
125 }
126
127 /**
128 * @return array[]
129 * @psalm-return array<string, array<int, string>>
130 */
131 public function getPrefixesPsr4()
132 {
133 return $this->prefixDirsPsr4;
134 }
135
136 /**
137 * @return array[]
138 * @psalm-return array<string, string>
139 */
140 public function getFallbackDirs()
141 {
142 return $this->fallbackDirsPsr0;
143 }
144
145 /**
146 * @return array[]
147 * @psalm-return array<string, string>
148 */
149 public function getFallbackDirsPsr4()
150 {
151 return $this->fallbackDirsPsr4;
152 }
153
154 /**
155 * @return string[] Array of classname => path
156 * @psalm-return array<string, string>
157 */
158 public function getClassMap()
159 {
160 return $this->classMap;
161 }
162
163 /**
164 * @param string[] $classMap Class to filename map
165 * @psalm-param array<string, string> $classMap
166 *
167 * @return void
168 */
169 public function addClassMap(array $classMap)
170 {
171 if ($this->classMap) {
172 $this->classMap = array_merge($this->classMap, $classMap);
173 } else {
174 $this->classMap = $classMap;
175 }
176 }
177
178 /**
179 * Registers a set of PSR-0 directories for a given prefix, either
180 * appending or prepending to the ones previously set for this prefix.
181 *
182 * @param string $prefix The prefix
183 * @param string[]|string $paths The PSR-0 root directories
184 * @param bool $prepend Whether to prepend the directories
185 *
186 * @return void
187 */
188 public function add($prefix, $paths, $prepend = false)
189 {
190 if (!$prefix) {
191 if ($prepend) {
192 $this->fallbackDirsPsr0 = array_merge(
193 (array) $paths,
194 $this->fallbackDirsPsr0
195 );
196 } else {
197 $this->fallbackDirsPsr0 = array_merge(
198 $this->fallbackDirsPsr0,
199 (array) $paths
200 );
201 }
202
203 return;
204 }
205
206 $first = $prefix[0];
207 if (!isset($this->prefixesPsr0[$first][$prefix])) {
208 $this->prefixesPsr0[$first][$prefix] = (array) $paths;
209
210 return;
211 }
212 if ($prepend) {
213 $this->prefixesPsr0[$first][$prefix] = array_merge(
214 (array) $paths,
215 $this->prefixesPsr0[$first][$prefix]
216 );
217 } else {
218 $this->prefixesPsr0[$first][$prefix] = array_merge(
219 $this->prefixesPsr0[$first][$prefix],
220 (array) $paths
221 );
222 }
223 }
224
225 /**
226 * Registers a set of PSR-4 directories for a given namespace, either
227 * appending or prepending to the ones previously set for this namespace.
228 *
229 * @param string $prefix The prefix/namespace, with trailing '\\'
230 * @param string[]|string $paths The PSR-4 base directories
231 * @param bool $prepend Whether to prepend the directories
232 *
233 * @throws \InvalidArgumentException
234 *
235 * @return void
236 */
237 public function addPsr4($prefix, $paths, $prepend = false)
238 {
239 if (!$prefix) {
240 // Register directories for the root namespace.
241 if ($prepend) {
242 $this->fallbackDirsPsr4 = array_merge(
243 (array) $paths,
244 $this->fallbackDirsPsr4
245 );
246 } else {
247 $this->fallbackDirsPsr4 = array_merge(
248 $this->fallbackDirsPsr4,
249 (array) $paths
250 );
251 }
252 } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
253 // Register directories for a new namespace.
254 $length = strlen($prefix);
255 if ('\\' !== $prefix[$length - 1]) {
256 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
257 }
258 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
259 $this->prefixDirsPsr4[$prefix] = (array) $paths;
260 } elseif ($prepend) {
261 // Prepend directories for an already registered namespace.
262 $this->prefixDirsPsr4[$prefix] = array_merge(
263 (array) $paths,
264 $this->prefixDirsPsr4[$prefix]
265 );
266 } else {
267 // Append directories for an already registered namespace.
268 $this->prefixDirsPsr4[$prefix] = array_merge(
269 $this->prefixDirsPsr4[$prefix],
270 (array) $paths
271 );
272 }
273 }
274
275 /**
276 * Registers a set of PSR-0 directories for a given prefix,
277 * replacing any others previously set for this prefix.
278 *
279 * @param string $prefix The prefix
280 * @param string[]|string $paths The PSR-0 base directories
281 *
282 * @return void
283 */
284 public function set($prefix, $paths)
285 {
286 if (!$prefix) {
287 $this->fallbackDirsPsr0 = (array) $paths;
288 } else {
289 $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
290 }
291 }
292
293 /**
294 * Registers a set of PSR-4 directories for a given namespace,
295 * replacing any others previously set for this namespace.
296 *
297 * @param string $prefix The prefix/namespace, with trailing '\\'
298 * @param string[]|string $paths The PSR-4 base directories
299 *
300 * @throws \InvalidArgumentException
301 *
302 * @return void
303 */
304 public function setPsr4($prefix, $paths)
305 {
306 if (!$prefix) {
307 $this->fallbackDirsPsr4 = (array) $paths;
308 } else {
309 $length = strlen($prefix);
310 if ('\\' !== $prefix[$length - 1]) {
311 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
312 }
313 $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
314 $this->prefixDirsPsr4[$prefix] = (array) $paths;
315 }
316 }
317
318 /**
319 * Turns on searching the include path for class files.
320 *
321 * @param bool $useIncludePath
322 *
323 * @return void
324 */
325 public function setUseIncludePath($useIncludePath)
326 {
327 $this->useIncludePath = $useIncludePath;
328 }
329
330 /**
331 * Can be used to check if the autoloader uses the include path to check
332 * for classes.
333 *
334 * @return bool
335 */
336 public function getUseIncludePath()
337 {
338 return $this->useIncludePath;
339 }
340
341 /**
342 * Turns off searching the prefix and fallback directories for classes
343 * that have not been registered with the class map.
344 *
345 * @param bool $classMapAuthoritative
346 *
347 * @return void
348 */
349 public function setClassMapAuthoritative($classMapAuthoritative)
350 {
351 $this->classMapAuthoritative = $classMapAuthoritative;
352 }
353
354 /**
355 * Should class lookup fail if not found in the current class map?
356 *
357 * @return bool
358 */
359 public function isClassMapAuthoritative()
360 {
361 return $this->classMapAuthoritative;
362 }
363
364 /**
365 * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
366 *
367 * @param string|null $apcuPrefix
368 *
369 * @return void
370 */
371 public function setApcuPrefix($apcuPrefix)
372 {
373 $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
374 }
375
376 /**
377 * The APCu prefix in use, or null if APCu caching is not enabled.
378 *
379 * @return string|null
380 */
381 public function getApcuPrefix()
382 {
383 return $this->apcuPrefix;
384 }
385
386 /**
387 * Registers this instance as an autoloader.
388 *
389 * @param bool $prepend Whether to prepend the autoloader or not
390 *
391 * @return void
392 */
393 public function register($prepend = false)
394 {
395 spl_autoload_register(array($this, 'loadClass'), true, $prepend);
396
397 if (null === $this->vendorDir) {
398 return;
399 }
400
401 if ($prepend) {
402 self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
403 } else {
404 unset(self::$registeredLoaders[$this->vendorDir]);
405 self::$registeredLoaders[$this->vendorDir] = $this;
406 }
407 }
408
409 /**
410 * Unregisters this instance as an autoloader.
411 *
412 * @return void
413 */
414 public function unregister()
415 {
416 spl_autoload_unregister(array($this, 'loadClass'));
417
418 if (null !== $this->vendorDir) {
419 unset(self::$registeredLoaders[$this->vendorDir]);
420 }
421 }
422
423 /**
424 * Loads the given class or interface.
425 *
426 * @param string $class The name of the class
427 * @return true|null True if loaded, null otherwise
428 */
429 public function loadClass($class)
430 {
431 if ($file = $this->findFile($class)) {
432 (self::$includeFile)($file);
433
434 return true;
435 }
436
437 return null;
438 }
439
440 /**
441 * Finds the path to the file where the class is defined.
442 *
443 * @param string $class The name of the class
444 *
445 * @return string|false The path if found, false otherwise
446 */
447 public function findFile($class)
448 {
449 // class map lookup
450 if (isset($this->classMap[$class])) {
451 return $this->classMap[$class];
452 }
453 if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
454 return false;
455 }
456 if (null !== $this->apcuPrefix) {
457 $file = apcu_fetch($this->apcuPrefix.$class, $hit);
458 if ($hit) {
459 return $file;
460 }
461 }
462
463 $file = $this->findFileWithExtension($class, '.php');
464
465 // Search for Hack files if we are running on HHVM
466 if (false === $file && defined('HHVM_VERSION')) {
467 $file = $this->findFileWithExtension($class, '.hh');
468 }
469
470 if (null !== $this->apcuPrefix) {
471 apcu_add($this->apcuPrefix.$class, $file);
472 }
473
474 if (false === $file) {
475 // Remember that this class does not exist.
476 $this->missingClasses[$class] = true;
477 }
478
479 return $file;
480 }
481
482 /**
483 * Returns the currently registered loaders indexed by their corresponding vendor directories.
484 *
485 * @return self[]
486 */
487 public static function getRegisteredLoaders()
488 {
489 return self::$registeredLoaders;
490 }
491
492 /**
493 * @param string $class
494 * @param string $ext
495 * @return string|false
496 */
497 private function findFileWithExtension($class, $ext)
498 {
499 // PSR-4 lookup
500 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
501
502 $first = $class[0];
503 if (isset($this->prefixLengthsPsr4[$first])) {
504 $subPath = $class;
505 while (false !== $lastPos = strrpos($subPath, '\\')) {
506 $subPath = substr($subPath, 0, $lastPos);
507 $search = $subPath . '\\';
508 if (isset($this->prefixDirsPsr4[$search])) {
509 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
510 foreach ($this->prefixDirsPsr4[$search] as $dir) {
511 if (file_exists($file = $dir . $pathEnd)) {
512 return $file;
513 }
514 }
515 }
516 }
517 }
518
519 // PSR-4 fallback dirs
520 foreach ($this->fallbackDirsPsr4 as $dir) {
521 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
522 return $file;
523 }
524 }
525
526 // PSR-0 lookup
527 if (false !== $pos = strrpos($class, '\\')) {
528 // namespaced class name
529 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
530 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
531 } else {
532 // PEAR-like class name
533 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
534 }
535
536 if (isset($this->prefixesPsr0[$first])) {
537 foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
538 if (0 === strpos($class, $prefix)) {
539 foreach ($dirs as $dir) {
540 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
541 return $file;
542 }
543 }
544 }
545 }
546 }
547
548 // PSR-0 fallback dirs
549 foreach ($this->fallbackDirsPsr0 as $dir) {
550 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
551 return $file;
552 }
553 }
554
555 // PSR-0 include paths.
556 if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
557 return $file;
558 }
559
560 return false;
561 }
562
563 private static function initializeIncludeClosure(): void
564 {
565 if (self::$includeFile !== null) {
566 return;
567 }
568
569 /**
570 * Scope isolated include.
571 *
572 * Prevents access to $this/self from included files.
573 *
574 * @param string $file
575 * @return void
576 */
577 self::$includeFile = static function($file) {
578 include $file;
579 };
580 }
581 }
582