1605f8e8dSAndreas Gohr<?php 2605f8e8dSAndreas Gohr 3605f8e8dSAndreas Gohr/* 4605f8e8dSAndreas Gohr * This file is part of Composer. 5605f8e8dSAndreas Gohr * 6605f8e8dSAndreas Gohr * (c) Nils Adermann <naderman@naderman.de> 7605f8e8dSAndreas Gohr * Jordi Boggiano <j.boggiano@seld.be> 8605f8e8dSAndreas Gohr * 9605f8e8dSAndreas Gohr * For the full copyright and license information, please view the LICENSE 10605f8e8dSAndreas Gohr * file that was distributed with this source code. 11605f8e8dSAndreas Gohr */ 12605f8e8dSAndreas Gohr 13605f8e8dSAndreas Gohrnamespace Composer\Autoload; 14605f8e8dSAndreas Gohr 15605f8e8dSAndreas Gohr/** 167a33d2f8SNiklas Keller * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17605f8e8dSAndreas Gohr * 18605f8e8dSAndreas Gohr * $loader = new \Composer\Autoload\ClassLoader(); 19605f8e8dSAndreas Gohr * 20605f8e8dSAndreas Gohr * // register classes with namespaces 21605f8e8dSAndreas Gohr * $loader->add('Symfony\Component', __DIR__.'/component'); 22605f8e8dSAndreas Gohr * $loader->add('Symfony', __DIR__.'/framework'); 23605f8e8dSAndreas Gohr * 24605f8e8dSAndreas Gohr * // activate the autoloader 25605f8e8dSAndreas Gohr * $loader->register(); 26605f8e8dSAndreas Gohr * 27605f8e8dSAndreas Gohr * // to enable searching the include path (eg. for PEAR packages) 28605f8e8dSAndreas Gohr * $loader->setUseIncludePath(true); 29605f8e8dSAndreas Gohr * 30605f8e8dSAndreas Gohr * In this example, if you try to use a class in the Symfony\Component 31605f8e8dSAndreas Gohr * namespace or one of its children (Symfony\Component\Console for instance), 32605f8e8dSAndreas Gohr * the autoloader will first look for the class under the component/ 33605f8e8dSAndreas Gohr * directory, and it will then fallback to the framework/ directory if not 34605f8e8dSAndreas Gohr * found before giving up. 35605f8e8dSAndreas Gohr * 36605f8e8dSAndreas Gohr * This class is loosely based on the Symfony UniversalClassLoader. 37605f8e8dSAndreas Gohr * 38605f8e8dSAndreas Gohr * @author Fabien Potencier <fabien@symfony.com> 39605f8e8dSAndreas Gohr * @author Jordi Boggiano <j.boggiano@seld.be> 406cb05674SAndreas Gohr * @see https://www.php-fig.org/psr/psr-0/ 416cb05674SAndreas Gohr * @see https://www.php-fig.org/psr/psr-4/ 42605f8e8dSAndreas Gohr */ 43605f8e8dSAndreas Gohrclass ClassLoader 44605f8e8dSAndreas Gohr{ 45*d3233986SAndreas Gohr /** @var ?string */ 466cb05674SAndreas Gohr private $vendorDir; 476cb05674SAndreas Gohr 48605f8e8dSAndreas Gohr // PSR-4 49*d3233986SAndreas Gohr /** 50*d3233986SAndreas Gohr * @var array[] 51*d3233986SAndreas Gohr * @psalm-var array<string, array<string, int>> 52*d3233986SAndreas Gohr */ 53605f8e8dSAndreas Gohr private $prefixLengthsPsr4 = array(); 54*d3233986SAndreas Gohr /** 55*d3233986SAndreas Gohr * @var array[] 56*d3233986SAndreas Gohr * @psalm-var array<string, array<int, string>> 57*d3233986SAndreas Gohr */ 58605f8e8dSAndreas Gohr private $prefixDirsPsr4 = array(); 59*d3233986SAndreas Gohr /** 60*d3233986SAndreas Gohr * @var array[] 61*d3233986SAndreas Gohr * @psalm-var array<string, string> 62*d3233986SAndreas Gohr */ 63605f8e8dSAndreas Gohr private $fallbackDirsPsr4 = array(); 64605f8e8dSAndreas Gohr 65605f8e8dSAndreas Gohr // PSR-0 66*d3233986SAndreas Gohr /** 67*d3233986SAndreas Gohr * @var array[] 68*d3233986SAndreas Gohr * @psalm-var array<string, array<string, string[]>> 69*d3233986SAndreas Gohr */ 70605f8e8dSAndreas Gohr private $prefixesPsr0 = array(); 71*d3233986SAndreas Gohr /** 72*d3233986SAndreas Gohr * @var array[] 73*d3233986SAndreas Gohr * @psalm-var array<string, string> 74*d3233986SAndreas Gohr */ 75605f8e8dSAndreas Gohr private $fallbackDirsPsr0 = array(); 76605f8e8dSAndreas Gohr 77*d3233986SAndreas Gohr /** @var bool */ 78605f8e8dSAndreas Gohr private $useIncludePath = false; 79*d3233986SAndreas Gohr 80*d3233986SAndreas Gohr /** 81*d3233986SAndreas Gohr * @var string[] 82*d3233986SAndreas Gohr * @psalm-var array<string, string> 83*d3233986SAndreas Gohr */ 84605f8e8dSAndreas Gohr private $classMap = array(); 85*d3233986SAndreas Gohr 86*d3233986SAndreas Gohr /** @var bool */ 87605f8e8dSAndreas Gohr private $classMapAuthoritative = false; 88*d3233986SAndreas Gohr 89*d3233986SAndreas Gohr /** 90*d3233986SAndreas Gohr * @var bool[] 91*d3233986SAndreas Gohr * @psalm-var array<string, bool> 92*d3233986SAndreas Gohr */ 937a33d2f8SNiklas Keller private $missingClasses = array(); 94*d3233986SAndreas Gohr 95*d3233986SAndreas Gohr /** @var ?string */ 96e0dd796dSAndreas Gohr private $apcuPrefix; 97605f8e8dSAndreas Gohr 98*d3233986SAndreas Gohr /** 99*d3233986SAndreas Gohr * @var self[] 100*d3233986SAndreas Gohr */ 1016cb05674SAndreas Gohr private static $registeredLoaders = array(); 1026cb05674SAndreas Gohr 103*d3233986SAndreas Gohr /** 104*d3233986SAndreas Gohr * @param ?string $vendorDir 105*d3233986SAndreas Gohr */ 1066cb05674SAndreas Gohr public function __construct($vendorDir = null) 1076cb05674SAndreas Gohr { 1086cb05674SAndreas Gohr $this->vendorDir = $vendorDir; 1096cb05674SAndreas Gohr } 1106cb05674SAndreas Gohr 111*d3233986SAndreas Gohr /** 112*d3233986SAndreas Gohr * @return string[] 113*d3233986SAndreas Gohr */ 114605f8e8dSAndreas Gohr public function getPrefixes() 115605f8e8dSAndreas Gohr { 116605f8e8dSAndreas Gohr if (!empty($this->prefixesPsr0)) { 117a3bfbb3cSAndreas Gohr return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 118605f8e8dSAndreas Gohr } 119605f8e8dSAndreas Gohr 120605f8e8dSAndreas Gohr return array(); 121605f8e8dSAndreas Gohr } 122605f8e8dSAndreas Gohr 123*d3233986SAndreas Gohr /** 124*d3233986SAndreas Gohr * @return array[] 125*d3233986SAndreas Gohr * @psalm-return array<string, array<int, string>> 126*d3233986SAndreas Gohr */ 127605f8e8dSAndreas Gohr public function getPrefixesPsr4() 128605f8e8dSAndreas Gohr { 129605f8e8dSAndreas Gohr return $this->prefixDirsPsr4; 130605f8e8dSAndreas Gohr } 131605f8e8dSAndreas Gohr 132*d3233986SAndreas Gohr /** 133*d3233986SAndreas Gohr * @return array[] 134*d3233986SAndreas Gohr * @psalm-return array<string, string> 135*d3233986SAndreas Gohr */ 136605f8e8dSAndreas Gohr public function getFallbackDirs() 137605f8e8dSAndreas Gohr { 138605f8e8dSAndreas Gohr return $this->fallbackDirsPsr0; 139605f8e8dSAndreas Gohr } 140605f8e8dSAndreas Gohr 141*d3233986SAndreas Gohr /** 142*d3233986SAndreas Gohr * @return array[] 143*d3233986SAndreas Gohr * @psalm-return array<string, string> 144*d3233986SAndreas Gohr */ 145605f8e8dSAndreas Gohr public function getFallbackDirsPsr4() 146605f8e8dSAndreas Gohr { 147605f8e8dSAndreas Gohr return $this->fallbackDirsPsr4; 148605f8e8dSAndreas Gohr } 149605f8e8dSAndreas Gohr 150*d3233986SAndreas Gohr /** 151*d3233986SAndreas Gohr * @return string[] Array of classname => path 152*d3233986SAndreas Gohr * @psalm-return array<string, string> 153*d3233986SAndreas Gohr */ 154605f8e8dSAndreas Gohr public function getClassMap() 155605f8e8dSAndreas Gohr { 156605f8e8dSAndreas Gohr return $this->classMap; 157605f8e8dSAndreas Gohr } 158605f8e8dSAndreas Gohr 159605f8e8dSAndreas Gohr /** 160*d3233986SAndreas Gohr * @param string[] $classMap Class to filename map 161*d3233986SAndreas Gohr * @psalm-param array<string, string> $classMap 162*d3233986SAndreas Gohr * 163*d3233986SAndreas Gohr * @return void 164605f8e8dSAndreas Gohr */ 165605f8e8dSAndreas Gohr public function addClassMap(array $classMap) 166605f8e8dSAndreas Gohr { 167605f8e8dSAndreas Gohr if ($this->classMap) { 168605f8e8dSAndreas Gohr $this->classMap = array_merge($this->classMap, $classMap); 169605f8e8dSAndreas Gohr } else { 170605f8e8dSAndreas Gohr $this->classMap = $classMap; 171605f8e8dSAndreas Gohr } 172605f8e8dSAndreas Gohr } 173605f8e8dSAndreas Gohr 174605f8e8dSAndreas Gohr /** 175605f8e8dSAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, either 176605f8e8dSAndreas Gohr * appending or prepending to the ones previously set for this prefix. 177605f8e8dSAndreas Gohr * 178605f8e8dSAndreas Gohr * @param string $prefix The prefix 179*d3233986SAndreas Gohr * @param string[]|string $paths The PSR-0 root directories 180605f8e8dSAndreas Gohr * @param bool $prepend Whether to prepend the directories 181*d3233986SAndreas Gohr * 182*d3233986SAndreas Gohr * @return void 183605f8e8dSAndreas Gohr */ 184605f8e8dSAndreas Gohr public function add($prefix, $paths, $prepend = false) 185605f8e8dSAndreas Gohr { 186605f8e8dSAndreas Gohr if (!$prefix) { 187605f8e8dSAndreas Gohr if ($prepend) { 188605f8e8dSAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 189605f8e8dSAndreas Gohr (array) $paths, 190605f8e8dSAndreas Gohr $this->fallbackDirsPsr0 191605f8e8dSAndreas Gohr ); 192605f8e8dSAndreas Gohr } else { 193605f8e8dSAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 194605f8e8dSAndreas Gohr $this->fallbackDirsPsr0, 195605f8e8dSAndreas Gohr (array) $paths 196605f8e8dSAndreas Gohr ); 197605f8e8dSAndreas Gohr } 198605f8e8dSAndreas Gohr 199605f8e8dSAndreas Gohr return; 200605f8e8dSAndreas Gohr } 201605f8e8dSAndreas Gohr 202605f8e8dSAndreas Gohr $first = $prefix[0]; 203605f8e8dSAndreas Gohr if (!isset($this->prefixesPsr0[$first][$prefix])) { 204605f8e8dSAndreas Gohr $this->prefixesPsr0[$first][$prefix] = (array) $paths; 205605f8e8dSAndreas Gohr 206605f8e8dSAndreas Gohr return; 207605f8e8dSAndreas Gohr } 208605f8e8dSAndreas Gohr if ($prepend) { 209605f8e8dSAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 210605f8e8dSAndreas Gohr (array) $paths, 211605f8e8dSAndreas Gohr $this->prefixesPsr0[$first][$prefix] 212605f8e8dSAndreas Gohr ); 213605f8e8dSAndreas Gohr } else { 214605f8e8dSAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 215605f8e8dSAndreas Gohr $this->prefixesPsr0[$first][$prefix], 216605f8e8dSAndreas Gohr (array) $paths 217605f8e8dSAndreas Gohr ); 218605f8e8dSAndreas Gohr } 219605f8e8dSAndreas Gohr } 220605f8e8dSAndreas Gohr 221605f8e8dSAndreas Gohr /** 222605f8e8dSAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, either 223605f8e8dSAndreas Gohr * appending or prepending to the ones previously set for this namespace. 224605f8e8dSAndreas Gohr * 225605f8e8dSAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 226*d3233986SAndreas Gohr * @param string[]|string $paths The PSR-4 base directories 227605f8e8dSAndreas Gohr * @param bool $prepend Whether to prepend the directories 228605f8e8dSAndreas Gohr * 229605f8e8dSAndreas Gohr * @throws \InvalidArgumentException 230*d3233986SAndreas Gohr * 231*d3233986SAndreas Gohr * @return void 232605f8e8dSAndreas Gohr */ 233605f8e8dSAndreas Gohr public function addPsr4($prefix, $paths, $prepend = false) 234605f8e8dSAndreas Gohr { 235605f8e8dSAndreas Gohr if (!$prefix) { 236605f8e8dSAndreas Gohr // Register directories for the root namespace. 237605f8e8dSAndreas Gohr if ($prepend) { 238605f8e8dSAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 239605f8e8dSAndreas Gohr (array) $paths, 240605f8e8dSAndreas Gohr $this->fallbackDirsPsr4 241605f8e8dSAndreas Gohr ); 242605f8e8dSAndreas Gohr } else { 243605f8e8dSAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 244605f8e8dSAndreas Gohr $this->fallbackDirsPsr4, 245605f8e8dSAndreas Gohr (array) $paths 246605f8e8dSAndreas Gohr ); 247605f8e8dSAndreas Gohr } 248605f8e8dSAndreas Gohr } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 249605f8e8dSAndreas Gohr // Register directories for a new namespace. 250605f8e8dSAndreas Gohr $length = strlen($prefix); 251605f8e8dSAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 252605f8e8dSAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 253605f8e8dSAndreas Gohr } 254605f8e8dSAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 255605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix] = (array) $paths; 256605f8e8dSAndreas Gohr } elseif ($prepend) { 257605f8e8dSAndreas Gohr // Prepend directories for an already registered namespace. 258605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 259605f8e8dSAndreas Gohr (array) $paths, 260605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix] 261605f8e8dSAndreas Gohr ); 262605f8e8dSAndreas Gohr } else { 263605f8e8dSAndreas Gohr // Append directories for an already registered namespace. 264605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 265605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix], 266605f8e8dSAndreas Gohr (array) $paths 267605f8e8dSAndreas Gohr ); 268605f8e8dSAndreas Gohr } 269605f8e8dSAndreas Gohr } 270605f8e8dSAndreas Gohr 271605f8e8dSAndreas Gohr /** 272605f8e8dSAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, 273605f8e8dSAndreas Gohr * replacing any others previously set for this prefix. 274605f8e8dSAndreas Gohr * 275605f8e8dSAndreas Gohr * @param string $prefix The prefix 276*d3233986SAndreas Gohr * @param string[]|string $paths The PSR-0 base directories 277*d3233986SAndreas Gohr * 278*d3233986SAndreas Gohr * @return void 279605f8e8dSAndreas Gohr */ 280605f8e8dSAndreas Gohr public function set($prefix, $paths) 281605f8e8dSAndreas Gohr { 282605f8e8dSAndreas Gohr if (!$prefix) { 283605f8e8dSAndreas Gohr $this->fallbackDirsPsr0 = (array) $paths; 284605f8e8dSAndreas Gohr } else { 285605f8e8dSAndreas Gohr $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 286605f8e8dSAndreas Gohr } 287605f8e8dSAndreas Gohr } 288605f8e8dSAndreas Gohr 289605f8e8dSAndreas Gohr /** 290605f8e8dSAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, 291605f8e8dSAndreas Gohr * replacing any others previously set for this namespace. 292605f8e8dSAndreas Gohr * 293605f8e8dSAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 294*d3233986SAndreas Gohr * @param string[]|string $paths The PSR-4 base directories 295605f8e8dSAndreas Gohr * 296605f8e8dSAndreas Gohr * @throws \InvalidArgumentException 297*d3233986SAndreas Gohr * 298*d3233986SAndreas Gohr * @return void 299605f8e8dSAndreas Gohr */ 300605f8e8dSAndreas Gohr public function setPsr4($prefix, $paths) 301605f8e8dSAndreas Gohr { 302605f8e8dSAndreas Gohr if (!$prefix) { 303605f8e8dSAndreas Gohr $this->fallbackDirsPsr4 = (array) $paths; 304605f8e8dSAndreas Gohr } else { 305605f8e8dSAndreas Gohr $length = strlen($prefix); 306605f8e8dSAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 307605f8e8dSAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 308605f8e8dSAndreas Gohr } 309605f8e8dSAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 310605f8e8dSAndreas Gohr $this->prefixDirsPsr4[$prefix] = (array) $paths; 311605f8e8dSAndreas Gohr } 312605f8e8dSAndreas Gohr } 313605f8e8dSAndreas Gohr 314605f8e8dSAndreas Gohr /** 315605f8e8dSAndreas Gohr * Turns on searching the include path for class files. 316605f8e8dSAndreas Gohr * 317605f8e8dSAndreas Gohr * @param bool $useIncludePath 318*d3233986SAndreas Gohr * 319*d3233986SAndreas Gohr * @return void 320605f8e8dSAndreas Gohr */ 321605f8e8dSAndreas Gohr public function setUseIncludePath($useIncludePath) 322605f8e8dSAndreas Gohr { 323605f8e8dSAndreas Gohr $this->useIncludePath = $useIncludePath; 324605f8e8dSAndreas Gohr } 325605f8e8dSAndreas Gohr 326605f8e8dSAndreas Gohr /** 327605f8e8dSAndreas Gohr * Can be used to check if the autoloader uses the include path to check 328605f8e8dSAndreas Gohr * for classes. 329605f8e8dSAndreas Gohr * 330605f8e8dSAndreas Gohr * @return bool 331605f8e8dSAndreas Gohr */ 332605f8e8dSAndreas Gohr public function getUseIncludePath() 333605f8e8dSAndreas Gohr { 334605f8e8dSAndreas Gohr return $this->useIncludePath; 335605f8e8dSAndreas Gohr } 336605f8e8dSAndreas Gohr 337605f8e8dSAndreas Gohr /** 338605f8e8dSAndreas Gohr * Turns off searching the prefix and fallback directories for classes 339605f8e8dSAndreas Gohr * that have not been registered with the class map. 340605f8e8dSAndreas Gohr * 341605f8e8dSAndreas Gohr * @param bool $classMapAuthoritative 342*d3233986SAndreas Gohr * 343*d3233986SAndreas Gohr * @return void 344605f8e8dSAndreas Gohr */ 345605f8e8dSAndreas Gohr public function setClassMapAuthoritative($classMapAuthoritative) 346605f8e8dSAndreas Gohr { 347605f8e8dSAndreas Gohr $this->classMapAuthoritative = $classMapAuthoritative; 348605f8e8dSAndreas Gohr } 349605f8e8dSAndreas Gohr 350605f8e8dSAndreas Gohr /** 351605f8e8dSAndreas Gohr * Should class lookup fail if not found in the current class map? 352605f8e8dSAndreas Gohr * 353605f8e8dSAndreas Gohr * @return bool 354605f8e8dSAndreas Gohr */ 355605f8e8dSAndreas Gohr public function isClassMapAuthoritative() 356605f8e8dSAndreas Gohr { 357605f8e8dSAndreas Gohr return $this->classMapAuthoritative; 358605f8e8dSAndreas Gohr } 359605f8e8dSAndreas Gohr 360605f8e8dSAndreas Gohr /** 361e0dd796dSAndreas Gohr * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 362e0dd796dSAndreas Gohr * 363e0dd796dSAndreas Gohr * @param string|null $apcuPrefix 364*d3233986SAndreas Gohr * 365*d3233986SAndreas Gohr * @return void 366e0dd796dSAndreas Gohr */ 367e0dd796dSAndreas Gohr public function setApcuPrefix($apcuPrefix) 368e0dd796dSAndreas Gohr { 369e43cd7e1SAndreas Gohr $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 370e0dd796dSAndreas Gohr } 371e0dd796dSAndreas Gohr 372e0dd796dSAndreas Gohr /** 373e0dd796dSAndreas Gohr * The APCu prefix in use, or null if APCu caching is not enabled. 374e0dd796dSAndreas Gohr * 375e0dd796dSAndreas Gohr * @return string|null 376e0dd796dSAndreas Gohr */ 377e0dd796dSAndreas Gohr public function getApcuPrefix() 378e0dd796dSAndreas Gohr { 379e0dd796dSAndreas Gohr return $this->apcuPrefix; 380e0dd796dSAndreas Gohr } 381e0dd796dSAndreas Gohr 382e0dd796dSAndreas Gohr /** 383605f8e8dSAndreas Gohr * Registers this instance as an autoloader. 384605f8e8dSAndreas Gohr * 385605f8e8dSAndreas Gohr * @param bool $prepend Whether to prepend the autoloader or not 386*d3233986SAndreas Gohr * 387*d3233986SAndreas Gohr * @return void 388605f8e8dSAndreas Gohr */ 389605f8e8dSAndreas Gohr public function register($prepend = false) 390605f8e8dSAndreas Gohr { 391605f8e8dSAndreas Gohr spl_autoload_register(array($this, 'loadClass'), true, $prepend); 3926cb05674SAndreas Gohr 3936cb05674SAndreas Gohr if (null === $this->vendorDir) { 3946cb05674SAndreas Gohr return; 3956cb05674SAndreas Gohr } 3966cb05674SAndreas Gohr 3976cb05674SAndreas Gohr if ($prepend) { 3986cb05674SAndreas Gohr self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 3996cb05674SAndreas Gohr } else { 4006cb05674SAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 4016cb05674SAndreas Gohr self::$registeredLoaders[$this->vendorDir] = $this; 4026cb05674SAndreas Gohr } 403605f8e8dSAndreas Gohr } 404605f8e8dSAndreas Gohr 405605f8e8dSAndreas Gohr /** 406605f8e8dSAndreas Gohr * Unregisters this instance as an autoloader. 407*d3233986SAndreas Gohr * 408*d3233986SAndreas Gohr * @return void 409605f8e8dSAndreas Gohr */ 410605f8e8dSAndreas Gohr public function unregister() 411605f8e8dSAndreas Gohr { 412605f8e8dSAndreas Gohr spl_autoload_unregister(array($this, 'loadClass')); 4136cb05674SAndreas Gohr 4146cb05674SAndreas Gohr if (null !== $this->vendorDir) { 4156cb05674SAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 4166cb05674SAndreas Gohr } 417605f8e8dSAndreas Gohr } 418605f8e8dSAndreas Gohr 419605f8e8dSAndreas Gohr /** 420605f8e8dSAndreas Gohr * Loads the given class or interface. 421605f8e8dSAndreas Gohr * 422605f8e8dSAndreas Gohr * @param string $class The name of the class 423*d3233986SAndreas Gohr * @return true|null True if loaded, null otherwise 424605f8e8dSAndreas Gohr */ 425605f8e8dSAndreas Gohr public function loadClass($class) 426605f8e8dSAndreas Gohr { 427605f8e8dSAndreas Gohr if ($file = $this->findFile($class)) { 428605f8e8dSAndreas Gohr includeFile($file); 429605f8e8dSAndreas Gohr 430605f8e8dSAndreas Gohr return true; 431605f8e8dSAndreas Gohr } 432*d3233986SAndreas Gohr 433*d3233986SAndreas Gohr return null; 434605f8e8dSAndreas Gohr } 435605f8e8dSAndreas Gohr 436605f8e8dSAndreas Gohr /** 437605f8e8dSAndreas Gohr * Finds the path to the file where the class is defined. 438605f8e8dSAndreas Gohr * 439605f8e8dSAndreas Gohr * @param string $class The name of the class 440605f8e8dSAndreas Gohr * 441605f8e8dSAndreas Gohr * @return string|false The path if found, false otherwise 442605f8e8dSAndreas Gohr */ 443605f8e8dSAndreas Gohr public function findFile($class) 444605f8e8dSAndreas Gohr { 445605f8e8dSAndreas Gohr // class map lookup 446605f8e8dSAndreas Gohr if (isset($this->classMap[$class])) { 447605f8e8dSAndreas Gohr return $this->classMap[$class]; 448605f8e8dSAndreas Gohr } 4497a33d2f8SNiklas Keller if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 450605f8e8dSAndreas Gohr return false; 451605f8e8dSAndreas Gohr } 452e0dd796dSAndreas Gohr if (null !== $this->apcuPrefix) { 453e0dd796dSAndreas Gohr $file = apcu_fetch($this->apcuPrefix.$class, $hit); 454e0dd796dSAndreas Gohr if ($hit) { 455e0dd796dSAndreas Gohr return $file; 456e0dd796dSAndreas Gohr } 457e0dd796dSAndreas Gohr } 458605f8e8dSAndreas Gohr 459605f8e8dSAndreas Gohr $file = $this->findFileWithExtension($class, '.php'); 460605f8e8dSAndreas Gohr 461605f8e8dSAndreas Gohr // Search for Hack files if we are running on HHVM 4627a33d2f8SNiklas Keller if (false === $file && defined('HHVM_VERSION')) { 463605f8e8dSAndreas Gohr $file = $this->findFileWithExtension($class, '.hh'); 464605f8e8dSAndreas Gohr } 465605f8e8dSAndreas Gohr 466e0dd796dSAndreas Gohr if (null !== $this->apcuPrefix) { 467e0dd796dSAndreas Gohr apcu_add($this->apcuPrefix.$class, $file); 468e0dd796dSAndreas Gohr } 469e0dd796dSAndreas Gohr 4707a33d2f8SNiklas Keller if (false === $file) { 471605f8e8dSAndreas Gohr // Remember that this class does not exist. 4727a33d2f8SNiklas Keller $this->missingClasses[$class] = true; 473605f8e8dSAndreas Gohr } 474605f8e8dSAndreas Gohr 475605f8e8dSAndreas Gohr return $file; 476605f8e8dSAndreas Gohr } 477605f8e8dSAndreas Gohr 4786cb05674SAndreas Gohr /** 4796cb05674SAndreas Gohr * Returns the currently registered loaders indexed by their corresponding vendor directories. 4806cb05674SAndreas Gohr * 4816cb05674SAndreas Gohr * @return self[] 4826cb05674SAndreas Gohr */ 4836cb05674SAndreas Gohr public static function getRegisteredLoaders() 4846cb05674SAndreas Gohr { 4856cb05674SAndreas Gohr return self::$registeredLoaders; 4866cb05674SAndreas Gohr } 4876cb05674SAndreas Gohr 488*d3233986SAndreas Gohr /** 489*d3233986SAndreas Gohr * @param string $class 490*d3233986SAndreas Gohr * @param string $ext 491*d3233986SAndreas Gohr * @return string|false 492*d3233986SAndreas Gohr */ 493605f8e8dSAndreas Gohr private function findFileWithExtension($class, $ext) 494605f8e8dSAndreas Gohr { 495605f8e8dSAndreas Gohr // PSR-4 lookup 496605f8e8dSAndreas Gohr $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 497605f8e8dSAndreas Gohr 498605f8e8dSAndreas Gohr $first = $class[0]; 499605f8e8dSAndreas Gohr if (isset($this->prefixLengthsPsr4[$first])) { 500e0dd796dSAndreas Gohr $subPath = $class; 501e0dd796dSAndreas Gohr while (false !== $lastPos = strrpos($subPath, '\\')) { 502e0dd796dSAndreas Gohr $subPath = substr($subPath, 0, $lastPos); 503e0dd796dSAndreas Gohr $search = $subPath . '\\'; 504e0dd796dSAndreas Gohr if (isset($this->prefixDirsPsr4[$search])) { 505e43cd7e1SAndreas Gohr $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 506e0dd796dSAndreas Gohr foreach ($this->prefixDirsPsr4[$search] as $dir) { 507e43cd7e1SAndreas Gohr if (file_exists($file = $dir . $pathEnd)) { 508605f8e8dSAndreas Gohr return $file; 509605f8e8dSAndreas Gohr } 510605f8e8dSAndreas Gohr } 511605f8e8dSAndreas Gohr } 512605f8e8dSAndreas Gohr } 513605f8e8dSAndreas Gohr } 514605f8e8dSAndreas Gohr 515605f8e8dSAndreas Gohr // PSR-4 fallback dirs 516605f8e8dSAndreas Gohr foreach ($this->fallbackDirsPsr4 as $dir) { 5177a33d2f8SNiklas Keller if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 518605f8e8dSAndreas Gohr return $file; 519605f8e8dSAndreas Gohr } 520605f8e8dSAndreas Gohr } 521605f8e8dSAndreas Gohr 522605f8e8dSAndreas Gohr // PSR-0 lookup 523605f8e8dSAndreas Gohr if (false !== $pos = strrpos($class, '\\')) { 524605f8e8dSAndreas Gohr // namespaced class name 525605f8e8dSAndreas Gohr $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 526605f8e8dSAndreas Gohr . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 527605f8e8dSAndreas Gohr } else { 528605f8e8dSAndreas Gohr // PEAR-like class name 529605f8e8dSAndreas Gohr $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 530605f8e8dSAndreas Gohr } 531605f8e8dSAndreas Gohr 532605f8e8dSAndreas Gohr if (isset($this->prefixesPsr0[$first])) { 533605f8e8dSAndreas Gohr foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 534605f8e8dSAndreas Gohr if (0 === strpos($class, $prefix)) { 535605f8e8dSAndreas Gohr foreach ($dirs as $dir) { 5367a33d2f8SNiklas Keller if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 537605f8e8dSAndreas Gohr return $file; 538605f8e8dSAndreas Gohr } 539605f8e8dSAndreas Gohr } 540605f8e8dSAndreas Gohr } 541605f8e8dSAndreas Gohr } 542605f8e8dSAndreas Gohr } 543605f8e8dSAndreas Gohr 544605f8e8dSAndreas Gohr // PSR-0 fallback dirs 545605f8e8dSAndreas Gohr foreach ($this->fallbackDirsPsr0 as $dir) { 5467a33d2f8SNiklas Keller if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 547605f8e8dSAndreas Gohr return $file; 548605f8e8dSAndreas Gohr } 549605f8e8dSAndreas Gohr } 550605f8e8dSAndreas Gohr 551605f8e8dSAndreas Gohr // PSR-0 include paths. 552605f8e8dSAndreas Gohr if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 553605f8e8dSAndreas Gohr return $file; 554605f8e8dSAndreas Gohr } 5557a33d2f8SNiklas Keller 5567a33d2f8SNiklas Keller return false; 557605f8e8dSAndreas Gohr } 558605f8e8dSAndreas Gohr} 559605f8e8dSAndreas Gohr 560605f8e8dSAndreas Gohr/** 561605f8e8dSAndreas Gohr * Scope isolated include. 562605f8e8dSAndreas Gohr * 563605f8e8dSAndreas Gohr * Prevents access to $this/self from included files. 564*d3233986SAndreas Gohr * 565*d3233986SAndreas Gohr * @param string $file 566*d3233986SAndreas Gohr * @return void 567*d3233986SAndreas Gohr * @private 568605f8e8dSAndreas Gohr */ 569605f8e8dSAndreas Gohrfunction includeFile($file) 570605f8e8dSAndreas Gohr{ 571605f8e8dSAndreas Gohr include $file; 572605f8e8dSAndreas Gohr} 573