18817535bSAndreas Gohr<?php 28817535bSAndreas Gohr 38817535bSAndreas Gohr/* 48817535bSAndreas Gohr * This file is part of Composer. 58817535bSAndreas Gohr * 68817535bSAndreas Gohr * (c) Nils Adermann <naderman@naderman.de> 78817535bSAndreas Gohr * Jordi Boggiano <j.boggiano@seld.be> 88817535bSAndreas Gohr * 98817535bSAndreas Gohr * For the full copyright and license information, please view the LICENSE 108817535bSAndreas Gohr * file that was distributed with this source code. 118817535bSAndreas Gohr */ 128817535bSAndreas Gohr 138817535bSAndreas Gohrnamespace Composer\Autoload; 148817535bSAndreas Gohr 158817535bSAndreas Gohr/** 168817535bSAndreas Gohr * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 178817535bSAndreas Gohr * 188817535bSAndreas Gohr * $loader = new \Composer\Autoload\ClassLoader(); 198817535bSAndreas Gohr * 208817535bSAndreas Gohr * // register classes with namespaces 218817535bSAndreas Gohr * $loader->add('Symfony\Component', __DIR__.'/component'); 228817535bSAndreas Gohr * $loader->add('Symfony', __DIR__.'/framework'); 238817535bSAndreas Gohr * 248817535bSAndreas Gohr * // activate the autoloader 258817535bSAndreas Gohr * $loader->register(); 268817535bSAndreas Gohr * 278817535bSAndreas Gohr * // to enable searching the include path (eg. for PEAR packages) 288817535bSAndreas Gohr * $loader->setUseIncludePath(true); 298817535bSAndreas Gohr * 308817535bSAndreas Gohr * In this example, if you try to use a class in the Symfony\Component 318817535bSAndreas Gohr * namespace or one of its children (Symfony\Component\Console for instance), 328817535bSAndreas Gohr * the autoloader will first look for the class under the component/ 338817535bSAndreas Gohr * directory, and it will then fallback to the framework/ directory if not 348817535bSAndreas Gohr * found before giving up. 358817535bSAndreas Gohr * 368817535bSAndreas Gohr * This class is loosely based on the Symfony UniversalClassLoader. 378817535bSAndreas Gohr * 388817535bSAndreas Gohr * @author Fabien Potencier <fabien@symfony.com> 398817535bSAndreas Gohr * @author Jordi Boggiano <j.boggiano@seld.be> 408817535bSAndreas Gohr * @see https://www.php-fig.org/psr/psr-0/ 418817535bSAndreas Gohr * @see https://www.php-fig.org/psr/psr-4/ 428817535bSAndreas Gohr */ 438817535bSAndreas Gohrclass ClassLoader 448817535bSAndreas Gohr{ 458817535bSAndreas Gohr /** @var \Closure(string):void */ 468817535bSAndreas Gohr private static $includeFile; 478817535bSAndreas Gohr 48*3379af09SAndreas Gohr /** @var string|null */ 498817535bSAndreas Gohr private $vendorDir; 508817535bSAndreas Gohr 518817535bSAndreas Gohr // PSR-4 528817535bSAndreas Gohr /** 53*3379af09SAndreas Gohr * @var array<string, array<string, int>> 548817535bSAndreas Gohr */ 558817535bSAndreas Gohr private $prefixLengthsPsr4 = array(); 568817535bSAndreas Gohr /** 57*3379af09SAndreas Gohr * @var array<string, list<string>> 588817535bSAndreas Gohr */ 598817535bSAndreas Gohr private $prefixDirsPsr4 = array(); 608817535bSAndreas Gohr /** 61*3379af09SAndreas Gohr * @var list<string> 628817535bSAndreas Gohr */ 638817535bSAndreas Gohr private $fallbackDirsPsr4 = array(); 648817535bSAndreas Gohr 658817535bSAndreas Gohr // PSR-0 668817535bSAndreas Gohr /** 67*3379af09SAndreas Gohr * List of PSR-0 prefixes 68*3379af09SAndreas Gohr * 69*3379af09SAndreas Gohr * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) 70*3379af09SAndreas Gohr * 71*3379af09SAndreas Gohr * @var array<string, array<string, list<string>>> 728817535bSAndreas Gohr */ 738817535bSAndreas Gohr private $prefixesPsr0 = array(); 748817535bSAndreas Gohr /** 75*3379af09SAndreas Gohr * @var list<string> 768817535bSAndreas Gohr */ 778817535bSAndreas Gohr private $fallbackDirsPsr0 = array(); 788817535bSAndreas Gohr 798817535bSAndreas Gohr /** @var bool */ 808817535bSAndreas Gohr private $useIncludePath = false; 818817535bSAndreas Gohr 828817535bSAndreas Gohr /** 83*3379af09SAndreas Gohr * @var array<string, string> 848817535bSAndreas Gohr */ 858817535bSAndreas Gohr private $classMap = array(); 868817535bSAndreas Gohr 878817535bSAndreas Gohr /** @var bool */ 888817535bSAndreas Gohr private $classMapAuthoritative = false; 898817535bSAndreas Gohr 908817535bSAndreas Gohr /** 91*3379af09SAndreas Gohr * @var array<string, bool> 928817535bSAndreas Gohr */ 938817535bSAndreas Gohr private $missingClasses = array(); 948817535bSAndreas Gohr 95*3379af09SAndreas Gohr /** @var string|null */ 968817535bSAndreas Gohr private $apcuPrefix; 978817535bSAndreas Gohr 988817535bSAndreas Gohr /** 99*3379af09SAndreas Gohr * @var array<string, self> 1008817535bSAndreas Gohr */ 1018817535bSAndreas Gohr private static $registeredLoaders = array(); 1028817535bSAndreas Gohr 1038817535bSAndreas Gohr /** 104*3379af09SAndreas Gohr * @param string|null $vendorDir 1058817535bSAndreas Gohr */ 1068817535bSAndreas Gohr public function __construct($vendorDir = null) 1078817535bSAndreas Gohr { 1088817535bSAndreas Gohr $this->vendorDir = $vendorDir; 1098817535bSAndreas Gohr self::initializeIncludeClosure(); 1108817535bSAndreas Gohr } 1118817535bSAndreas Gohr 1128817535bSAndreas Gohr /** 113*3379af09SAndreas Gohr * @return array<string, list<string>> 1148817535bSAndreas Gohr */ 1158817535bSAndreas Gohr public function getPrefixes() 1168817535bSAndreas Gohr { 1178817535bSAndreas Gohr if (!empty($this->prefixesPsr0)) { 1188817535bSAndreas Gohr return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 1198817535bSAndreas Gohr } 1208817535bSAndreas Gohr 1218817535bSAndreas Gohr return array(); 1228817535bSAndreas Gohr } 1238817535bSAndreas Gohr 1248817535bSAndreas Gohr /** 125*3379af09SAndreas Gohr * @return array<string, list<string>> 1268817535bSAndreas Gohr */ 1278817535bSAndreas Gohr public function getPrefixesPsr4() 1288817535bSAndreas Gohr { 1298817535bSAndreas Gohr return $this->prefixDirsPsr4; 1308817535bSAndreas Gohr } 1318817535bSAndreas Gohr 1328817535bSAndreas Gohr /** 133*3379af09SAndreas Gohr * @return list<string> 1348817535bSAndreas Gohr */ 1358817535bSAndreas Gohr public function getFallbackDirs() 1368817535bSAndreas Gohr { 1378817535bSAndreas Gohr return $this->fallbackDirsPsr0; 1388817535bSAndreas Gohr } 1398817535bSAndreas Gohr 1408817535bSAndreas Gohr /** 141*3379af09SAndreas Gohr * @return list<string> 1428817535bSAndreas Gohr */ 1438817535bSAndreas Gohr public function getFallbackDirsPsr4() 1448817535bSAndreas Gohr { 1458817535bSAndreas Gohr return $this->fallbackDirsPsr4; 1468817535bSAndreas Gohr } 1478817535bSAndreas Gohr 1488817535bSAndreas Gohr /** 149*3379af09SAndreas Gohr * @return array<string, string> Array of classname => path 1508817535bSAndreas Gohr */ 1518817535bSAndreas Gohr public function getClassMap() 1528817535bSAndreas Gohr { 1538817535bSAndreas Gohr return $this->classMap; 1548817535bSAndreas Gohr } 1558817535bSAndreas Gohr 1568817535bSAndreas Gohr /** 157*3379af09SAndreas Gohr * @param array<string, string> $classMap Class to filename map 1588817535bSAndreas Gohr * 1598817535bSAndreas Gohr * @return void 1608817535bSAndreas Gohr */ 1618817535bSAndreas Gohr public function addClassMap(array $classMap) 1628817535bSAndreas Gohr { 1638817535bSAndreas Gohr if ($this->classMap) { 1648817535bSAndreas Gohr $this->classMap = array_merge($this->classMap, $classMap); 1658817535bSAndreas Gohr } else { 1668817535bSAndreas Gohr $this->classMap = $classMap; 1678817535bSAndreas Gohr } 1688817535bSAndreas Gohr } 1698817535bSAndreas Gohr 1708817535bSAndreas Gohr /** 1718817535bSAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, either 1728817535bSAndreas Gohr * appending or prepending to the ones previously set for this prefix. 1738817535bSAndreas Gohr * 1748817535bSAndreas Gohr * @param string $prefix The prefix 175*3379af09SAndreas Gohr * @param list<string>|string $paths The PSR-0 root directories 1768817535bSAndreas Gohr * @param bool $prepend Whether to prepend the directories 1778817535bSAndreas Gohr * 1788817535bSAndreas Gohr * @return void 1798817535bSAndreas Gohr */ 1808817535bSAndreas Gohr public function add($prefix, $paths, $prepend = false) 1818817535bSAndreas Gohr { 182*3379af09SAndreas Gohr $paths = (array) $paths; 1838817535bSAndreas Gohr if (!$prefix) { 1848817535bSAndreas Gohr if ($prepend) { 1858817535bSAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 186*3379af09SAndreas Gohr $paths, 1878817535bSAndreas Gohr $this->fallbackDirsPsr0 1888817535bSAndreas Gohr ); 1898817535bSAndreas Gohr } else { 1908817535bSAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 1918817535bSAndreas Gohr $this->fallbackDirsPsr0, 192*3379af09SAndreas Gohr $paths 1938817535bSAndreas Gohr ); 1948817535bSAndreas Gohr } 1958817535bSAndreas Gohr 1968817535bSAndreas Gohr return; 1978817535bSAndreas Gohr } 1988817535bSAndreas Gohr 1998817535bSAndreas Gohr $first = $prefix[0]; 2008817535bSAndreas Gohr if (!isset($this->prefixesPsr0[$first][$prefix])) { 201*3379af09SAndreas Gohr $this->prefixesPsr0[$first][$prefix] = $paths; 2028817535bSAndreas Gohr 2038817535bSAndreas Gohr return; 2048817535bSAndreas Gohr } 2058817535bSAndreas Gohr if ($prepend) { 2068817535bSAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 207*3379af09SAndreas Gohr $paths, 2088817535bSAndreas Gohr $this->prefixesPsr0[$first][$prefix] 2098817535bSAndreas Gohr ); 2108817535bSAndreas Gohr } else { 2118817535bSAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 2128817535bSAndreas Gohr $this->prefixesPsr0[$first][$prefix], 213*3379af09SAndreas Gohr $paths 2148817535bSAndreas Gohr ); 2158817535bSAndreas Gohr } 2168817535bSAndreas Gohr } 2178817535bSAndreas Gohr 2188817535bSAndreas Gohr /** 2198817535bSAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, either 2208817535bSAndreas Gohr * appending or prepending to the ones previously set for this namespace. 2218817535bSAndreas Gohr * 2228817535bSAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 223*3379af09SAndreas Gohr * @param list<string>|string $paths The PSR-4 base directories 2248817535bSAndreas Gohr * @param bool $prepend Whether to prepend the directories 2258817535bSAndreas Gohr * 2268817535bSAndreas Gohr * @throws \InvalidArgumentException 2278817535bSAndreas Gohr * 2288817535bSAndreas Gohr * @return void 2298817535bSAndreas Gohr */ 2308817535bSAndreas Gohr public function addPsr4($prefix, $paths, $prepend = false) 2318817535bSAndreas Gohr { 232*3379af09SAndreas Gohr $paths = (array) $paths; 2338817535bSAndreas Gohr if (!$prefix) { 2348817535bSAndreas Gohr // Register directories for the root namespace. 2358817535bSAndreas Gohr if ($prepend) { 2368817535bSAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 237*3379af09SAndreas Gohr $paths, 2388817535bSAndreas Gohr $this->fallbackDirsPsr4 2398817535bSAndreas Gohr ); 2408817535bSAndreas Gohr } else { 2418817535bSAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 2428817535bSAndreas Gohr $this->fallbackDirsPsr4, 243*3379af09SAndreas Gohr $paths 2448817535bSAndreas Gohr ); 2458817535bSAndreas Gohr } 2468817535bSAndreas Gohr } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 2478817535bSAndreas Gohr // Register directories for a new namespace. 2488817535bSAndreas Gohr $length = strlen($prefix); 2498817535bSAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 2508817535bSAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 2518817535bSAndreas Gohr } 2528817535bSAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 253*3379af09SAndreas Gohr $this->prefixDirsPsr4[$prefix] = $paths; 2548817535bSAndreas Gohr } elseif ($prepend) { 2558817535bSAndreas Gohr // Prepend directories for an already registered namespace. 2568817535bSAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 257*3379af09SAndreas Gohr $paths, 2588817535bSAndreas Gohr $this->prefixDirsPsr4[$prefix] 2598817535bSAndreas Gohr ); 2608817535bSAndreas Gohr } else { 2618817535bSAndreas Gohr // Append directories for an already registered namespace. 2628817535bSAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 2638817535bSAndreas Gohr $this->prefixDirsPsr4[$prefix], 264*3379af09SAndreas Gohr $paths 2658817535bSAndreas Gohr ); 2668817535bSAndreas Gohr } 2678817535bSAndreas Gohr } 2688817535bSAndreas Gohr 2698817535bSAndreas Gohr /** 2708817535bSAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, 2718817535bSAndreas Gohr * replacing any others previously set for this prefix. 2728817535bSAndreas Gohr * 2738817535bSAndreas Gohr * @param string $prefix The prefix 274*3379af09SAndreas Gohr * @param list<string>|string $paths The PSR-0 base directories 2758817535bSAndreas Gohr * 2768817535bSAndreas Gohr * @return void 2778817535bSAndreas Gohr */ 2788817535bSAndreas Gohr public function set($prefix, $paths) 2798817535bSAndreas Gohr { 2808817535bSAndreas Gohr if (!$prefix) { 2818817535bSAndreas Gohr $this->fallbackDirsPsr0 = (array) $paths; 2828817535bSAndreas Gohr } else { 2838817535bSAndreas Gohr $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 2848817535bSAndreas Gohr } 2858817535bSAndreas Gohr } 2868817535bSAndreas Gohr 2878817535bSAndreas Gohr /** 2888817535bSAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, 2898817535bSAndreas Gohr * replacing any others previously set for this namespace. 2908817535bSAndreas Gohr * 2918817535bSAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 292*3379af09SAndreas Gohr * @param list<string>|string $paths The PSR-4 base directories 2938817535bSAndreas Gohr * 2948817535bSAndreas Gohr * @throws \InvalidArgumentException 2958817535bSAndreas Gohr * 2968817535bSAndreas Gohr * @return void 2978817535bSAndreas Gohr */ 2988817535bSAndreas Gohr public function setPsr4($prefix, $paths) 2998817535bSAndreas Gohr { 3008817535bSAndreas Gohr if (!$prefix) { 3018817535bSAndreas Gohr $this->fallbackDirsPsr4 = (array) $paths; 3028817535bSAndreas Gohr } else { 3038817535bSAndreas Gohr $length = strlen($prefix); 3048817535bSAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 3058817535bSAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 3068817535bSAndreas Gohr } 3078817535bSAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 3088817535bSAndreas Gohr $this->prefixDirsPsr4[$prefix] = (array) $paths; 3098817535bSAndreas Gohr } 3108817535bSAndreas Gohr } 3118817535bSAndreas Gohr 3128817535bSAndreas Gohr /** 3138817535bSAndreas Gohr * Turns on searching the include path for class files. 3148817535bSAndreas Gohr * 3158817535bSAndreas Gohr * @param bool $useIncludePath 3168817535bSAndreas Gohr * 3178817535bSAndreas Gohr * @return void 3188817535bSAndreas Gohr */ 3198817535bSAndreas Gohr public function setUseIncludePath($useIncludePath) 3208817535bSAndreas Gohr { 3218817535bSAndreas Gohr $this->useIncludePath = $useIncludePath; 3228817535bSAndreas Gohr } 3238817535bSAndreas Gohr 3248817535bSAndreas Gohr /** 3258817535bSAndreas Gohr * Can be used to check if the autoloader uses the include path to check 3268817535bSAndreas Gohr * for classes. 3278817535bSAndreas Gohr * 3288817535bSAndreas Gohr * @return bool 3298817535bSAndreas Gohr */ 3308817535bSAndreas Gohr public function getUseIncludePath() 3318817535bSAndreas Gohr { 3328817535bSAndreas Gohr return $this->useIncludePath; 3338817535bSAndreas Gohr } 3348817535bSAndreas Gohr 3358817535bSAndreas Gohr /** 3368817535bSAndreas Gohr * Turns off searching the prefix and fallback directories for classes 3378817535bSAndreas Gohr * that have not been registered with the class map. 3388817535bSAndreas Gohr * 3398817535bSAndreas Gohr * @param bool $classMapAuthoritative 3408817535bSAndreas Gohr * 3418817535bSAndreas Gohr * @return void 3428817535bSAndreas Gohr */ 3438817535bSAndreas Gohr public function setClassMapAuthoritative($classMapAuthoritative) 3448817535bSAndreas Gohr { 3458817535bSAndreas Gohr $this->classMapAuthoritative = $classMapAuthoritative; 3468817535bSAndreas Gohr } 3478817535bSAndreas Gohr 3488817535bSAndreas Gohr /** 3498817535bSAndreas Gohr * Should class lookup fail if not found in the current class map? 3508817535bSAndreas Gohr * 3518817535bSAndreas Gohr * @return bool 3528817535bSAndreas Gohr */ 3538817535bSAndreas Gohr public function isClassMapAuthoritative() 3548817535bSAndreas Gohr { 3558817535bSAndreas Gohr return $this->classMapAuthoritative; 3568817535bSAndreas Gohr } 3578817535bSAndreas Gohr 3588817535bSAndreas Gohr /** 3598817535bSAndreas Gohr * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 3608817535bSAndreas Gohr * 3618817535bSAndreas Gohr * @param string|null $apcuPrefix 3628817535bSAndreas Gohr * 3638817535bSAndreas Gohr * @return void 3648817535bSAndreas Gohr */ 3658817535bSAndreas Gohr public function setApcuPrefix($apcuPrefix) 3668817535bSAndreas Gohr { 3678817535bSAndreas Gohr $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 3688817535bSAndreas Gohr } 3698817535bSAndreas Gohr 3708817535bSAndreas Gohr /** 3718817535bSAndreas Gohr * The APCu prefix in use, or null if APCu caching is not enabled. 3728817535bSAndreas Gohr * 3738817535bSAndreas Gohr * @return string|null 3748817535bSAndreas Gohr */ 3758817535bSAndreas Gohr public function getApcuPrefix() 3768817535bSAndreas Gohr { 3778817535bSAndreas Gohr return $this->apcuPrefix; 3788817535bSAndreas Gohr } 3798817535bSAndreas Gohr 3808817535bSAndreas Gohr /** 3818817535bSAndreas Gohr * Registers this instance as an autoloader. 3828817535bSAndreas Gohr * 3838817535bSAndreas Gohr * @param bool $prepend Whether to prepend the autoloader or not 3848817535bSAndreas Gohr * 3858817535bSAndreas Gohr * @return void 3868817535bSAndreas Gohr */ 3878817535bSAndreas Gohr public function register($prepend = false) 3888817535bSAndreas Gohr { 3898817535bSAndreas Gohr spl_autoload_register(array($this, 'loadClass'), true, $prepend); 3908817535bSAndreas Gohr 3918817535bSAndreas Gohr if (null === $this->vendorDir) { 3928817535bSAndreas Gohr return; 3938817535bSAndreas Gohr } 3948817535bSAndreas Gohr 3958817535bSAndreas Gohr if ($prepend) { 3968817535bSAndreas Gohr self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 3978817535bSAndreas Gohr } else { 3988817535bSAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 3998817535bSAndreas Gohr self::$registeredLoaders[$this->vendorDir] = $this; 4008817535bSAndreas Gohr } 4018817535bSAndreas Gohr } 4028817535bSAndreas Gohr 4038817535bSAndreas Gohr /** 4048817535bSAndreas Gohr * Unregisters this instance as an autoloader. 4058817535bSAndreas Gohr * 4068817535bSAndreas Gohr * @return void 4078817535bSAndreas Gohr */ 4088817535bSAndreas Gohr public function unregister() 4098817535bSAndreas Gohr { 4108817535bSAndreas Gohr spl_autoload_unregister(array($this, 'loadClass')); 4118817535bSAndreas Gohr 4128817535bSAndreas Gohr if (null !== $this->vendorDir) { 4138817535bSAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 4148817535bSAndreas Gohr } 4158817535bSAndreas Gohr } 4168817535bSAndreas Gohr 4178817535bSAndreas Gohr /** 4188817535bSAndreas Gohr * Loads the given class or interface. 4198817535bSAndreas Gohr * 4208817535bSAndreas Gohr * @param string $class The name of the class 4218817535bSAndreas Gohr * @return true|null True if loaded, null otherwise 4228817535bSAndreas Gohr */ 4238817535bSAndreas Gohr public function loadClass($class) 4248817535bSAndreas Gohr { 4258817535bSAndreas Gohr if ($file = $this->findFile($class)) { 4268817535bSAndreas Gohr $includeFile = self::$includeFile; 4278817535bSAndreas Gohr $includeFile($file); 4288817535bSAndreas Gohr 4298817535bSAndreas Gohr return true; 4308817535bSAndreas Gohr } 4318817535bSAndreas Gohr 4328817535bSAndreas Gohr return null; 4338817535bSAndreas Gohr } 4348817535bSAndreas Gohr 4358817535bSAndreas Gohr /** 4368817535bSAndreas Gohr * Finds the path to the file where the class is defined. 4378817535bSAndreas Gohr * 4388817535bSAndreas Gohr * @param string $class The name of the class 4398817535bSAndreas Gohr * 4408817535bSAndreas Gohr * @return string|false The path if found, false otherwise 4418817535bSAndreas Gohr */ 4428817535bSAndreas Gohr public function findFile($class) 4438817535bSAndreas Gohr { 4448817535bSAndreas Gohr // class map lookup 4458817535bSAndreas Gohr if (isset($this->classMap[$class])) { 4468817535bSAndreas Gohr return $this->classMap[$class]; 4478817535bSAndreas Gohr } 4488817535bSAndreas Gohr if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 4498817535bSAndreas Gohr return false; 4508817535bSAndreas Gohr } 4518817535bSAndreas Gohr if (null !== $this->apcuPrefix) { 4528817535bSAndreas Gohr $file = apcu_fetch($this->apcuPrefix.$class, $hit); 4538817535bSAndreas Gohr if ($hit) { 4548817535bSAndreas Gohr return $file; 4558817535bSAndreas Gohr } 4568817535bSAndreas Gohr } 4578817535bSAndreas Gohr 4588817535bSAndreas Gohr $file = $this->findFileWithExtension($class, '.php'); 4598817535bSAndreas Gohr 4608817535bSAndreas Gohr // Search for Hack files if we are running on HHVM 4618817535bSAndreas Gohr if (false === $file && defined('HHVM_VERSION')) { 4628817535bSAndreas Gohr $file = $this->findFileWithExtension($class, '.hh'); 4638817535bSAndreas Gohr } 4648817535bSAndreas Gohr 4658817535bSAndreas Gohr if (null !== $this->apcuPrefix) { 4668817535bSAndreas Gohr apcu_add($this->apcuPrefix.$class, $file); 4678817535bSAndreas Gohr } 4688817535bSAndreas Gohr 4698817535bSAndreas Gohr if (false === $file) { 4708817535bSAndreas Gohr // Remember that this class does not exist. 4718817535bSAndreas Gohr $this->missingClasses[$class] = true; 4728817535bSAndreas Gohr } 4738817535bSAndreas Gohr 4748817535bSAndreas Gohr return $file; 4758817535bSAndreas Gohr } 4768817535bSAndreas Gohr 4778817535bSAndreas Gohr /** 478*3379af09SAndreas Gohr * Returns the currently registered loaders keyed by their corresponding vendor directories. 4798817535bSAndreas Gohr * 480*3379af09SAndreas Gohr * @return array<string, self> 4818817535bSAndreas Gohr */ 4828817535bSAndreas Gohr public static function getRegisteredLoaders() 4838817535bSAndreas Gohr { 4848817535bSAndreas Gohr return self::$registeredLoaders; 4858817535bSAndreas Gohr } 4868817535bSAndreas Gohr 4878817535bSAndreas Gohr /** 4888817535bSAndreas Gohr * @param string $class 4898817535bSAndreas Gohr * @param string $ext 4908817535bSAndreas Gohr * @return string|false 4918817535bSAndreas Gohr */ 4928817535bSAndreas Gohr private function findFileWithExtension($class, $ext) 4938817535bSAndreas Gohr { 4948817535bSAndreas Gohr // PSR-4 lookup 4958817535bSAndreas Gohr $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 4968817535bSAndreas Gohr 4978817535bSAndreas Gohr $first = $class[0]; 4988817535bSAndreas Gohr if (isset($this->prefixLengthsPsr4[$first])) { 4998817535bSAndreas Gohr $subPath = $class; 5008817535bSAndreas Gohr while (false !== $lastPos = strrpos($subPath, '\\')) { 5018817535bSAndreas Gohr $subPath = substr($subPath, 0, $lastPos); 5028817535bSAndreas Gohr $search = $subPath . '\\'; 5038817535bSAndreas Gohr if (isset($this->prefixDirsPsr4[$search])) { 5048817535bSAndreas Gohr $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 5058817535bSAndreas Gohr foreach ($this->prefixDirsPsr4[$search] as $dir) { 5068817535bSAndreas Gohr if (file_exists($file = $dir . $pathEnd)) { 5078817535bSAndreas Gohr return $file; 5088817535bSAndreas Gohr } 5098817535bSAndreas Gohr } 5108817535bSAndreas Gohr } 5118817535bSAndreas Gohr } 5128817535bSAndreas Gohr } 5138817535bSAndreas Gohr 5148817535bSAndreas Gohr // PSR-4 fallback dirs 5158817535bSAndreas Gohr foreach ($this->fallbackDirsPsr4 as $dir) { 5168817535bSAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 5178817535bSAndreas Gohr return $file; 5188817535bSAndreas Gohr } 5198817535bSAndreas Gohr } 5208817535bSAndreas Gohr 5218817535bSAndreas Gohr // PSR-0 lookup 5228817535bSAndreas Gohr if (false !== $pos = strrpos($class, '\\')) { 5238817535bSAndreas Gohr // namespaced class name 5248817535bSAndreas Gohr $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 5258817535bSAndreas Gohr . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 5268817535bSAndreas Gohr } else { 5278817535bSAndreas Gohr // PEAR-like class name 5288817535bSAndreas Gohr $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 5298817535bSAndreas Gohr } 5308817535bSAndreas Gohr 5318817535bSAndreas Gohr if (isset($this->prefixesPsr0[$first])) { 5328817535bSAndreas Gohr foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 5338817535bSAndreas Gohr if (0 === strpos($class, $prefix)) { 5348817535bSAndreas Gohr foreach ($dirs as $dir) { 5358817535bSAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 5368817535bSAndreas Gohr return $file; 5378817535bSAndreas Gohr } 5388817535bSAndreas Gohr } 5398817535bSAndreas Gohr } 5408817535bSAndreas Gohr } 5418817535bSAndreas Gohr } 5428817535bSAndreas Gohr 5438817535bSAndreas Gohr // PSR-0 fallback dirs 5448817535bSAndreas Gohr foreach ($this->fallbackDirsPsr0 as $dir) { 5458817535bSAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 5468817535bSAndreas Gohr return $file; 5478817535bSAndreas Gohr } 5488817535bSAndreas Gohr } 5498817535bSAndreas Gohr 5508817535bSAndreas Gohr // PSR-0 include paths. 5518817535bSAndreas Gohr if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 5528817535bSAndreas Gohr return $file; 5538817535bSAndreas Gohr } 5548817535bSAndreas Gohr 5558817535bSAndreas Gohr return false; 5568817535bSAndreas Gohr } 5578817535bSAndreas Gohr 5588817535bSAndreas Gohr /** 5598817535bSAndreas Gohr * @return void 5608817535bSAndreas Gohr */ 5618817535bSAndreas Gohr private static function initializeIncludeClosure() 5628817535bSAndreas Gohr { 5638817535bSAndreas Gohr if (self::$includeFile !== null) { 5648817535bSAndreas Gohr return; 5658817535bSAndreas Gohr } 5668817535bSAndreas Gohr 5678817535bSAndreas Gohr /** 5688817535bSAndreas Gohr * Scope isolated include. 5698817535bSAndreas Gohr * 5708817535bSAndreas Gohr * Prevents access to $this/self from included files. 5718817535bSAndreas Gohr * 5728817535bSAndreas Gohr * @param string $file 5738817535bSAndreas Gohr * @return void 5748817535bSAndreas Gohr */ 5758817535bSAndreas Gohr self::$includeFile = \Closure::bind(static function($file) { 5768817535bSAndreas Gohr include $file; 5778817535bSAndreas Gohr }, null, null); 5788817535bSAndreas Gohr } 5798817535bSAndreas Gohr} 580