123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- <?php
- /*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- * Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Composer\Autoload;
- /**
- * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
- *
- * $loader = new \Composer\Autoload\ClassLoader();
- *
- * // register classes with namespaces
- * $loader->add('Symfony\Component', __DIR__.'/component');
- * $loader->add('Symfony', __DIR__.'/framework');
- *
- * // activate the autoloader
- * $loader->register();
- *
- * // to enable searching the include path (eg. for PEAR packages)
- * $loader->setUseIncludePath(true);
- *
- * In this example, if you try to use a class in the Symfony\Component
- * namespace or one of its children (Symfony\Component\Console for instance),
- * the autoloader will first look for the class under the component/
- * directory, and it will then fallback to the framework/ directory if not
- * found before giving up.
- *
- * This class is loosely based on the Symfony UniversalClassLoader.
- *
- * @author Fabien Potencier <fabien@symfony.com>
- * @author Jordi Boggiano <j.boggiano@seld.be>
- * @see https://www.php-fig.org/psr/psr-0/
- * @see https://www.php-fig.org/psr/psr-4/
- */
- class ClassLoader
- {
- /** @var ?string */
- private $vendorDir;
- // PSR-4
- /**
- * @var array[]
- * @psalm-var array<string, array<string, int>>
- */
- private $prefixLengthsPsr4 = array();
- /**
- * @var array[]
- * @psalm-var array<string, array<int, string>>
- */
- private $prefixDirsPsr4 = array();
- /**
- * @var array[]
- * @psalm-var array<string, string>
- */
- private $fallbackDirsPsr4 = array();
- // PSR-0
- /**
- * @var array[]
- * @psalm-var array<string, array<string, string[]>>
- */
- private $prefixesPsr0 = array();
- /**
- * @var array[]
- * @psalm-var array<string, string>
- */
- private $fallbackDirsPsr0 = array();
- /** @var bool */
- private $useIncludePath = false;
- /**
- * @var string[]
- * @psalm-var array<string, string>
- */
- private $classMap = array();
- /** @var bool */
- private $classMapAuthoritative = false;
- /**
- * @var bool[]
- * @psalm-var array<string, bool>
- */
- private $missingClasses = array();
- /** @var ?string */
- private $apcuPrefix;
- /**
- * @var self[]
- */
- private static $registeredLoaders = array();
- /**
- * @param ?string $vendorDir
- */
- public function __construct($vendorDir = null)
- {
- $this->vendorDir = $vendorDir;
- }
- /**
- * @return string[]
- */
- public function getPrefixes()
- {
- if (!empty($this->prefixesPsr0)) {
- return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
- }
- return array();
- }
- /**
- * @return array[]
- * @psalm-return array<string, array<int, string>>
- */
- public function getPrefixesPsr4()
- {
- return $this->prefixDirsPsr4;
- }
- /**
- * @return array[]
- * @psalm-return array<string, string>
- */
- public function getFallbackDirs()
- {
- return $this->fallbackDirsPsr0;
- }
- /**
- * @return array[]
- * @psalm-return array<string, string>
- */
- public function getFallbackDirsPsr4()
- {
- return $this->fallbackDirsPsr4;
- }
- /**
- * @return string[] Array of classname => path
- * @psalm-return array<string, string>
- */
- public function getClassMap()
- {
- return $this->classMap;
- }
- /**
- * @param string[] $classMap Class to filename map
- * @psalm-param array<string, string> $classMap
- *
- * @return void
- */
- public function addClassMap(array $classMap)
- {
- if ($this->classMap) {
- $this->classMap = array_merge($this->classMap, $classMap);
- } else {
- $this->classMap = $classMap;
- }
- }
- /**
- * Registers a set of PSR-0 directories for a given prefix, either
- * appending or prepending to the ones previously set for this prefix.
- *
- * @param string $prefix The prefix
- * @param string[]|string $paths The PSR-0 root directories
- * @param bool $prepend Whether to prepend the directories
- *
- * @return void
- */
- public function add($prefix, $paths, $prepend = false)
- {
- if (!$prefix) {
- if ($prepend) {
- $this->fallbackDirsPsr0 = array_merge(
- (array) $paths,
- $this->fallbackDirsPsr0
- );
- } else {
- $this->fallbackDirsPsr0 = array_merge(
- $this->fallbackDirsPsr0,
- (array) $paths
- );
- }
- return;
- }
- $first = $prefix[0];
- if (!isset($this->prefixesPsr0[$first][$prefix])) {
- $this->prefixesPsr0[$first][$prefix] = (array) $paths;
- return;
- }
- if ($prepend) {
- $this->prefixesPsr0[$first][$prefix] = array_merge(
- (array) $paths,
- $this->prefixesPsr0[$first][$prefix]
- );
- } else {
- $this->prefixesPsr0[$first][$prefix] = array_merge(
- $this->prefixesPsr0[$first][$prefix],
- (array) $paths
- );
- }
- }
- /**
- * Registers a set of PSR-4 directories for a given namespace, either
- * appending or prepending to the ones previously set for this namespace.
- *
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param string[]|string $paths The PSR-4 base directories
- * @param bool $prepend Whether to prepend the directories
- *
- * @throws \InvalidArgumentException
- *
- * @return void
- */
- public function addPsr4($prefix, $paths, $prepend = false)
- {
- if (!$prefix) {
- // Register directories for the root namespace.
- if ($prepend) {
- $this->fallbackDirsPsr4 = array_merge(
- (array) $paths,
- $this->fallbackDirsPsr4
- );
- } else {
- $this->fallbackDirsPsr4 = array_merge(
- $this->fallbackDirsPsr4,
- (array) $paths
- );
- }
- } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
- // Register directories for a new namespace.
- $length = strlen($prefix);
- if ('\\' !== $prefix[$length - 1]) {
- throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
- }
- $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
- $this->prefixDirsPsr4[$prefix] = (array) $paths;
- } elseif ($prepend) {
- // Prepend directories for an already registered namespace.
- $this->prefixDirsPsr4[$prefix] = array_merge(
- (array) $paths,
- $this->prefixDirsPsr4[$prefix]
- );
- } else {
- // Append directories for an already registered namespace.
- $this->prefixDirsPsr4[$prefix] = array_merge(
- $this->prefixDirsPsr4[$prefix],
- (array) $paths
- );
- }
- }
- /**
- * Registers a set of PSR-0 directories for a given prefix,
- * replacing any others previously set for this prefix.
- *
- * @param string $prefix The prefix
- * @param string[]|string $paths The PSR-0 base directories
- *
- * @return void
- */
- public function set($prefix, $paths)
- {
- if (!$prefix) {
- $this->fallbackDirsPsr0 = (array) $paths;
- } else {
- $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
- }
- }
- /**
- * Registers a set of PSR-4 directories for a given namespace,
- * replacing any others previously set for this namespace.
- *
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param string[]|string $paths The PSR-4 base directories
- *
- * @throws \InvalidArgumentException
- *
- * @return void
- */
- public function setPsr4($prefix, $paths)
- {
- if (!$prefix) {
- $this->fallbackDirsPsr4 = (array) $paths;
- } else {
- $length = strlen($prefix);
- if ('\\' !== $prefix[$length - 1]) {
- throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
- }
- $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
- $this->prefixDirsPsr4[$prefix] = (array) $paths;
- }
- }
- /**
- * Turns on searching the include path for class files.
- *
- * @param bool $useIncludePath
- *
- * @return void
- */
- public function setUseIncludePath($useIncludePath)
- {
- $this->useIncludePath = $useIncludePath;
- }
- /**
- * Can be used to check if the autoloader uses the include path to check
- * for classes.
- *
- * @return bool
- */
- public function getUseIncludePath()
- {
- return $this->useIncludePath;
- }
- /**
- * Turns off searching the prefix and fallback directories for classes
- * that have not been registered with the class map.
- *
- * @param bool $classMapAuthoritative
- *
- * @return void
- */
- public function setClassMapAuthoritative($classMapAuthoritative)
- {
- $this->classMapAuthoritative = $classMapAuthoritative;
- }
- /**
- * Should class lookup fail if not found in the current class map?
- *
- * @return bool
- */
- public function isClassMapAuthoritative()
- {
- return $this->classMapAuthoritative;
- }
- /**
- * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
- *
- * @param string|null $apcuPrefix
- *
- * @return void
- */
- public function setApcuPrefix($apcuPrefix)
- {
- $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
- }
- /**
- * The APCu prefix in use, or null if APCu caching is not enabled.
- *
- * @return string|null
- */
- public function getApcuPrefix()
- {
- return $this->apcuPrefix;
- }
- /**
- * Registers this instance as an autoloader.
- *
- * @param bool $prepend Whether to prepend the autoloader or not
- *
- * @return void
- */
- public function register($prepend = false)
- {
- spl_autoload_register(array($this, 'loadClass'), true, $prepend);
- if (null === $this->vendorDir) {
- return;
- }
- if ($prepend) {
- self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
- } else {
- unset(self::$registeredLoaders[$this->vendorDir]);
- self::$registeredLoaders[$this->vendorDir] = $this;
- }
- }
- /**
- * Unregisters this instance as an autoloader.
- *
- * @return void
- */
- public function unregister()
- {
- spl_autoload_unregister(array($this, 'loadClass'));
- if (null !== $this->vendorDir) {
- unset(self::$registeredLoaders[$this->vendorDir]);
- }
- }
- /**
- * Loads the given class or interface.
- *
- * @param string $class The name of the class
- * @return true|null True if loaded, null otherwise
- */
- public function loadClass($class)
- {
- if ($file = $this->findFile($class)) {
- includeFile($file);
- return true;
- }
- return null;
- }
- /**
- * Finds the path to the file where the class is defined.
- *
- * @param string $class The name of the class
- *
- * @return string|false The path if found, false otherwise
- */
- public function findFile($class)
- {
- // class map lookup
- if (isset($this->classMap[$class])) {
- return $this->classMap[$class];
- }
- if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
- return false;
- }
- if (null !== $this->apcuPrefix) {
- $file = apcu_fetch($this->apcuPrefix.$class, $hit);
- if ($hit) {
- return $file;
- }
- }
- $file = $this->findFileWithExtension($class, '.php');
- // Search for Hack files if we are running on HHVM
- if (false === $file && defined('HHVM_VERSION')) {
- $file = $this->findFileWithExtension($class, '.hh');
- }
- if (null !== $this->apcuPrefix) {
- apcu_add($this->apcuPrefix.$class, $file);
- }
- if (false === $file) {
- // Remember that this class does not exist.
- $this->missingClasses[$class] = true;
- }
- return $file;
- }
- /**
- * Returns the currently registered loaders indexed by their corresponding vendor directories.
- *
- * @return self[]
- */
- public static function getRegisteredLoaders()
- {
- return self::$registeredLoaders;
- }
- /**
- * @param string $class
- * @param string $ext
- * @return string|false
- */
- private function findFileWithExtension($class, $ext)
- {
- // PSR-4 lookup
- $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
- $first = $class[0];
- if (isset($this->prefixLengthsPsr4[$first])) {
- $subPath = $class;
- while (false !== $lastPos = strrpos($subPath, '\\')) {
- $subPath = substr($subPath, 0, $lastPos);
- $search = $subPath . '\\';
- if (isset($this->prefixDirsPsr4[$search])) {
- $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
- foreach ($this->prefixDirsPsr4[$search] as $dir) {
- if (file_exists($file = $dir . $pathEnd)) {
- return $file;
- }
- }
- }
- }
- }
- // PSR-4 fallback dirs
- foreach ($this->fallbackDirsPsr4 as $dir) {
- if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
- return $file;
- }
- }
- // PSR-0 lookup
- if (false !== $pos = strrpos($class, '\\')) {
- // namespaced class name
- $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
- . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
- } else {
- // PEAR-like class name
- $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
- }
- if (isset($this->prefixesPsr0[$first])) {
- foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
- if (0 === strpos($class, $prefix)) {
- foreach ($dirs as $dir) {
- if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
- return $file;
- }
- }
- }
- }
- }
- // PSR-0 fallback dirs
- foreach ($this->fallbackDirsPsr0 as $dir) {
- if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
- return $file;
- }
- }
- // PSR-0 include paths.
- if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
- return $file;
- }
- return false;
- }
- }
- /**
- * Scope isolated include.
- *
- * Prevents access to $this/self from included files.
- *
- * @param string $file
- * @return void
- * @private
- */
- function includeFile($file)
- {
- include $file;
- }
|