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