10b3fd2d3SAndreas Gohr<?php 20b3fd2d3SAndreas Gohr 30b3fd2d3SAndreas Gohr/* 40b3fd2d3SAndreas Gohr * This file is part of Composer. 50b3fd2d3SAndreas Gohr * 60b3fd2d3SAndreas Gohr * (c) Nils Adermann <naderman@naderman.de> 70b3fd2d3SAndreas Gohr * Jordi Boggiano <j.boggiano@seld.be> 80b3fd2d3SAndreas Gohr * 90b3fd2d3SAndreas Gohr * For the full copyright and license information, please view the LICENSE 100b3fd2d3SAndreas Gohr * file that was distributed with this source code. 110b3fd2d3SAndreas Gohr */ 120b3fd2d3SAndreas Gohr 130b3fd2d3SAndreas Gohrnamespace Composer\Autoload; 140b3fd2d3SAndreas Gohr 150b3fd2d3SAndreas Gohr/** 160b3fd2d3SAndreas Gohr * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 170b3fd2d3SAndreas Gohr * 180b3fd2d3SAndreas Gohr * $loader = new \Composer\Autoload\ClassLoader(); 190b3fd2d3SAndreas Gohr * 200b3fd2d3SAndreas Gohr * // register classes with namespaces 210b3fd2d3SAndreas Gohr * $loader->add('Symfony\Component', __DIR__.'/component'); 220b3fd2d3SAndreas Gohr * $loader->add('Symfony', __DIR__.'/framework'); 230b3fd2d3SAndreas Gohr * 240b3fd2d3SAndreas Gohr * // activate the autoloader 250b3fd2d3SAndreas Gohr * $loader->register(); 260b3fd2d3SAndreas Gohr * 270b3fd2d3SAndreas Gohr * // to enable searching the include path (eg. for PEAR packages) 280b3fd2d3SAndreas Gohr * $loader->setUseIncludePath(true); 290b3fd2d3SAndreas Gohr * 300b3fd2d3SAndreas Gohr * In this example, if you try to use a class in the Symfony\Component 310b3fd2d3SAndreas Gohr * namespace or one of its children (Symfony\Component\Console for instance), 320b3fd2d3SAndreas Gohr * the autoloader will first look for the class under the component/ 330b3fd2d3SAndreas Gohr * directory, and it will then fallback to the framework/ directory if not 340b3fd2d3SAndreas Gohr * found before giving up. 350b3fd2d3SAndreas Gohr * 360b3fd2d3SAndreas Gohr * This class is loosely based on the Symfony UniversalClassLoader. 370b3fd2d3SAndreas Gohr * 380b3fd2d3SAndreas Gohr * @author Fabien Potencier <fabien@symfony.com> 390b3fd2d3SAndreas Gohr * @author Jordi Boggiano <j.boggiano@seld.be> 40*fd0855ecSAndreas Gohr * @see https://www.php-fig.org/psr/psr-0/ 41*fd0855ecSAndreas Gohr * @see https://www.php-fig.org/psr/psr-4/ 420b3fd2d3SAndreas Gohr */ 430b3fd2d3SAndreas Gohrclass ClassLoader 440b3fd2d3SAndreas Gohr{ 45*fd0855ecSAndreas Gohr private $vendorDir; 46*fd0855ecSAndreas Gohr 470b3fd2d3SAndreas Gohr // PSR-4 480b3fd2d3SAndreas Gohr private $prefixLengthsPsr4 = array(); 490b3fd2d3SAndreas Gohr private $prefixDirsPsr4 = array(); 500b3fd2d3SAndreas Gohr private $fallbackDirsPsr4 = array(); 510b3fd2d3SAndreas Gohr 520b3fd2d3SAndreas Gohr // PSR-0 530b3fd2d3SAndreas Gohr private $prefixesPsr0 = array(); 540b3fd2d3SAndreas Gohr private $fallbackDirsPsr0 = array(); 550b3fd2d3SAndreas Gohr 560b3fd2d3SAndreas Gohr private $useIncludePath = false; 570b3fd2d3SAndreas Gohr private $classMap = array(); 580b3fd2d3SAndreas Gohr private $classMapAuthoritative = false; 590b3fd2d3SAndreas Gohr private $missingClasses = array(); 600b3fd2d3SAndreas Gohr private $apcuPrefix; 610b3fd2d3SAndreas Gohr 62*fd0855ecSAndreas Gohr private static $registeredLoaders = array(); 63*fd0855ecSAndreas Gohr 64*fd0855ecSAndreas Gohr public function __construct($vendorDir = null) 65*fd0855ecSAndreas Gohr { 66*fd0855ecSAndreas Gohr $this->vendorDir = $vendorDir; 67*fd0855ecSAndreas Gohr } 68*fd0855ecSAndreas Gohr 690b3fd2d3SAndreas Gohr public function getPrefixes() 700b3fd2d3SAndreas Gohr { 710b3fd2d3SAndreas Gohr if (!empty($this->prefixesPsr0)) { 72*fd0855ecSAndreas Gohr return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 730b3fd2d3SAndreas Gohr } 740b3fd2d3SAndreas Gohr 750b3fd2d3SAndreas Gohr return array(); 760b3fd2d3SAndreas Gohr } 770b3fd2d3SAndreas Gohr 780b3fd2d3SAndreas Gohr public function getPrefixesPsr4() 790b3fd2d3SAndreas Gohr { 800b3fd2d3SAndreas Gohr return $this->prefixDirsPsr4; 810b3fd2d3SAndreas Gohr } 820b3fd2d3SAndreas Gohr 830b3fd2d3SAndreas Gohr public function getFallbackDirs() 840b3fd2d3SAndreas Gohr { 850b3fd2d3SAndreas Gohr return $this->fallbackDirsPsr0; 860b3fd2d3SAndreas Gohr } 870b3fd2d3SAndreas Gohr 880b3fd2d3SAndreas Gohr public function getFallbackDirsPsr4() 890b3fd2d3SAndreas Gohr { 900b3fd2d3SAndreas Gohr return $this->fallbackDirsPsr4; 910b3fd2d3SAndreas Gohr } 920b3fd2d3SAndreas Gohr 930b3fd2d3SAndreas Gohr public function getClassMap() 940b3fd2d3SAndreas Gohr { 950b3fd2d3SAndreas Gohr return $this->classMap; 960b3fd2d3SAndreas Gohr } 970b3fd2d3SAndreas Gohr 980b3fd2d3SAndreas Gohr /** 990b3fd2d3SAndreas Gohr * @param array $classMap Class to filename map 1000b3fd2d3SAndreas Gohr */ 1010b3fd2d3SAndreas Gohr public function addClassMap(array $classMap) 1020b3fd2d3SAndreas Gohr { 1030b3fd2d3SAndreas Gohr if ($this->classMap) { 1040b3fd2d3SAndreas Gohr $this->classMap = array_merge($this->classMap, $classMap); 1050b3fd2d3SAndreas Gohr } else { 1060b3fd2d3SAndreas Gohr $this->classMap = $classMap; 1070b3fd2d3SAndreas Gohr } 1080b3fd2d3SAndreas Gohr } 1090b3fd2d3SAndreas Gohr 1100b3fd2d3SAndreas Gohr /** 1110b3fd2d3SAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, either 1120b3fd2d3SAndreas Gohr * appending or prepending to the ones previously set for this prefix. 1130b3fd2d3SAndreas Gohr * 1140b3fd2d3SAndreas Gohr * @param string $prefix The prefix 1150b3fd2d3SAndreas Gohr * @param array|string $paths The PSR-0 root directories 1160b3fd2d3SAndreas Gohr * @param bool $prepend Whether to prepend the directories 1170b3fd2d3SAndreas Gohr */ 1180b3fd2d3SAndreas Gohr public function add($prefix, $paths, $prepend = false) 1190b3fd2d3SAndreas Gohr { 1200b3fd2d3SAndreas Gohr if (!$prefix) { 1210b3fd2d3SAndreas Gohr if ($prepend) { 1220b3fd2d3SAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 1230b3fd2d3SAndreas Gohr (array) $paths, 1240b3fd2d3SAndreas Gohr $this->fallbackDirsPsr0 1250b3fd2d3SAndreas Gohr ); 1260b3fd2d3SAndreas Gohr } else { 1270b3fd2d3SAndreas Gohr $this->fallbackDirsPsr0 = array_merge( 1280b3fd2d3SAndreas Gohr $this->fallbackDirsPsr0, 1290b3fd2d3SAndreas Gohr (array) $paths 1300b3fd2d3SAndreas Gohr ); 1310b3fd2d3SAndreas Gohr } 1320b3fd2d3SAndreas Gohr 1330b3fd2d3SAndreas Gohr return; 1340b3fd2d3SAndreas Gohr } 1350b3fd2d3SAndreas Gohr 1360b3fd2d3SAndreas Gohr $first = $prefix[0]; 1370b3fd2d3SAndreas Gohr if (!isset($this->prefixesPsr0[$first][$prefix])) { 1380b3fd2d3SAndreas Gohr $this->prefixesPsr0[$first][$prefix] = (array) $paths; 1390b3fd2d3SAndreas Gohr 1400b3fd2d3SAndreas Gohr return; 1410b3fd2d3SAndreas Gohr } 1420b3fd2d3SAndreas Gohr if ($prepend) { 1430b3fd2d3SAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 1440b3fd2d3SAndreas Gohr (array) $paths, 1450b3fd2d3SAndreas Gohr $this->prefixesPsr0[$first][$prefix] 1460b3fd2d3SAndreas Gohr ); 1470b3fd2d3SAndreas Gohr } else { 1480b3fd2d3SAndreas Gohr $this->prefixesPsr0[$first][$prefix] = array_merge( 1490b3fd2d3SAndreas Gohr $this->prefixesPsr0[$first][$prefix], 1500b3fd2d3SAndreas Gohr (array) $paths 1510b3fd2d3SAndreas Gohr ); 1520b3fd2d3SAndreas Gohr } 1530b3fd2d3SAndreas Gohr } 1540b3fd2d3SAndreas Gohr 1550b3fd2d3SAndreas Gohr /** 1560b3fd2d3SAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, either 1570b3fd2d3SAndreas Gohr * appending or prepending to the ones previously set for this namespace. 1580b3fd2d3SAndreas Gohr * 1590b3fd2d3SAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 1600b3fd2d3SAndreas Gohr * @param array|string $paths The PSR-4 base directories 1610b3fd2d3SAndreas Gohr * @param bool $prepend Whether to prepend the directories 1620b3fd2d3SAndreas Gohr * 1630b3fd2d3SAndreas Gohr * @throws \InvalidArgumentException 1640b3fd2d3SAndreas Gohr */ 1650b3fd2d3SAndreas Gohr public function addPsr4($prefix, $paths, $prepend = false) 1660b3fd2d3SAndreas Gohr { 1670b3fd2d3SAndreas Gohr if (!$prefix) { 1680b3fd2d3SAndreas Gohr // Register directories for the root namespace. 1690b3fd2d3SAndreas Gohr if ($prepend) { 1700b3fd2d3SAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 1710b3fd2d3SAndreas Gohr (array) $paths, 1720b3fd2d3SAndreas Gohr $this->fallbackDirsPsr4 1730b3fd2d3SAndreas Gohr ); 1740b3fd2d3SAndreas Gohr } else { 1750b3fd2d3SAndreas Gohr $this->fallbackDirsPsr4 = array_merge( 1760b3fd2d3SAndreas Gohr $this->fallbackDirsPsr4, 1770b3fd2d3SAndreas Gohr (array) $paths 1780b3fd2d3SAndreas Gohr ); 1790b3fd2d3SAndreas Gohr } 1800b3fd2d3SAndreas Gohr } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 1810b3fd2d3SAndreas Gohr // Register directories for a new namespace. 1820b3fd2d3SAndreas Gohr $length = strlen($prefix); 1830b3fd2d3SAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 1840b3fd2d3SAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 1850b3fd2d3SAndreas Gohr } 1860b3fd2d3SAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 1870b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix] = (array) $paths; 1880b3fd2d3SAndreas Gohr } elseif ($prepend) { 1890b3fd2d3SAndreas Gohr // Prepend directories for an already registered namespace. 1900b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 1910b3fd2d3SAndreas Gohr (array) $paths, 1920b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix] 1930b3fd2d3SAndreas Gohr ); 1940b3fd2d3SAndreas Gohr } else { 1950b3fd2d3SAndreas Gohr // Append directories for an already registered namespace. 1960b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix] = array_merge( 1970b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix], 1980b3fd2d3SAndreas Gohr (array) $paths 1990b3fd2d3SAndreas Gohr ); 2000b3fd2d3SAndreas Gohr } 2010b3fd2d3SAndreas Gohr } 2020b3fd2d3SAndreas Gohr 2030b3fd2d3SAndreas Gohr /** 2040b3fd2d3SAndreas Gohr * Registers a set of PSR-0 directories for a given prefix, 2050b3fd2d3SAndreas Gohr * replacing any others previously set for this prefix. 2060b3fd2d3SAndreas Gohr * 2070b3fd2d3SAndreas Gohr * @param string $prefix The prefix 2080b3fd2d3SAndreas Gohr * @param array|string $paths The PSR-0 base directories 2090b3fd2d3SAndreas Gohr */ 2100b3fd2d3SAndreas Gohr public function set($prefix, $paths) 2110b3fd2d3SAndreas Gohr { 2120b3fd2d3SAndreas Gohr if (!$prefix) { 2130b3fd2d3SAndreas Gohr $this->fallbackDirsPsr0 = (array) $paths; 2140b3fd2d3SAndreas Gohr } else { 2150b3fd2d3SAndreas Gohr $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 2160b3fd2d3SAndreas Gohr } 2170b3fd2d3SAndreas Gohr } 2180b3fd2d3SAndreas Gohr 2190b3fd2d3SAndreas Gohr /** 2200b3fd2d3SAndreas Gohr * Registers a set of PSR-4 directories for a given namespace, 2210b3fd2d3SAndreas Gohr * replacing any others previously set for this namespace. 2220b3fd2d3SAndreas Gohr * 2230b3fd2d3SAndreas Gohr * @param string $prefix The prefix/namespace, with trailing '\\' 2240b3fd2d3SAndreas Gohr * @param array|string $paths The PSR-4 base directories 2250b3fd2d3SAndreas Gohr * 2260b3fd2d3SAndreas Gohr * @throws \InvalidArgumentException 2270b3fd2d3SAndreas Gohr */ 2280b3fd2d3SAndreas Gohr public function setPsr4($prefix, $paths) 2290b3fd2d3SAndreas Gohr { 2300b3fd2d3SAndreas Gohr if (!$prefix) { 2310b3fd2d3SAndreas Gohr $this->fallbackDirsPsr4 = (array) $paths; 2320b3fd2d3SAndreas Gohr } else { 2330b3fd2d3SAndreas Gohr $length = strlen($prefix); 2340b3fd2d3SAndreas Gohr if ('\\' !== $prefix[$length - 1]) { 2350b3fd2d3SAndreas Gohr throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 2360b3fd2d3SAndreas Gohr } 2370b3fd2d3SAndreas Gohr $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 2380b3fd2d3SAndreas Gohr $this->prefixDirsPsr4[$prefix] = (array) $paths; 2390b3fd2d3SAndreas Gohr } 2400b3fd2d3SAndreas Gohr } 2410b3fd2d3SAndreas Gohr 2420b3fd2d3SAndreas Gohr /** 2430b3fd2d3SAndreas Gohr * Turns on searching the include path for class files. 2440b3fd2d3SAndreas Gohr * 2450b3fd2d3SAndreas Gohr * @param bool $useIncludePath 2460b3fd2d3SAndreas Gohr */ 2470b3fd2d3SAndreas Gohr public function setUseIncludePath($useIncludePath) 2480b3fd2d3SAndreas Gohr { 2490b3fd2d3SAndreas Gohr $this->useIncludePath = $useIncludePath; 2500b3fd2d3SAndreas Gohr } 2510b3fd2d3SAndreas Gohr 2520b3fd2d3SAndreas Gohr /** 2530b3fd2d3SAndreas Gohr * Can be used to check if the autoloader uses the include path to check 2540b3fd2d3SAndreas Gohr * for classes. 2550b3fd2d3SAndreas Gohr * 2560b3fd2d3SAndreas Gohr * @return bool 2570b3fd2d3SAndreas Gohr */ 2580b3fd2d3SAndreas Gohr public function getUseIncludePath() 2590b3fd2d3SAndreas Gohr { 2600b3fd2d3SAndreas Gohr return $this->useIncludePath; 2610b3fd2d3SAndreas Gohr } 2620b3fd2d3SAndreas Gohr 2630b3fd2d3SAndreas Gohr /** 2640b3fd2d3SAndreas Gohr * Turns off searching the prefix and fallback directories for classes 2650b3fd2d3SAndreas Gohr * that have not been registered with the class map. 2660b3fd2d3SAndreas Gohr * 2670b3fd2d3SAndreas Gohr * @param bool $classMapAuthoritative 2680b3fd2d3SAndreas Gohr */ 2690b3fd2d3SAndreas Gohr public function setClassMapAuthoritative($classMapAuthoritative) 2700b3fd2d3SAndreas Gohr { 2710b3fd2d3SAndreas Gohr $this->classMapAuthoritative = $classMapAuthoritative; 2720b3fd2d3SAndreas Gohr } 2730b3fd2d3SAndreas Gohr 2740b3fd2d3SAndreas Gohr /** 2750b3fd2d3SAndreas Gohr * Should class lookup fail if not found in the current class map? 2760b3fd2d3SAndreas Gohr * 2770b3fd2d3SAndreas Gohr * @return bool 2780b3fd2d3SAndreas Gohr */ 2790b3fd2d3SAndreas Gohr public function isClassMapAuthoritative() 2800b3fd2d3SAndreas Gohr { 2810b3fd2d3SAndreas Gohr return $this->classMapAuthoritative; 2820b3fd2d3SAndreas Gohr } 2830b3fd2d3SAndreas Gohr 2840b3fd2d3SAndreas Gohr /** 2850b3fd2d3SAndreas Gohr * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 2860b3fd2d3SAndreas Gohr * 2870b3fd2d3SAndreas Gohr * @param string|null $apcuPrefix 2880b3fd2d3SAndreas Gohr */ 2890b3fd2d3SAndreas Gohr public function setApcuPrefix($apcuPrefix) 2900b3fd2d3SAndreas Gohr { 2910b3fd2d3SAndreas Gohr $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 2920b3fd2d3SAndreas Gohr } 2930b3fd2d3SAndreas Gohr 2940b3fd2d3SAndreas Gohr /** 2950b3fd2d3SAndreas Gohr * The APCu prefix in use, or null if APCu caching is not enabled. 2960b3fd2d3SAndreas Gohr * 2970b3fd2d3SAndreas Gohr * @return string|null 2980b3fd2d3SAndreas Gohr */ 2990b3fd2d3SAndreas Gohr public function getApcuPrefix() 3000b3fd2d3SAndreas Gohr { 3010b3fd2d3SAndreas Gohr return $this->apcuPrefix; 3020b3fd2d3SAndreas Gohr } 3030b3fd2d3SAndreas Gohr 3040b3fd2d3SAndreas Gohr /** 3050b3fd2d3SAndreas Gohr * Registers this instance as an autoloader. 3060b3fd2d3SAndreas Gohr * 3070b3fd2d3SAndreas Gohr * @param bool $prepend Whether to prepend the autoloader or not 3080b3fd2d3SAndreas Gohr */ 3090b3fd2d3SAndreas Gohr public function register($prepend = false) 3100b3fd2d3SAndreas Gohr { 3110b3fd2d3SAndreas Gohr spl_autoload_register(array($this, 'loadClass'), true, $prepend); 312*fd0855ecSAndreas Gohr 313*fd0855ecSAndreas Gohr if (null === $this->vendorDir) { 314*fd0855ecSAndreas Gohr return; 315*fd0855ecSAndreas Gohr } 316*fd0855ecSAndreas Gohr 317*fd0855ecSAndreas Gohr if ($prepend) { 318*fd0855ecSAndreas Gohr self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 319*fd0855ecSAndreas Gohr } else { 320*fd0855ecSAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 321*fd0855ecSAndreas Gohr self::$registeredLoaders[$this->vendorDir] = $this; 322*fd0855ecSAndreas Gohr } 3230b3fd2d3SAndreas Gohr } 3240b3fd2d3SAndreas Gohr 3250b3fd2d3SAndreas Gohr /** 3260b3fd2d3SAndreas Gohr * Unregisters this instance as an autoloader. 3270b3fd2d3SAndreas Gohr */ 3280b3fd2d3SAndreas Gohr public function unregister() 3290b3fd2d3SAndreas Gohr { 3300b3fd2d3SAndreas Gohr spl_autoload_unregister(array($this, 'loadClass')); 331*fd0855ecSAndreas Gohr 332*fd0855ecSAndreas Gohr if (null !== $this->vendorDir) { 333*fd0855ecSAndreas Gohr unset(self::$registeredLoaders[$this->vendorDir]); 334*fd0855ecSAndreas Gohr } 3350b3fd2d3SAndreas Gohr } 3360b3fd2d3SAndreas Gohr 3370b3fd2d3SAndreas Gohr /** 3380b3fd2d3SAndreas Gohr * Loads the given class or interface. 3390b3fd2d3SAndreas Gohr * 3400b3fd2d3SAndreas Gohr * @param string $class The name of the class 341*fd0855ecSAndreas Gohr * @return true|null True if loaded, null otherwise 3420b3fd2d3SAndreas Gohr */ 3430b3fd2d3SAndreas Gohr public function loadClass($class) 3440b3fd2d3SAndreas Gohr { 3450b3fd2d3SAndreas Gohr if ($file = $this->findFile($class)) { 3460b3fd2d3SAndreas Gohr includeFile($file); 3470b3fd2d3SAndreas Gohr 3480b3fd2d3SAndreas Gohr return true; 3490b3fd2d3SAndreas Gohr } 350*fd0855ecSAndreas Gohr 351*fd0855ecSAndreas Gohr return null; 3520b3fd2d3SAndreas Gohr } 3530b3fd2d3SAndreas Gohr 3540b3fd2d3SAndreas Gohr /** 3550b3fd2d3SAndreas Gohr * Finds the path to the file where the class is defined. 3560b3fd2d3SAndreas Gohr * 3570b3fd2d3SAndreas Gohr * @param string $class The name of the class 3580b3fd2d3SAndreas Gohr * 3590b3fd2d3SAndreas Gohr * @return string|false The path if found, false otherwise 3600b3fd2d3SAndreas Gohr */ 3610b3fd2d3SAndreas Gohr public function findFile($class) 3620b3fd2d3SAndreas Gohr { 3630b3fd2d3SAndreas Gohr // class map lookup 3640b3fd2d3SAndreas Gohr if (isset($this->classMap[$class])) { 3650b3fd2d3SAndreas Gohr return $this->classMap[$class]; 3660b3fd2d3SAndreas Gohr } 3670b3fd2d3SAndreas Gohr if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 3680b3fd2d3SAndreas Gohr return false; 3690b3fd2d3SAndreas Gohr } 3700b3fd2d3SAndreas Gohr if (null !== $this->apcuPrefix) { 3710b3fd2d3SAndreas Gohr $file = apcu_fetch($this->apcuPrefix.$class, $hit); 3720b3fd2d3SAndreas Gohr if ($hit) { 3730b3fd2d3SAndreas Gohr return $file; 3740b3fd2d3SAndreas Gohr } 3750b3fd2d3SAndreas Gohr } 3760b3fd2d3SAndreas Gohr 3770b3fd2d3SAndreas Gohr $file = $this->findFileWithExtension($class, '.php'); 3780b3fd2d3SAndreas Gohr 3790b3fd2d3SAndreas Gohr // Search for Hack files if we are running on HHVM 3800b3fd2d3SAndreas Gohr if (false === $file && defined('HHVM_VERSION')) { 3810b3fd2d3SAndreas Gohr $file = $this->findFileWithExtension($class, '.hh'); 3820b3fd2d3SAndreas Gohr } 3830b3fd2d3SAndreas Gohr 3840b3fd2d3SAndreas Gohr if (null !== $this->apcuPrefix) { 3850b3fd2d3SAndreas Gohr apcu_add($this->apcuPrefix.$class, $file); 3860b3fd2d3SAndreas Gohr } 3870b3fd2d3SAndreas Gohr 3880b3fd2d3SAndreas Gohr if (false === $file) { 3890b3fd2d3SAndreas Gohr // Remember that this class does not exist. 3900b3fd2d3SAndreas Gohr $this->missingClasses[$class] = true; 3910b3fd2d3SAndreas Gohr } 3920b3fd2d3SAndreas Gohr 3930b3fd2d3SAndreas Gohr return $file; 3940b3fd2d3SAndreas Gohr } 3950b3fd2d3SAndreas Gohr 396*fd0855ecSAndreas Gohr /** 397*fd0855ecSAndreas Gohr * Returns the currently registered loaders indexed by their corresponding vendor directories. 398*fd0855ecSAndreas Gohr * 399*fd0855ecSAndreas Gohr * @return self[] 400*fd0855ecSAndreas Gohr */ 401*fd0855ecSAndreas Gohr public static function getRegisteredLoaders() 402*fd0855ecSAndreas Gohr { 403*fd0855ecSAndreas Gohr return self::$registeredLoaders; 404*fd0855ecSAndreas Gohr } 405*fd0855ecSAndreas Gohr 4060b3fd2d3SAndreas Gohr private function findFileWithExtension($class, $ext) 4070b3fd2d3SAndreas Gohr { 4080b3fd2d3SAndreas Gohr // PSR-4 lookup 4090b3fd2d3SAndreas Gohr $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 4100b3fd2d3SAndreas Gohr 4110b3fd2d3SAndreas Gohr $first = $class[0]; 4120b3fd2d3SAndreas Gohr if (isset($this->prefixLengthsPsr4[$first])) { 4130b3fd2d3SAndreas Gohr $subPath = $class; 4140b3fd2d3SAndreas Gohr while (false !== $lastPos = strrpos($subPath, '\\')) { 4150b3fd2d3SAndreas Gohr $subPath = substr($subPath, 0, $lastPos); 4160b3fd2d3SAndreas Gohr $search = $subPath . '\\'; 4170b3fd2d3SAndreas Gohr if (isset($this->prefixDirsPsr4[$search])) { 4180b3fd2d3SAndreas Gohr $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 4190b3fd2d3SAndreas Gohr foreach ($this->prefixDirsPsr4[$search] as $dir) { 4200b3fd2d3SAndreas Gohr if (file_exists($file = $dir . $pathEnd)) { 4210b3fd2d3SAndreas Gohr return $file; 4220b3fd2d3SAndreas Gohr } 4230b3fd2d3SAndreas Gohr } 4240b3fd2d3SAndreas Gohr } 4250b3fd2d3SAndreas Gohr } 4260b3fd2d3SAndreas Gohr } 4270b3fd2d3SAndreas Gohr 4280b3fd2d3SAndreas Gohr // PSR-4 fallback dirs 4290b3fd2d3SAndreas Gohr foreach ($this->fallbackDirsPsr4 as $dir) { 4300b3fd2d3SAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 4310b3fd2d3SAndreas Gohr return $file; 4320b3fd2d3SAndreas Gohr } 4330b3fd2d3SAndreas Gohr } 4340b3fd2d3SAndreas Gohr 4350b3fd2d3SAndreas Gohr // PSR-0 lookup 4360b3fd2d3SAndreas Gohr if (false !== $pos = strrpos($class, '\\')) { 4370b3fd2d3SAndreas Gohr // namespaced class name 4380b3fd2d3SAndreas Gohr $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 4390b3fd2d3SAndreas Gohr . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 4400b3fd2d3SAndreas Gohr } else { 4410b3fd2d3SAndreas Gohr // PEAR-like class name 4420b3fd2d3SAndreas Gohr $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 4430b3fd2d3SAndreas Gohr } 4440b3fd2d3SAndreas Gohr 4450b3fd2d3SAndreas Gohr if (isset($this->prefixesPsr0[$first])) { 4460b3fd2d3SAndreas Gohr foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 4470b3fd2d3SAndreas Gohr if (0 === strpos($class, $prefix)) { 4480b3fd2d3SAndreas Gohr foreach ($dirs as $dir) { 4490b3fd2d3SAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 4500b3fd2d3SAndreas Gohr return $file; 4510b3fd2d3SAndreas Gohr } 4520b3fd2d3SAndreas Gohr } 4530b3fd2d3SAndreas Gohr } 4540b3fd2d3SAndreas Gohr } 4550b3fd2d3SAndreas Gohr } 4560b3fd2d3SAndreas Gohr 4570b3fd2d3SAndreas Gohr // PSR-0 fallback dirs 4580b3fd2d3SAndreas Gohr foreach ($this->fallbackDirsPsr0 as $dir) { 4590b3fd2d3SAndreas Gohr if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 4600b3fd2d3SAndreas Gohr return $file; 4610b3fd2d3SAndreas Gohr } 4620b3fd2d3SAndreas Gohr } 4630b3fd2d3SAndreas Gohr 4640b3fd2d3SAndreas Gohr // PSR-0 include paths. 4650b3fd2d3SAndreas Gohr if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 4660b3fd2d3SAndreas Gohr return $file; 4670b3fd2d3SAndreas Gohr } 4680b3fd2d3SAndreas Gohr 4690b3fd2d3SAndreas Gohr return false; 4700b3fd2d3SAndreas Gohr } 4710b3fd2d3SAndreas Gohr} 4720b3fd2d3SAndreas Gohr 4730b3fd2d3SAndreas Gohr/** 4740b3fd2d3SAndreas Gohr * Scope isolated include. 4750b3fd2d3SAndreas Gohr * 4760b3fd2d3SAndreas Gohr * Prevents access to $this/self from included files. 4770b3fd2d3SAndreas Gohr */ 4780b3fd2d3SAndreas Gohrfunction includeFile($file) 4790b3fd2d3SAndreas Gohr{ 4800b3fd2d3SAndreas Gohr include $file; 4810b3fd2d3SAndreas Gohr} 482