137748cd8SNickeau<?php 237748cd8SNickeau 337748cd8SNickeau/* 437748cd8SNickeau * This file is part of Composer. 537748cd8SNickeau * 637748cd8SNickeau * (c) Nils Adermann <naderman@naderman.de> 737748cd8SNickeau * Jordi Boggiano <j.boggiano@seld.be> 837748cd8SNickeau * 937748cd8SNickeau * For the full copyright and license information, please view the LICENSE 1037748cd8SNickeau * file that was distributed with this source code. 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace Composer\Autoload; 1437748cd8SNickeau 1537748cd8SNickeau/** 1637748cd8SNickeau * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 1737748cd8SNickeau * 1837748cd8SNickeau * $loader = new \Composer\Autoload\ClassLoader(); 1937748cd8SNickeau * 2037748cd8SNickeau * // register classes with namespaces 2137748cd8SNickeau * $loader->add('Symfony\Component', __DIR__.'/component'); 2237748cd8SNickeau * $loader->add('Symfony', __DIR__.'/framework'); 2337748cd8SNickeau * 2437748cd8SNickeau * // activate the autoloader 2537748cd8SNickeau * $loader->register(); 2637748cd8SNickeau * 2737748cd8SNickeau * // to enable searching the include path (eg. for PEAR packages) 2837748cd8SNickeau * $loader->setUseIncludePath(true); 2937748cd8SNickeau * 3037748cd8SNickeau * In this example, if you try to use a class in the Symfony\Component 3137748cd8SNickeau * namespace or one of its children (Symfony\Component\Console for instance), 3237748cd8SNickeau * the autoloader will first look for the class under the component/ 3337748cd8SNickeau * directory, and it will then fallback to the framework/ directory if not 3437748cd8SNickeau * found before giving up. 3537748cd8SNickeau * 3637748cd8SNickeau * This class is loosely based on the Symfony UniversalClassLoader. 3737748cd8SNickeau * 3837748cd8SNickeau * @author Fabien Potencier <fabien@symfony.com> 3937748cd8SNickeau * @author Jordi Boggiano <j.boggiano@seld.be> 4037748cd8SNickeau * @see https://www.php-fig.org/psr/psr-0/ 4137748cd8SNickeau * @see https://www.php-fig.org/psr/psr-4/ 4237748cd8SNickeau */ 4337748cd8SNickeauclass ClassLoader 4437748cd8SNickeau{ 45*83c68632SNico /** @var \Closure(string):void */ 46*83c68632SNico private static $includeFile; 47*83c68632SNico 48*83c68632SNico /** @var string|null */ 4937748cd8SNickeau private $vendorDir; 5037748cd8SNickeau 5137748cd8SNickeau // PSR-4 52*83c68632SNico /** 53*83c68632SNico * @var array<string, array<string, int>> 54*83c68632SNico */ 5537748cd8SNickeau private $prefixLengthsPsr4 = array(); 56*83c68632SNico /** 57*83c68632SNico * @var array<string, list<string>> 58*83c68632SNico */ 5937748cd8SNickeau private $prefixDirsPsr4 = array(); 60*83c68632SNico /** 61*83c68632SNico * @var list<string> 62*83c68632SNico */ 6337748cd8SNickeau private $fallbackDirsPsr4 = array(); 6437748cd8SNickeau 6537748cd8SNickeau // PSR-0 66*83c68632SNico /** 67*83c68632SNico * List of PSR-0 prefixes 68*83c68632SNico * 69*83c68632SNico * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) 70*83c68632SNico * 71*83c68632SNico * @var array<string, array<string, list<string>>> 72*83c68632SNico */ 7337748cd8SNickeau private $prefixesPsr0 = array(); 74*83c68632SNico /** 75*83c68632SNico * @var list<string> 76*83c68632SNico */ 7737748cd8SNickeau private $fallbackDirsPsr0 = array(); 7837748cd8SNickeau 79*83c68632SNico /** @var bool */ 8037748cd8SNickeau private $useIncludePath = false; 81*83c68632SNico 82*83c68632SNico /** 83*83c68632SNico * @var array<string, string> 84*83c68632SNico */ 8537748cd8SNickeau private $classMap = array(); 86*83c68632SNico 87*83c68632SNico /** @var bool */ 8837748cd8SNickeau private $classMapAuthoritative = false; 89*83c68632SNico 90*83c68632SNico /** 91*83c68632SNico * @var array<string, bool> 92*83c68632SNico */ 9337748cd8SNickeau private $missingClasses = array(); 94*83c68632SNico 95*83c68632SNico /** @var string|null */ 9637748cd8SNickeau private $apcuPrefix; 9737748cd8SNickeau 98*83c68632SNico /** 99*83c68632SNico * @var array<string, self> 100*83c68632SNico */ 10137748cd8SNickeau private static $registeredLoaders = array(); 10237748cd8SNickeau 103*83c68632SNico /** 104*83c68632SNico * @param string|null $vendorDir 105*83c68632SNico */ 10637748cd8SNickeau public function __construct($vendorDir = null) 10737748cd8SNickeau { 10837748cd8SNickeau $this->vendorDir = $vendorDir; 109*83c68632SNico self::initializeIncludeClosure(); 11037748cd8SNickeau } 11137748cd8SNickeau 112*83c68632SNico /** 113*83c68632SNico * @return array<string, list<string>> 114*83c68632SNico */ 11537748cd8SNickeau public function getPrefixes() 11637748cd8SNickeau { 11737748cd8SNickeau if (!empty($this->prefixesPsr0)) { 11837748cd8SNickeau return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 11937748cd8SNickeau } 12037748cd8SNickeau 12137748cd8SNickeau return array(); 12237748cd8SNickeau } 12337748cd8SNickeau 124*83c68632SNico /** 125*83c68632SNico * @return array<string, list<string>> 126*83c68632SNico */ 12737748cd8SNickeau public function getPrefixesPsr4() 12837748cd8SNickeau { 12937748cd8SNickeau return $this->prefixDirsPsr4; 13037748cd8SNickeau } 13137748cd8SNickeau 132*83c68632SNico /** 133*83c68632SNico * @return list<string> 134*83c68632SNico */ 13537748cd8SNickeau public function getFallbackDirs() 13637748cd8SNickeau { 13737748cd8SNickeau return $this->fallbackDirsPsr0; 13837748cd8SNickeau } 13937748cd8SNickeau 140*83c68632SNico /** 141*83c68632SNico * @return list<string> 142*83c68632SNico */ 14337748cd8SNickeau public function getFallbackDirsPsr4() 14437748cd8SNickeau { 14537748cd8SNickeau return $this->fallbackDirsPsr4; 14637748cd8SNickeau } 14737748cd8SNickeau 148*83c68632SNico /** 149*83c68632SNico * @return array<string, string> Array of classname => path 150*83c68632SNico */ 15137748cd8SNickeau public function getClassMap() 15237748cd8SNickeau { 15337748cd8SNickeau return $this->classMap; 15437748cd8SNickeau } 15537748cd8SNickeau 15637748cd8SNickeau /** 157*83c68632SNico * @param array<string, string> $classMap Class to filename map 158*83c68632SNico * 159*83c68632SNico * @return void 16037748cd8SNickeau */ 16137748cd8SNickeau public function addClassMap(array $classMap) 16237748cd8SNickeau { 16337748cd8SNickeau if ($this->classMap) { 16437748cd8SNickeau $this->classMap = array_merge($this->classMap, $classMap); 16537748cd8SNickeau } else { 16637748cd8SNickeau $this->classMap = $classMap; 16737748cd8SNickeau } 16837748cd8SNickeau } 16937748cd8SNickeau 17037748cd8SNickeau /** 17137748cd8SNickeau * Registers a set of PSR-0 directories for a given prefix, either 17237748cd8SNickeau * appending or prepending to the ones previously set for this prefix. 17337748cd8SNickeau * 17437748cd8SNickeau * @param string $prefix The prefix 175*83c68632SNico * @param list<string>|string $paths The PSR-0 root directories 17637748cd8SNickeau * @param bool $prepend Whether to prepend the directories 177*83c68632SNico * 178*83c68632SNico * @return void 17937748cd8SNickeau */ 18037748cd8SNickeau public function add($prefix, $paths, $prepend = false) 18137748cd8SNickeau { 182*83c68632SNico $paths = (array) $paths; 18337748cd8SNickeau if (!$prefix) { 18437748cd8SNickeau if ($prepend) { 18537748cd8SNickeau $this->fallbackDirsPsr0 = array_merge( 186*83c68632SNico $paths, 18737748cd8SNickeau $this->fallbackDirsPsr0 18837748cd8SNickeau ); 18937748cd8SNickeau } else { 19037748cd8SNickeau $this->fallbackDirsPsr0 = array_merge( 19137748cd8SNickeau $this->fallbackDirsPsr0, 192*83c68632SNico $paths 19337748cd8SNickeau ); 19437748cd8SNickeau } 19537748cd8SNickeau 19637748cd8SNickeau return; 19737748cd8SNickeau } 19837748cd8SNickeau 19937748cd8SNickeau $first = $prefix[0]; 20037748cd8SNickeau if (!isset($this->prefixesPsr0[$first][$prefix])) { 201*83c68632SNico $this->prefixesPsr0[$first][$prefix] = $paths; 20237748cd8SNickeau 20337748cd8SNickeau return; 20437748cd8SNickeau } 20537748cd8SNickeau if ($prepend) { 20637748cd8SNickeau $this->prefixesPsr0[$first][$prefix] = array_merge( 207*83c68632SNico $paths, 20837748cd8SNickeau $this->prefixesPsr0[$first][$prefix] 20937748cd8SNickeau ); 21037748cd8SNickeau } else { 21137748cd8SNickeau $this->prefixesPsr0[$first][$prefix] = array_merge( 21237748cd8SNickeau $this->prefixesPsr0[$first][$prefix], 213*83c68632SNico $paths 21437748cd8SNickeau ); 21537748cd8SNickeau } 21637748cd8SNickeau } 21737748cd8SNickeau 21837748cd8SNickeau /** 21937748cd8SNickeau * Registers a set of PSR-4 directories for a given namespace, either 22037748cd8SNickeau * appending or prepending to the ones previously set for this namespace. 22137748cd8SNickeau * 22237748cd8SNickeau * @param string $prefix The prefix/namespace, with trailing '\\' 223*83c68632SNico * @param list<string>|string $paths The PSR-4 base directories 22437748cd8SNickeau * @param bool $prepend Whether to prepend the directories 22537748cd8SNickeau * 22637748cd8SNickeau * @throws \InvalidArgumentException 227*83c68632SNico * 228*83c68632SNico * @return void 22937748cd8SNickeau */ 23037748cd8SNickeau public function addPsr4($prefix, $paths, $prepend = false) 23137748cd8SNickeau { 232*83c68632SNico $paths = (array) $paths; 23337748cd8SNickeau if (!$prefix) { 23437748cd8SNickeau // Register directories for the root namespace. 23537748cd8SNickeau if ($prepend) { 23637748cd8SNickeau $this->fallbackDirsPsr4 = array_merge( 237*83c68632SNico $paths, 23837748cd8SNickeau $this->fallbackDirsPsr4 23937748cd8SNickeau ); 24037748cd8SNickeau } else { 24137748cd8SNickeau $this->fallbackDirsPsr4 = array_merge( 24237748cd8SNickeau $this->fallbackDirsPsr4, 243*83c68632SNico $paths 24437748cd8SNickeau ); 24537748cd8SNickeau } 24637748cd8SNickeau } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 24737748cd8SNickeau // Register directories for a new namespace. 24837748cd8SNickeau $length = strlen($prefix); 24937748cd8SNickeau if ('\\' !== $prefix[$length - 1]) { 25037748cd8SNickeau throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 25137748cd8SNickeau } 25237748cd8SNickeau $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 253*83c68632SNico $this->prefixDirsPsr4[$prefix] = $paths; 25437748cd8SNickeau } elseif ($prepend) { 25537748cd8SNickeau // Prepend directories for an already registered namespace. 25637748cd8SNickeau $this->prefixDirsPsr4[$prefix] = array_merge( 257*83c68632SNico $paths, 25837748cd8SNickeau $this->prefixDirsPsr4[$prefix] 25937748cd8SNickeau ); 26037748cd8SNickeau } else { 26137748cd8SNickeau // Append directories for an already registered namespace. 26237748cd8SNickeau $this->prefixDirsPsr4[$prefix] = array_merge( 26337748cd8SNickeau $this->prefixDirsPsr4[$prefix], 264*83c68632SNico $paths 26537748cd8SNickeau ); 26637748cd8SNickeau } 26737748cd8SNickeau } 26837748cd8SNickeau 26937748cd8SNickeau /** 27037748cd8SNickeau * Registers a set of PSR-0 directories for a given prefix, 27137748cd8SNickeau * replacing any others previously set for this prefix. 27237748cd8SNickeau * 27337748cd8SNickeau * @param string $prefix The prefix 274*83c68632SNico * @param list<string>|string $paths The PSR-0 base directories 275*83c68632SNico * 276*83c68632SNico * @return void 27737748cd8SNickeau */ 27837748cd8SNickeau public function set($prefix, $paths) 27937748cd8SNickeau { 28037748cd8SNickeau if (!$prefix) { 28137748cd8SNickeau $this->fallbackDirsPsr0 = (array) $paths; 28237748cd8SNickeau } else { 28337748cd8SNickeau $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 28437748cd8SNickeau } 28537748cd8SNickeau } 28637748cd8SNickeau 28737748cd8SNickeau /** 28837748cd8SNickeau * Registers a set of PSR-4 directories for a given namespace, 28937748cd8SNickeau * replacing any others previously set for this namespace. 29037748cd8SNickeau * 29137748cd8SNickeau * @param string $prefix The prefix/namespace, with trailing '\\' 292*83c68632SNico * @param list<string>|string $paths The PSR-4 base directories 29337748cd8SNickeau * 29437748cd8SNickeau * @throws \InvalidArgumentException 295*83c68632SNico * 296*83c68632SNico * @return void 29737748cd8SNickeau */ 29837748cd8SNickeau public function setPsr4($prefix, $paths) 29937748cd8SNickeau { 30037748cd8SNickeau if (!$prefix) { 30137748cd8SNickeau $this->fallbackDirsPsr4 = (array) $paths; 30237748cd8SNickeau } else { 30337748cd8SNickeau $length = strlen($prefix); 30437748cd8SNickeau if ('\\' !== $prefix[$length - 1]) { 30537748cd8SNickeau throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 30637748cd8SNickeau } 30737748cd8SNickeau $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 30837748cd8SNickeau $this->prefixDirsPsr4[$prefix] = (array) $paths; 30937748cd8SNickeau } 31037748cd8SNickeau } 31137748cd8SNickeau 31237748cd8SNickeau /** 31337748cd8SNickeau * Turns on searching the include path for class files. 31437748cd8SNickeau * 31537748cd8SNickeau * @param bool $useIncludePath 316*83c68632SNico * 317*83c68632SNico * @return void 31837748cd8SNickeau */ 31937748cd8SNickeau public function setUseIncludePath($useIncludePath) 32037748cd8SNickeau { 32137748cd8SNickeau $this->useIncludePath = $useIncludePath; 32237748cd8SNickeau } 32337748cd8SNickeau 32437748cd8SNickeau /** 32537748cd8SNickeau * Can be used to check if the autoloader uses the include path to check 32637748cd8SNickeau * for classes. 32737748cd8SNickeau * 32837748cd8SNickeau * @return bool 32937748cd8SNickeau */ 33037748cd8SNickeau public function getUseIncludePath() 33137748cd8SNickeau { 33237748cd8SNickeau return $this->useIncludePath; 33337748cd8SNickeau } 33437748cd8SNickeau 33537748cd8SNickeau /** 33637748cd8SNickeau * Turns off searching the prefix and fallback directories for classes 33737748cd8SNickeau * that have not been registered with the class map. 33837748cd8SNickeau * 33937748cd8SNickeau * @param bool $classMapAuthoritative 340*83c68632SNico * 341*83c68632SNico * @return void 34237748cd8SNickeau */ 34337748cd8SNickeau public function setClassMapAuthoritative($classMapAuthoritative) 34437748cd8SNickeau { 34537748cd8SNickeau $this->classMapAuthoritative = $classMapAuthoritative; 34637748cd8SNickeau } 34737748cd8SNickeau 34837748cd8SNickeau /** 34937748cd8SNickeau * Should class lookup fail if not found in the current class map? 35037748cd8SNickeau * 35137748cd8SNickeau * @return bool 35237748cd8SNickeau */ 35337748cd8SNickeau public function isClassMapAuthoritative() 35437748cd8SNickeau { 35537748cd8SNickeau return $this->classMapAuthoritative; 35637748cd8SNickeau } 35737748cd8SNickeau 35837748cd8SNickeau /** 35937748cd8SNickeau * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 36037748cd8SNickeau * 36137748cd8SNickeau * @param string|null $apcuPrefix 362*83c68632SNico * 363*83c68632SNico * @return void 36437748cd8SNickeau */ 36537748cd8SNickeau public function setApcuPrefix($apcuPrefix) 36637748cd8SNickeau { 36737748cd8SNickeau $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 36837748cd8SNickeau } 36937748cd8SNickeau 37037748cd8SNickeau /** 37137748cd8SNickeau * The APCu prefix in use, or null if APCu caching is not enabled. 37237748cd8SNickeau * 37337748cd8SNickeau * @return string|null 37437748cd8SNickeau */ 37537748cd8SNickeau public function getApcuPrefix() 37637748cd8SNickeau { 37737748cd8SNickeau return $this->apcuPrefix; 37837748cd8SNickeau } 37937748cd8SNickeau 38037748cd8SNickeau /** 38137748cd8SNickeau * Registers this instance as an autoloader. 38237748cd8SNickeau * 38337748cd8SNickeau * @param bool $prepend Whether to prepend the autoloader or not 384*83c68632SNico * 385*83c68632SNico * @return void 38637748cd8SNickeau */ 38737748cd8SNickeau public function register($prepend = false) 38837748cd8SNickeau { 38937748cd8SNickeau spl_autoload_register(array($this, 'loadClass'), true, $prepend); 39037748cd8SNickeau 39137748cd8SNickeau if (null === $this->vendorDir) { 39237748cd8SNickeau return; 39337748cd8SNickeau } 39437748cd8SNickeau 39537748cd8SNickeau if ($prepend) { 39637748cd8SNickeau self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 39737748cd8SNickeau } else { 39837748cd8SNickeau unset(self::$registeredLoaders[$this->vendorDir]); 39937748cd8SNickeau self::$registeredLoaders[$this->vendorDir] = $this; 40037748cd8SNickeau } 40137748cd8SNickeau } 40237748cd8SNickeau 40337748cd8SNickeau /** 40437748cd8SNickeau * Unregisters this instance as an autoloader. 405*83c68632SNico * 406*83c68632SNico * @return void 40737748cd8SNickeau */ 40837748cd8SNickeau public function unregister() 40937748cd8SNickeau { 41037748cd8SNickeau spl_autoload_unregister(array($this, 'loadClass')); 41137748cd8SNickeau 41237748cd8SNickeau if (null !== $this->vendorDir) { 41337748cd8SNickeau unset(self::$registeredLoaders[$this->vendorDir]); 41437748cd8SNickeau } 41537748cd8SNickeau } 41637748cd8SNickeau 41737748cd8SNickeau /** 41837748cd8SNickeau * Loads the given class or interface. 41937748cd8SNickeau * 42037748cd8SNickeau * @param string $class The name of the class 421*83c68632SNico * @return true|null True if loaded, null otherwise 42237748cd8SNickeau */ 42337748cd8SNickeau public function loadClass($class) 42437748cd8SNickeau { 42537748cd8SNickeau if ($file = $this->findFile($class)) { 426*83c68632SNico $includeFile = self::$includeFile; 427*83c68632SNico $includeFile($file); 42837748cd8SNickeau 42937748cd8SNickeau return true; 43037748cd8SNickeau } 431*83c68632SNico 432*83c68632SNico return null; 43337748cd8SNickeau } 43437748cd8SNickeau 43537748cd8SNickeau /** 43637748cd8SNickeau * Finds the path to the file where the class is defined. 43737748cd8SNickeau * 43837748cd8SNickeau * @param string $class The name of the class 43937748cd8SNickeau * 44037748cd8SNickeau * @return string|false The path if found, false otherwise 44137748cd8SNickeau */ 44237748cd8SNickeau public function findFile($class) 44337748cd8SNickeau { 44437748cd8SNickeau // class map lookup 44537748cd8SNickeau if (isset($this->classMap[$class])) { 44637748cd8SNickeau return $this->classMap[$class]; 44737748cd8SNickeau } 44837748cd8SNickeau if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 44937748cd8SNickeau return false; 45037748cd8SNickeau } 45137748cd8SNickeau if (null !== $this->apcuPrefix) { 45237748cd8SNickeau $file = apcu_fetch($this->apcuPrefix.$class, $hit); 45337748cd8SNickeau if ($hit) { 45437748cd8SNickeau return $file; 45537748cd8SNickeau } 45637748cd8SNickeau } 45737748cd8SNickeau 45837748cd8SNickeau $file = $this->findFileWithExtension($class, '.php'); 45937748cd8SNickeau 46037748cd8SNickeau // Search for Hack files if we are running on HHVM 46137748cd8SNickeau if (false === $file && defined('HHVM_VERSION')) { 46237748cd8SNickeau $file = $this->findFileWithExtension($class, '.hh'); 46337748cd8SNickeau } 46437748cd8SNickeau 46537748cd8SNickeau if (null !== $this->apcuPrefix) { 46637748cd8SNickeau apcu_add($this->apcuPrefix.$class, $file); 46737748cd8SNickeau } 46837748cd8SNickeau 46937748cd8SNickeau if (false === $file) { 47037748cd8SNickeau // Remember that this class does not exist. 47137748cd8SNickeau $this->missingClasses[$class] = true; 47237748cd8SNickeau } 47337748cd8SNickeau 47437748cd8SNickeau return $file; 47537748cd8SNickeau } 47637748cd8SNickeau 47737748cd8SNickeau /** 478*83c68632SNico * Returns the currently registered loaders keyed by their corresponding vendor directories. 47937748cd8SNickeau * 480*83c68632SNico * @return array<string, self> 48137748cd8SNickeau */ 48237748cd8SNickeau public static function getRegisteredLoaders() 48337748cd8SNickeau { 48437748cd8SNickeau return self::$registeredLoaders; 48537748cd8SNickeau } 48637748cd8SNickeau 487*83c68632SNico /** 488*83c68632SNico * @param string $class 489*83c68632SNico * @param string $ext 490*83c68632SNico * @return string|false 491*83c68632SNico */ 49237748cd8SNickeau private function findFileWithExtension($class, $ext) 49337748cd8SNickeau { 49437748cd8SNickeau // PSR-4 lookup 49537748cd8SNickeau $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 49637748cd8SNickeau 49737748cd8SNickeau $first = $class[0]; 49837748cd8SNickeau if (isset($this->prefixLengthsPsr4[$first])) { 49937748cd8SNickeau $subPath = $class; 50037748cd8SNickeau while (false !== $lastPos = strrpos($subPath, '\\')) { 50137748cd8SNickeau $subPath = substr($subPath, 0, $lastPos); 50237748cd8SNickeau $search = $subPath . '\\'; 50337748cd8SNickeau if (isset($this->prefixDirsPsr4[$search])) { 50437748cd8SNickeau $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 50537748cd8SNickeau foreach ($this->prefixDirsPsr4[$search] as $dir) { 50637748cd8SNickeau if (file_exists($file = $dir . $pathEnd)) { 50737748cd8SNickeau return $file; 50837748cd8SNickeau } 50937748cd8SNickeau } 51037748cd8SNickeau } 51137748cd8SNickeau } 51237748cd8SNickeau } 51337748cd8SNickeau 51437748cd8SNickeau // PSR-4 fallback dirs 51537748cd8SNickeau foreach ($this->fallbackDirsPsr4 as $dir) { 51637748cd8SNickeau if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 51737748cd8SNickeau return $file; 51837748cd8SNickeau } 51937748cd8SNickeau } 52037748cd8SNickeau 52137748cd8SNickeau // PSR-0 lookup 52237748cd8SNickeau if (false !== $pos = strrpos($class, '\\')) { 52337748cd8SNickeau // namespaced class name 52437748cd8SNickeau $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 52537748cd8SNickeau . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 52637748cd8SNickeau } else { 52737748cd8SNickeau // PEAR-like class name 52837748cd8SNickeau $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 52937748cd8SNickeau } 53037748cd8SNickeau 53137748cd8SNickeau if (isset($this->prefixesPsr0[$first])) { 53237748cd8SNickeau foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 53337748cd8SNickeau if (0 === strpos($class, $prefix)) { 53437748cd8SNickeau foreach ($dirs as $dir) { 53537748cd8SNickeau if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 53637748cd8SNickeau return $file; 53737748cd8SNickeau } 53837748cd8SNickeau } 53937748cd8SNickeau } 54037748cd8SNickeau } 54137748cd8SNickeau } 54237748cd8SNickeau 54337748cd8SNickeau // PSR-0 fallback dirs 54437748cd8SNickeau foreach ($this->fallbackDirsPsr0 as $dir) { 54537748cd8SNickeau if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 54637748cd8SNickeau return $file; 54737748cd8SNickeau } 54837748cd8SNickeau } 54937748cd8SNickeau 55037748cd8SNickeau // PSR-0 include paths. 55137748cd8SNickeau if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 55237748cd8SNickeau return $file; 55337748cd8SNickeau } 55437748cd8SNickeau 55537748cd8SNickeau return false; 55637748cd8SNickeau } 557*83c68632SNico 558*83c68632SNico /** 559*83c68632SNico * @return void 560*83c68632SNico */ 561*83c68632SNico private static function initializeIncludeClosure() 562*83c68632SNico { 563*83c68632SNico if (self::$includeFile !== null) { 564*83c68632SNico return; 56537748cd8SNickeau } 56637748cd8SNickeau 56737748cd8SNickeau /** 56837748cd8SNickeau * Scope isolated include. 56937748cd8SNickeau * 57037748cd8SNickeau * Prevents access to $this/self from included files. 571*83c68632SNico * 572*83c68632SNico * @param string $file 573*83c68632SNico * @return void 57437748cd8SNickeau */ 575*83c68632SNico self::$includeFile = \Closure::bind(static function($file) { 57637748cd8SNickeau include $file; 577*83c68632SNico }, null, null); 578*83c68632SNico } 57937748cd8SNickeau} 580