xref: /plugin/dw2pdf/vendor/composer/ClassLoader.php (revision fb347f35dc824cf59875d883dbf86d311f54de06)
10119ca25SAndreas Gohr<?php
20119ca25SAndreas Gohr
30119ca25SAndreas Gohr/*
40119ca25SAndreas Gohr * This file is part of Composer.
50119ca25SAndreas Gohr *
60119ca25SAndreas Gohr * (c) Nils Adermann <naderman@naderman.de>
70119ca25SAndreas Gohr *     Jordi Boggiano <j.boggiano@seld.be>
80119ca25SAndreas Gohr *
90119ca25SAndreas Gohr * For the full copyright and license information, please view the LICENSE
100119ca25SAndreas Gohr * file that was distributed with this source code.
110119ca25SAndreas Gohr */
120119ca25SAndreas Gohr
130119ca25SAndreas Gohrnamespace Composer\Autoload;
140119ca25SAndreas Gohr
150119ca25SAndreas Gohr/**
160119ca25SAndreas Gohr * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
170119ca25SAndreas Gohr *
180119ca25SAndreas Gohr *     $loader = new \Composer\Autoload\ClassLoader();
190119ca25SAndreas Gohr *
200119ca25SAndreas Gohr *     // register classes with namespaces
210119ca25SAndreas Gohr *     $loader->add('Symfony\Component', __DIR__.'/component');
220119ca25SAndreas Gohr *     $loader->add('Symfony',           __DIR__.'/framework');
230119ca25SAndreas Gohr *
240119ca25SAndreas Gohr *     // activate the autoloader
250119ca25SAndreas Gohr *     $loader->register();
260119ca25SAndreas Gohr *
270119ca25SAndreas Gohr *     // to enable searching the include path (eg. for PEAR packages)
280119ca25SAndreas Gohr *     $loader->setUseIncludePath(true);
290119ca25SAndreas Gohr *
300119ca25SAndreas Gohr * In this example, if you try to use a class in the Symfony\Component
310119ca25SAndreas Gohr * namespace or one of its children (Symfony\Component\Console for instance),
320119ca25SAndreas Gohr * the autoloader will first look for the class under the component/
330119ca25SAndreas Gohr * directory, and it will then fallback to the framework/ directory if not
340119ca25SAndreas Gohr * found before giving up.
350119ca25SAndreas Gohr *
360119ca25SAndreas Gohr * This class is loosely based on the Symfony UniversalClassLoader.
370119ca25SAndreas Gohr *
380119ca25SAndreas Gohr * @author Fabien Potencier <fabien@symfony.com>
390119ca25SAndreas Gohr * @author Jordi Boggiano <j.boggiano@seld.be>
40*fb347f35SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-0/
41*fb347f35SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-4/
420119ca25SAndreas Gohr */
430119ca25SAndreas Gohrclass ClassLoader
440119ca25SAndreas Gohr{
45*fb347f35SAndreas Gohr    /** @var \Closure(string):void */
46*fb347f35SAndreas Gohr    private static $includeFile;
47*fb347f35SAndreas Gohr
48*fb347f35SAndreas Gohr    /** @var ?string */
49*fb347f35SAndreas Gohr    private $vendorDir;
50*fb347f35SAndreas Gohr
510119ca25SAndreas Gohr    // PSR-4
52*fb347f35SAndreas Gohr    /**
53*fb347f35SAndreas Gohr     * @var array[]
54*fb347f35SAndreas Gohr     * @psalm-var array<string, array<string, int>>
55*fb347f35SAndreas Gohr     */
560119ca25SAndreas Gohr    private $prefixLengthsPsr4 = array();
57*fb347f35SAndreas Gohr    /**
58*fb347f35SAndreas Gohr     * @var array[]
59*fb347f35SAndreas Gohr     * @psalm-var array<string, array<int, string>>
60*fb347f35SAndreas Gohr     */
610119ca25SAndreas Gohr    private $prefixDirsPsr4 = array();
62*fb347f35SAndreas Gohr    /**
63*fb347f35SAndreas Gohr     * @var array[]
64*fb347f35SAndreas Gohr     * @psalm-var array<string, string>
65*fb347f35SAndreas Gohr     */
660119ca25SAndreas Gohr    private $fallbackDirsPsr4 = array();
670119ca25SAndreas Gohr
680119ca25SAndreas Gohr    // PSR-0
69*fb347f35SAndreas Gohr    /**
70*fb347f35SAndreas Gohr     * @var array[]
71*fb347f35SAndreas Gohr     * @psalm-var array<string, array<string, string[]>>
72*fb347f35SAndreas Gohr     */
730119ca25SAndreas Gohr    private $prefixesPsr0 = array();
74*fb347f35SAndreas Gohr    /**
75*fb347f35SAndreas Gohr     * @var array[]
76*fb347f35SAndreas Gohr     * @psalm-var array<string, string>
77*fb347f35SAndreas Gohr     */
780119ca25SAndreas Gohr    private $fallbackDirsPsr0 = array();
790119ca25SAndreas Gohr
80*fb347f35SAndreas Gohr    /** @var bool */
810119ca25SAndreas Gohr    private $useIncludePath = false;
82*fb347f35SAndreas Gohr
83*fb347f35SAndreas Gohr    /**
84*fb347f35SAndreas Gohr     * @var string[]
85*fb347f35SAndreas Gohr     * @psalm-var array<string, string>
86*fb347f35SAndreas Gohr     */
870119ca25SAndreas Gohr    private $classMap = array();
88*fb347f35SAndreas Gohr
89*fb347f35SAndreas Gohr    /** @var bool */
900119ca25SAndreas Gohr    private $classMapAuthoritative = false;
91*fb347f35SAndreas Gohr
92*fb347f35SAndreas Gohr    /**
93*fb347f35SAndreas Gohr     * @var bool[]
94*fb347f35SAndreas Gohr     * @psalm-var array<string, bool>
95*fb347f35SAndreas Gohr     */
960119ca25SAndreas Gohr    private $missingClasses = array();
97*fb347f35SAndreas Gohr
98*fb347f35SAndreas Gohr    /** @var ?string */
990119ca25SAndreas Gohr    private $apcuPrefix;
1000119ca25SAndreas Gohr
101*fb347f35SAndreas Gohr    /**
102*fb347f35SAndreas Gohr     * @var self[]
103*fb347f35SAndreas Gohr     */
104*fb347f35SAndreas Gohr    private static $registeredLoaders = array();
105*fb347f35SAndreas Gohr
106*fb347f35SAndreas Gohr    /**
107*fb347f35SAndreas Gohr     * @param ?string $vendorDir
108*fb347f35SAndreas Gohr     */
109*fb347f35SAndreas Gohr    public function __construct($vendorDir = null)
110*fb347f35SAndreas Gohr    {
111*fb347f35SAndreas Gohr        $this->vendorDir = $vendorDir;
112*fb347f35SAndreas Gohr        self::initializeIncludeClosure();
113*fb347f35SAndreas Gohr    }
114*fb347f35SAndreas Gohr
115*fb347f35SAndreas Gohr    /**
116*fb347f35SAndreas Gohr     * @return string[]
117*fb347f35SAndreas Gohr     */
1180119ca25SAndreas Gohr    public function getPrefixes()
1190119ca25SAndreas Gohr    {
1200119ca25SAndreas Gohr        if (!empty($this->prefixesPsr0)) {
121*fb347f35SAndreas Gohr            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
1220119ca25SAndreas Gohr        }
1230119ca25SAndreas Gohr
1240119ca25SAndreas Gohr        return array();
1250119ca25SAndreas Gohr    }
1260119ca25SAndreas Gohr
127*fb347f35SAndreas Gohr    /**
128*fb347f35SAndreas Gohr     * @return array[]
129*fb347f35SAndreas Gohr     * @psalm-return array<string, array<int, string>>
130*fb347f35SAndreas Gohr     */
1310119ca25SAndreas Gohr    public function getPrefixesPsr4()
1320119ca25SAndreas Gohr    {
1330119ca25SAndreas Gohr        return $this->prefixDirsPsr4;
1340119ca25SAndreas Gohr    }
1350119ca25SAndreas Gohr
136*fb347f35SAndreas Gohr    /**
137*fb347f35SAndreas Gohr     * @return array[]
138*fb347f35SAndreas Gohr     * @psalm-return array<string, string>
139*fb347f35SAndreas Gohr     */
1400119ca25SAndreas Gohr    public function getFallbackDirs()
1410119ca25SAndreas Gohr    {
1420119ca25SAndreas Gohr        return $this->fallbackDirsPsr0;
1430119ca25SAndreas Gohr    }
1440119ca25SAndreas Gohr
145*fb347f35SAndreas Gohr    /**
146*fb347f35SAndreas Gohr     * @return array[]
147*fb347f35SAndreas Gohr     * @psalm-return array<string, string>
148*fb347f35SAndreas Gohr     */
1490119ca25SAndreas Gohr    public function getFallbackDirsPsr4()
1500119ca25SAndreas Gohr    {
1510119ca25SAndreas Gohr        return $this->fallbackDirsPsr4;
1520119ca25SAndreas Gohr    }
1530119ca25SAndreas Gohr
154*fb347f35SAndreas Gohr    /**
155*fb347f35SAndreas Gohr     * @return string[] Array of classname => path
156*fb347f35SAndreas Gohr     * @psalm-return array<string, string>
157*fb347f35SAndreas Gohr     */
1580119ca25SAndreas Gohr    public function getClassMap()
1590119ca25SAndreas Gohr    {
1600119ca25SAndreas Gohr        return $this->classMap;
1610119ca25SAndreas Gohr    }
1620119ca25SAndreas Gohr
1630119ca25SAndreas Gohr    /**
164*fb347f35SAndreas Gohr     * @param string[] $classMap Class to filename map
165*fb347f35SAndreas Gohr     * @psalm-param array<string, string> $classMap
166*fb347f35SAndreas Gohr     *
167*fb347f35SAndreas Gohr     * @return void
1680119ca25SAndreas Gohr     */
1690119ca25SAndreas Gohr    public function addClassMap(array $classMap)
1700119ca25SAndreas Gohr    {
1710119ca25SAndreas Gohr        if ($this->classMap) {
1720119ca25SAndreas Gohr            $this->classMap = array_merge($this->classMap, $classMap);
1730119ca25SAndreas Gohr        } else {
1740119ca25SAndreas Gohr            $this->classMap = $classMap;
1750119ca25SAndreas Gohr        }
1760119ca25SAndreas Gohr    }
1770119ca25SAndreas Gohr
1780119ca25SAndreas Gohr    /**
1790119ca25SAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix, either
1800119ca25SAndreas Gohr     * appending or prepending to the ones previously set for this prefix.
1810119ca25SAndreas Gohr     *
1820119ca25SAndreas Gohr     * @param string          $prefix  The prefix
183*fb347f35SAndreas Gohr     * @param string[]|string $paths   The PSR-0 root directories
1840119ca25SAndreas Gohr     * @param bool            $prepend Whether to prepend the directories
185*fb347f35SAndreas Gohr     *
186*fb347f35SAndreas Gohr     * @return void
1870119ca25SAndreas Gohr     */
1880119ca25SAndreas Gohr    public function add($prefix, $paths, $prepend = false)
1890119ca25SAndreas Gohr    {
1900119ca25SAndreas Gohr        if (!$prefix) {
1910119ca25SAndreas Gohr            if ($prepend) {
1920119ca25SAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
1930119ca25SAndreas Gohr                    (array) $paths,
1940119ca25SAndreas Gohr                    $this->fallbackDirsPsr0
1950119ca25SAndreas Gohr                );
1960119ca25SAndreas Gohr            } else {
1970119ca25SAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
1980119ca25SAndreas Gohr                    $this->fallbackDirsPsr0,
1990119ca25SAndreas Gohr                    (array) $paths
2000119ca25SAndreas Gohr                );
2010119ca25SAndreas Gohr            }
2020119ca25SAndreas Gohr
2030119ca25SAndreas Gohr            return;
2040119ca25SAndreas Gohr        }
2050119ca25SAndreas Gohr
2060119ca25SAndreas Gohr        $first = $prefix[0];
2070119ca25SAndreas Gohr        if (!isset($this->prefixesPsr0[$first][$prefix])) {
2080119ca25SAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
2090119ca25SAndreas Gohr
2100119ca25SAndreas Gohr            return;
2110119ca25SAndreas Gohr        }
2120119ca25SAndreas Gohr        if ($prepend) {
2130119ca25SAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
2140119ca25SAndreas Gohr                (array) $paths,
2150119ca25SAndreas Gohr                $this->prefixesPsr0[$first][$prefix]
2160119ca25SAndreas Gohr            );
2170119ca25SAndreas Gohr        } else {
2180119ca25SAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
2190119ca25SAndreas Gohr                $this->prefixesPsr0[$first][$prefix],
2200119ca25SAndreas Gohr                (array) $paths
2210119ca25SAndreas Gohr            );
2220119ca25SAndreas Gohr        }
2230119ca25SAndreas Gohr    }
2240119ca25SAndreas Gohr
2250119ca25SAndreas Gohr    /**
2260119ca25SAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace, either
2270119ca25SAndreas Gohr     * appending or prepending to the ones previously set for this namespace.
2280119ca25SAndreas Gohr     *
2290119ca25SAndreas Gohr     * @param string          $prefix  The prefix/namespace, with trailing '\\'
230*fb347f35SAndreas Gohr     * @param string[]|string $paths   The PSR-4 base directories
2310119ca25SAndreas Gohr     * @param bool            $prepend Whether to prepend the directories
2320119ca25SAndreas Gohr     *
2330119ca25SAndreas Gohr     * @throws \InvalidArgumentException
234*fb347f35SAndreas Gohr     *
235*fb347f35SAndreas Gohr     * @return void
2360119ca25SAndreas Gohr     */
2370119ca25SAndreas Gohr    public function addPsr4($prefix, $paths, $prepend = false)
2380119ca25SAndreas Gohr    {
2390119ca25SAndreas Gohr        if (!$prefix) {
2400119ca25SAndreas Gohr            // Register directories for the root namespace.
2410119ca25SAndreas Gohr            if ($prepend) {
2420119ca25SAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
2430119ca25SAndreas Gohr                    (array) $paths,
2440119ca25SAndreas Gohr                    $this->fallbackDirsPsr4
2450119ca25SAndreas Gohr                );
2460119ca25SAndreas Gohr            } else {
2470119ca25SAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
2480119ca25SAndreas Gohr                    $this->fallbackDirsPsr4,
2490119ca25SAndreas Gohr                    (array) $paths
2500119ca25SAndreas Gohr                );
2510119ca25SAndreas Gohr            }
2520119ca25SAndreas Gohr        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
2530119ca25SAndreas Gohr            // Register directories for a new namespace.
2540119ca25SAndreas Gohr            $length = strlen($prefix);
2550119ca25SAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
2560119ca25SAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
2570119ca25SAndreas Gohr            }
2580119ca25SAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
2590119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
2600119ca25SAndreas Gohr        } elseif ($prepend) {
2610119ca25SAndreas Gohr            // Prepend directories for an already registered namespace.
2620119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
2630119ca25SAndreas Gohr                (array) $paths,
2640119ca25SAndreas Gohr                $this->prefixDirsPsr4[$prefix]
2650119ca25SAndreas Gohr            );
2660119ca25SAndreas Gohr        } else {
2670119ca25SAndreas Gohr            // Append directories for an already registered namespace.
2680119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
2690119ca25SAndreas Gohr                $this->prefixDirsPsr4[$prefix],
2700119ca25SAndreas Gohr                (array) $paths
2710119ca25SAndreas Gohr            );
2720119ca25SAndreas Gohr        }
2730119ca25SAndreas Gohr    }
2740119ca25SAndreas Gohr
2750119ca25SAndreas Gohr    /**
2760119ca25SAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix,
2770119ca25SAndreas Gohr     * replacing any others previously set for this prefix.
2780119ca25SAndreas Gohr     *
2790119ca25SAndreas Gohr     * @param string          $prefix The prefix
280*fb347f35SAndreas Gohr     * @param string[]|string $paths  The PSR-0 base directories
281*fb347f35SAndreas Gohr     *
282*fb347f35SAndreas Gohr     * @return void
2830119ca25SAndreas Gohr     */
2840119ca25SAndreas Gohr    public function set($prefix, $paths)
2850119ca25SAndreas Gohr    {
2860119ca25SAndreas Gohr        if (!$prefix) {
2870119ca25SAndreas Gohr            $this->fallbackDirsPsr0 = (array) $paths;
2880119ca25SAndreas Gohr        } else {
2890119ca25SAndreas Gohr            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
2900119ca25SAndreas Gohr        }
2910119ca25SAndreas Gohr    }
2920119ca25SAndreas Gohr
2930119ca25SAndreas Gohr    /**
2940119ca25SAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace,
2950119ca25SAndreas Gohr     * replacing any others previously set for this namespace.
2960119ca25SAndreas Gohr     *
2970119ca25SAndreas Gohr     * @param string          $prefix The prefix/namespace, with trailing '\\'
298*fb347f35SAndreas Gohr     * @param string[]|string $paths  The PSR-4 base directories
2990119ca25SAndreas Gohr     *
3000119ca25SAndreas Gohr     * @throws \InvalidArgumentException
301*fb347f35SAndreas Gohr     *
302*fb347f35SAndreas Gohr     * @return void
3030119ca25SAndreas Gohr     */
3040119ca25SAndreas Gohr    public function setPsr4($prefix, $paths)
3050119ca25SAndreas Gohr    {
3060119ca25SAndreas Gohr        if (!$prefix) {
3070119ca25SAndreas Gohr            $this->fallbackDirsPsr4 = (array) $paths;
3080119ca25SAndreas Gohr        } else {
3090119ca25SAndreas Gohr            $length = strlen($prefix);
3100119ca25SAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
3110119ca25SAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
3120119ca25SAndreas Gohr            }
3130119ca25SAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
3140119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
3150119ca25SAndreas Gohr        }
3160119ca25SAndreas Gohr    }
3170119ca25SAndreas Gohr
3180119ca25SAndreas Gohr    /**
3190119ca25SAndreas Gohr     * Turns on searching the include path for class files.
3200119ca25SAndreas Gohr     *
3210119ca25SAndreas Gohr     * @param bool $useIncludePath
322*fb347f35SAndreas Gohr     *
323*fb347f35SAndreas Gohr     * @return void
3240119ca25SAndreas Gohr     */
3250119ca25SAndreas Gohr    public function setUseIncludePath($useIncludePath)
3260119ca25SAndreas Gohr    {
3270119ca25SAndreas Gohr        $this->useIncludePath = $useIncludePath;
3280119ca25SAndreas Gohr    }
3290119ca25SAndreas Gohr
3300119ca25SAndreas Gohr    /**
3310119ca25SAndreas Gohr     * Can be used to check if the autoloader uses the include path to check
3320119ca25SAndreas Gohr     * for classes.
3330119ca25SAndreas Gohr     *
3340119ca25SAndreas Gohr     * @return bool
3350119ca25SAndreas Gohr     */
3360119ca25SAndreas Gohr    public function getUseIncludePath()
3370119ca25SAndreas Gohr    {
3380119ca25SAndreas Gohr        return $this->useIncludePath;
3390119ca25SAndreas Gohr    }
3400119ca25SAndreas Gohr
3410119ca25SAndreas Gohr    /**
3420119ca25SAndreas Gohr     * Turns off searching the prefix and fallback directories for classes
3430119ca25SAndreas Gohr     * that have not been registered with the class map.
3440119ca25SAndreas Gohr     *
3450119ca25SAndreas Gohr     * @param bool $classMapAuthoritative
346*fb347f35SAndreas Gohr     *
347*fb347f35SAndreas Gohr     * @return void
3480119ca25SAndreas Gohr     */
3490119ca25SAndreas Gohr    public function setClassMapAuthoritative($classMapAuthoritative)
3500119ca25SAndreas Gohr    {
3510119ca25SAndreas Gohr        $this->classMapAuthoritative = $classMapAuthoritative;
3520119ca25SAndreas Gohr    }
3530119ca25SAndreas Gohr
3540119ca25SAndreas Gohr    /**
3550119ca25SAndreas Gohr     * Should class lookup fail if not found in the current class map?
3560119ca25SAndreas Gohr     *
3570119ca25SAndreas Gohr     * @return bool
3580119ca25SAndreas Gohr     */
3590119ca25SAndreas Gohr    public function isClassMapAuthoritative()
3600119ca25SAndreas Gohr    {
3610119ca25SAndreas Gohr        return $this->classMapAuthoritative;
3620119ca25SAndreas Gohr    }
3630119ca25SAndreas Gohr
3640119ca25SAndreas Gohr    /**
3650119ca25SAndreas Gohr     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
3660119ca25SAndreas Gohr     *
3670119ca25SAndreas Gohr     * @param string|null $apcuPrefix
368*fb347f35SAndreas Gohr     *
369*fb347f35SAndreas Gohr     * @return void
3700119ca25SAndreas Gohr     */
3710119ca25SAndreas Gohr    public function setApcuPrefix($apcuPrefix)
3720119ca25SAndreas Gohr    {
373dc4d9dc6SAnna Dabrowska        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
3740119ca25SAndreas Gohr    }
3750119ca25SAndreas Gohr
3760119ca25SAndreas Gohr    /**
3770119ca25SAndreas Gohr     * The APCu prefix in use, or null if APCu caching is not enabled.
3780119ca25SAndreas Gohr     *
3790119ca25SAndreas Gohr     * @return string|null
3800119ca25SAndreas Gohr     */
3810119ca25SAndreas Gohr    public function getApcuPrefix()
3820119ca25SAndreas Gohr    {
3830119ca25SAndreas Gohr        return $this->apcuPrefix;
3840119ca25SAndreas Gohr    }
3850119ca25SAndreas Gohr
3860119ca25SAndreas Gohr    /**
3870119ca25SAndreas Gohr     * Registers this instance as an autoloader.
3880119ca25SAndreas Gohr     *
3890119ca25SAndreas Gohr     * @param bool $prepend Whether to prepend the autoloader or not
390*fb347f35SAndreas Gohr     *
391*fb347f35SAndreas Gohr     * @return void
3920119ca25SAndreas Gohr     */
3930119ca25SAndreas Gohr    public function register($prepend = false)
3940119ca25SAndreas Gohr    {
3950119ca25SAndreas Gohr        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
396*fb347f35SAndreas Gohr
397*fb347f35SAndreas Gohr        if (null === $this->vendorDir) {
398*fb347f35SAndreas Gohr            return;
399*fb347f35SAndreas Gohr        }
400*fb347f35SAndreas Gohr
401*fb347f35SAndreas Gohr        if ($prepend) {
402*fb347f35SAndreas Gohr            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
403*fb347f35SAndreas Gohr        } else {
404*fb347f35SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
405*fb347f35SAndreas Gohr            self::$registeredLoaders[$this->vendorDir] = $this;
406*fb347f35SAndreas Gohr        }
4070119ca25SAndreas Gohr    }
4080119ca25SAndreas Gohr
4090119ca25SAndreas Gohr    /**
4100119ca25SAndreas Gohr     * Unregisters this instance as an autoloader.
411*fb347f35SAndreas Gohr     *
412*fb347f35SAndreas Gohr     * @return void
4130119ca25SAndreas Gohr     */
4140119ca25SAndreas Gohr    public function unregister()
4150119ca25SAndreas Gohr    {
4160119ca25SAndreas Gohr        spl_autoload_unregister(array($this, 'loadClass'));
417*fb347f35SAndreas Gohr
418*fb347f35SAndreas Gohr        if (null !== $this->vendorDir) {
419*fb347f35SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
420*fb347f35SAndreas Gohr        }
4210119ca25SAndreas Gohr    }
4220119ca25SAndreas Gohr
4230119ca25SAndreas Gohr    /**
4240119ca25SAndreas Gohr     * Loads the given class or interface.
4250119ca25SAndreas Gohr     *
4260119ca25SAndreas Gohr     * @param  string    $class The name of the class
427*fb347f35SAndreas Gohr     * @return true|null True if loaded, null otherwise
4280119ca25SAndreas Gohr     */
4290119ca25SAndreas Gohr    public function loadClass($class)
4300119ca25SAndreas Gohr    {
4310119ca25SAndreas Gohr        if ($file = $this->findFile($class)) {
432*fb347f35SAndreas Gohr            $includeFile = self::$includeFile;
433*fb347f35SAndreas Gohr            $includeFile($file);
4340119ca25SAndreas Gohr
4350119ca25SAndreas Gohr            return true;
4360119ca25SAndreas Gohr        }
437*fb347f35SAndreas Gohr
438*fb347f35SAndreas Gohr        return null;
4390119ca25SAndreas Gohr    }
4400119ca25SAndreas Gohr
4410119ca25SAndreas Gohr    /**
4420119ca25SAndreas Gohr     * Finds the path to the file where the class is defined.
4430119ca25SAndreas Gohr     *
4440119ca25SAndreas Gohr     * @param string $class The name of the class
4450119ca25SAndreas Gohr     *
4460119ca25SAndreas Gohr     * @return string|false The path if found, false otherwise
4470119ca25SAndreas Gohr     */
4480119ca25SAndreas Gohr    public function findFile($class)
4490119ca25SAndreas Gohr    {
4500119ca25SAndreas Gohr        // class map lookup
4510119ca25SAndreas Gohr        if (isset($this->classMap[$class])) {
4520119ca25SAndreas Gohr            return $this->classMap[$class];
4530119ca25SAndreas Gohr        }
4540119ca25SAndreas Gohr        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
4550119ca25SAndreas Gohr            return false;
4560119ca25SAndreas Gohr        }
4570119ca25SAndreas Gohr        if (null !== $this->apcuPrefix) {
4580119ca25SAndreas Gohr            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
4590119ca25SAndreas Gohr            if ($hit) {
4600119ca25SAndreas Gohr                return $file;
4610119ca25SAndreas Gohr            }
4620119ca25SAndreas Gohr        }
4630119ca25SAndreas Gohr
4640119ca25SAndreas Gohr        $file = $this->findFileWithExtension($class, '.php');
4650119ca25SAndreas Gohr
4660119ca25SAndreas Gohr        // Search for Hack files if we are running on HHVM
4670119ca25SAndreas Gohr        if (false === $file && defined('HHVM_VERSION')) {
4680119ca25SAndreas Gohr            $file = $this->findFileWithExtension($class, '.hh');
4690119ca25SAndreas Gohr        }
4700119ca25SAndreas Gohr
4710119ca25SAndreas Gohr        if (null !== $this->apcuPrefix) {
4720119ca25SAndreas Gohr            apcu_add($this->apcuPrefix.$class, $file);
4730119ca25SAndreas Gohr        }
4740119ca25SAndreas Gohr
4750119ca25SAndreas Gohr        if (false === $file) {
4760119ca25SAndreas Gohr            // Remember that this class does not exist.
4770119ca25SAndreas Gohr            $this->missingClasses[$class] = true;
4780119ca25SAndreas Gohr        }
4790119ca25SAndreas Gohr
4800119ca25SAndreas Gohr        return $file;
4810119ca25SAndreas Gohr    }
4820119ca25SAndreas Gohr
483*fb347f35SAndreas Gohr    /**
484*fb347f35SAndreas Gohr     * Returns the currently registered loaders indexed by their corresponding vendor directories.
485*fb347f35SAndreas Gohr     *
486*fb347f35SAndreas Gohr     * @return self[]
487*fb347f35SAndreas Gohr     */
488*fb347f35SAndreas Gohr    public static function getRegisteredLoaders()
489*fb347f35SAndreas Gohr    {
490*fb347f35SAndreas Gohr        return self::$registeredLoaders;
491*fb347f35SAndreas Gohr    }
492*fb347f35SAndreas Gohr
493*fb347f35SAndreas Gohr    /**
494*fb347f35SAndreas Gohr     * @param  string       $class
495*fb347f35SAndreas Gohr     * @param  string       $ext
496*fb347f35SAndreas Gohr     * @return string|false
497*fb347f35SAndreas Gohr     */
4980119ca25SAndreas Gohr    private function findFileWithExtension($class, $ext)
4990119ca25SAndreas Gohr    {
5000119ca25SAndreas Gohr        // PSR-4 lookup
5010119ca25SAndreas Gohr        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
5020119ca25SAndreas Gohr
5030119ca25SAndreas Gohr        $first = $class[0];
5040119ca25SAndreas Gohr        if (isset($this->prefixLengthsPsr4[$first])) {
5057f027680SAndreas Gohr            $subPath = $class;
5067f027680SAndreas Gohr            while (false !== $lastPos = strrpos($subPath, '\\')) {
5077f027680SAndreas Gohr                $subPath = substr($subPath, 0, $lastPos);
5087f027680SAndreas Gohr                $search = $subPath . '\\';
5097f027680SAndreas Gohr                if (isset($this->prefixDirsPsr4[$search])) {
510e56751dfSMichael Große                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
5117f027680SAndreas Gohr                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
512e56751dfSMichael Große                        if (file_exists($file = $dir . $pathEnd)) {
5130119ca25SAndreas Gohr                            return $file;
5140119ca25SAndreas Gohr                        }
5150119ca25SAndreas Gohr                    }
5160119ca25SAndreas Gohr                }
5170119ca25SAndreas Gohr            }
5180119ca25SAndreas Gohr        }
5190119ca25SAndreas Gohr
5200119ca25SAndreas Gohr        // PSR-4 fallback dirs
5210119ca25SAndreas Gohr        foreach ($this->fallbackDirsPsr4 as $dir) {
5220119ca25SAndreas Gohr            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
5230119ca25SAndreas Gohr                return $file;
5240119ca25SAndreas Gohr            }
5250119ca25SAndreas Gohr        }
5260119ca25SAndreas Gohr
5270119ca25SAndreas Gohr        // PSR-0 lookup
5280119ca25SAndreas Gohr        if (false !== $pos = strrpos($class, '\\')) {
5290119ca25SAndreas Gohr            // namespaced class name
5300119ca25SAndreas Gohr            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
5310119ca25SAndreas Gohr                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
5320119ca25SAndreas Gohr        } else {
5330119ca25SAndreas Gohr            // PEAR-like class name
5340119ca25SAndreas Gohr            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
5350119ca25SAndreas Gohr        }
5360119ca25SAndreas Gohr
5370119ca25SAndreas Gohr        if (isset($this->prefixesPsr0[$first])) {
5380119ca25SAndreas Gohr            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
5390119ca25SAndreas Gohr                if (0 === strpos($class, $prefix)) {
5400119ca25SAndreas Gohr                    foreach ($dirs as $dir) {
5410119ca25SAndreas Gohr                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
5420119ca25SAndreas Gohr                            return $file;
5430119ca25SAndreas Gohr                        }
5440119ca25SAndreas Gohr                    }
5450119ca25SAndreas Gohr                }
5460119ca25SAndreas Gohr            }
5470119ca25SAndreas Gohr        }
5480119ca25SAndreas Gohr
5490119ca25SAndreas Gohr        // PSR-0 fallback dirs
5500119ca25SAndreas Gohr        foreach ($this->fallbackDirsPsr0 as $dir) {
5510119ca25SAndreas Gohr            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
5520119ca25SAndreas Gohr                return $file;
5530119ca25SAndreas Gohr            }
5540119ca25SAndreas Gohr        }
5550119ca25SAndreas Gohr
5560119ca25SAndreas Gohr        // PSR-0 include paths.
5570119ca25SAndreas Gohr        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
5580119ca25SAndreas Gohr            return $file;
5590119ca25SAndreas Gohr        }
5600119ca25SAndreas Gohr
5610119ca25SAndreas Gohr        return false;
5620119ca25SAndreas Gohr    }
563*fb347f35SAndreas Gohr
564*fb347f35SAndreas Gohr    /**
565*fb347f35SAndreas Gohr     * @return void
566*fb347f35SAndreas Gohr     */
567*fb347f35SAndreas Gohr    private static function initializeIncludeClosure()
568*fb347f35SAndreas Gohr    {
569*fb347f35SAndreas Gohr        if (self::$includeFile !== null) {
570*fb347f35SAndreas Gohr            return;
5710119ca25SAndreas Gohr        }
5720119ca25SAndreas Gohr
5730119ca25SAndreas Gohr        /**
5740119ca25SAndreas Gohr         * Scope isolated include.
5750119ca25SAndreas Gohr         *
5760119ca25SAndreas Gohr         * Prevents access to $this/self from included files.
577*fb347f35SAndreas Gohr         *
578*fb347f35SAndreas Gohr         * @param  string $file
579*fb347f35SAndreas Gohr         * @return void
5800119ca25SAndreas Gohr         */
581*fb347f35SAndreas Gohr        self::$includeFile = \Closure::bind(static function($file) {
5820119ca25SAndreas Gohr            include $file;
583*fb347f35SAndreas Gohr        }, null, null);
584*fb347f35SAndreas Gohr    }
5850119ca25SAndreas Gohr}
586