xref: /plugin/dw2pdf/vendor/composer/ClassLoader.php (revision f6b6ef8702ed2497118554fab8d66533b3b9b760)
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>
40fb347f35SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-0/
41fb347f35SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-4/
420119ca25SAndreas Gohr */
430119ca25SAndreas Gohrclass ClassLoader
440119ca25SAndreas Gohr{
45fb347f35SAndreas Gohr    /** @var \Closure(string):void */
46fb347f35SAndreas Gohr    private static $includeFile;
47fb347f35SAndreas Gohr
48*f6b6ef87SEduardo Mozart de Oliveira    /** @var string|null */
49fb347f35SAndreas Gohr    private $vendorDir;
50fb347f35SAndreas Gohr
510119ca25SAndreas Gohr    // PSR-4
52fb347f35SAndreas Gohr    /**
53*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, array<string, int>>
54fb347f35SAndreas Gohr     */
550119ca25SAndreas Gohr    private $prefixLengthsPsr4 = array();
56fb347f35SAndreas Gohr    /**
57*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, list<string>>
58fb347f35SAndreas Gohr     */
590119ca25SAndreas Gohr    private $prefixDirsPsr4 = array();
60fb347f35SAndreas Gohr    /**
61*f6b6ef87SEduardo Mozart de Oliveira     * @var list<string>
62fb347f35SAndreas Gohr     */
630119ca25SAndreas Gohr    private $fallbackDirsPsr4 = array();
640119ca25SAndreas Gohr
650119ca25SAndreas Gohr    // PSR-0
66fb347f35SAndreas Gohr    /**
67*f6b6ef87SEduardo Mozart de Oliveira     * List of PSR-0 prefixes
68*f6b6ef87SEduardo Mozart de Oliveira     *
69*f6b6ef87SEduardo Mozart de Oliveira     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
70*f6b6ef87SEduardo Mozart de Oliveira     *
71*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, array<string, list<string>>>
72fb347f35SAndreas Gohr     */
730119ca25SAndreas Gohr    private $prefixesPsr0 = array();
74fb347f35SAndreas Gohr    /**
75*f6b6ef87SEduardo Mozart de Oliveira     * @var list<string>
76fb347f35SAndreas Gohr     */
770119ca25SAndreas Gohr    private $fallbackDirsPsr0 = array();
780119ca25SAndreas Gohr
79fb347f35SAndreas Gohr    /** @var bool */
800119ca25SAndreas Gohr    private $useIncludePath = false;
81fb347f35SAndreas Gohr
82fb347f35SAndreas Gohr    /**
83*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, string>
84fb347f35SAndreas Gohr     */
850119ca25SAndreas Gohr    private $classMap = array();
86fb347f35SAndreas Gohr
87fb347f35SAndreas Gohr    /** @var bool */
880119ca25SAndreas Gohr    private $classMapAuthoritative = false;
89fb347f35SAndreas Gohr
90fb347f35SAndreas Gohr    /**
91*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, bool>
92fb347f35SAndreas Gohr     */
930119ca25SAndreas Gohr    private $missingClasses = array();
94fb347f35SAndreas Gohr
95*f6b6ef87SEduardo Mozart de Oliveira    /** @var string|null */
960119ca25SAndreas Gohr    private $apcuPrefix;
970119ca25SAndreas Gohr
98fb347f35SAndreas Gohr    /**
99*f6b6ef87SEduardo Mozart de Oliveira     * @var array<string, self>
100fb347f35SAndreas Gohr     */
101fb347f35SAndreas Gohr    private static $registeredLoaders = array();
102fb347f35SAndreas Gohr
103fb347f35SAndreas Gohr    /**
104*f6b6ef87SEduardo Mozart de Oliveira     * @param string|null $vendorDir
105fb347f35SAndreas Gohr     */
106fb347f35SAndreas Gohr    public function __construct($vendorDir = null)
107fb347f35SAndreas Gohr    {
108fb347f35SAndreas Gohr        $this->vendorDir = $vendorDir;
109fb347f35SAndreas Gohr        self::initializeIncludeClosure();
110fb347f35SAndreas Gohr    }
111fb347f35SAndreas Gohr
112fb347f35SAndreas Gohr    /**
113*f6b6ef87SEduardo Mozart de Oliveira     * @return array<string, list<string>>
114fb347f35SAndreas Gohr     */
1150119ca25SAndreas Gohr    public function getPrefixes()
1160119ca25SAndreas Gohr    {
1170119ca25SAndreas Gohr        if (!empty($this->prefixesPsr0)) {
118fb347f35SAndreas Gohr            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
1190119ca25SAndreas Gohr        }
1200119ca25SAndreas Gohr
1210119ca25SAndreas Gohr        return array();
1220119ca25SAndreas Gohr    }
1230119ca25SAndreas Gohr
124fb347f35SAndreas Gohr    /**
125*f6b6ef87SEduardo Mozart de Oliveira     * @return array<string, list<string>>
126fb347f35SAndreas Gohr     */
1270119ca25SAndreas Gohr    public function getPrefixesPsr4()
1280119ca25SAndreas Gohr    {
1290119ca25SAndreas Gohr        return $this->prefixDirsPsr4;
1300119ca25SAndreas Gohr    }
1310119ca25SAndreas Gohr
132fb347f35SAndreas Gohr    /**
133*f6b6ef87SEduardo Mozart de Oliveira     * @return list<string>
134fb347f35SAndreas Gohr     */
1350119ca25SAndreas Gohr    public function getFallbackDirs()
1360119ca25SAndreas Gohr    {
1370119ca25SAndreas Gohr        return $this->fallbackDirsPsr0;
1380119ca25SAndreas Gohr    }
1390119ca25SAndreas Gohr
140fb347f35SAndreas Gohr    /**
141*f6b6ef87SEduardo Mozart de Oliveira     * @return list<string>
142fb347f35SAndreas Gohr     */
1430119ca25SAndreas Gohr    public function getFallbackDirsPsr4()
1440119ca25SAndreas Gohr    {
1450119ca25SAndreas Gohr        return $this->fallbackDirsPsr4;
1460119ca25SAndreas Gohr    }
1470119ca25SAndreas Gohr
148fb347f35SAndreas Gohr    /**
149*f6b6ef87SEduardo Mozart de Oliveira     * @return array<string, string> Array of classname => path
150fb347f35SAndreas Gohr     */
1510119ca25SAndreas Gohr    public function getClassMap()
1520119ca25SAndreas Gohr    {
1530119ca25SAndreas Gohr        return $this->classMap;
1540119ca25SAndreas Gohr    }
1550119ca25SAndreas Gohr
1560119ca25SAndreas Gohr    /**
157*f6b6ef87SEduardo Mozart de Oliveira     * @param array<string, string> $classMap Class to filename map
158fb347f35SAndreas Gohr     *
159fb347f35SAndreas Gohr     * @return void
1600119ca25SAndreas Gohr     */
1610119ca25SAndreas Gohr    public function addClassMap(array $classMap)
1620119ca25SAndreas Gohr    {
1630119ca25SAndreas Gohr        if ($this->classMap) {
1640119ca25SAndreas Gohr            $this->classMap = array_merge($this->classMap, $classMap);
1650119ca25SAndreas Gohr        } else {
1660119ca25SAndreas Gohr            $this->classMap = $classMap;
1670119ca25SAndreas Gohr        }
1680119ca25SAndreas Gohr    }
1690119ca25SAndreas Gohr
1700119ca25SAndreas Gohr    /**
1710119ca25SAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix, either
1720119ca25SAndreas Gohr     * appending or prepending to the ones previously set for this prefix.
1730119ca25SAndreas Gohr     *
1740119ca25SAndreas Gohr     * @param string              $prefix  The prefix
175*f6b6ef87SEduardo Mozart de Oliveira     * @param list<string>|string $paths   The PSR-0 root directories
1760119ca25SAndreas Gohr     * @param bool                $prepend Whether to prepend the directories
177fb347f35SAndreas Gohr     *
178fb347f35SAndreas Gohr     * @return void
1790119ca25SAndreas Gohr     */
1800119ca25SAndreas Gohr    public function add($prefix, $paths, $prepend = false)
1810119ca25SAndreas Gohr    {
182*f6b6ef87SEduardo Mozart de Oliveira        $paths = (array) $paths;
1830119ca25SAndreas Gohr        if (!$prefix) {
1840119ca25SAndreas Gohr            if ($prepend) {
1850119ca25SAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
186*f6b6ef87SEduardo Mozart de Oliveira                    $paths,
1870119ca25SAndreas Gohr                    $this->fallbackDirsPsr0
1880119ca25SAndreas Gohr                );
1890119ca25SAndreas Gohr            } else {
1900119ca25SAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
1910119ca25SAndreas Gohr                    $this->fallbackDirsPsr0,
192*f6b6ef87SEduardo Mozart de Oliveira                    $paths
1930119ca25SAndreas Gohr                );
1940119ca25SAndreas Gohr            }
1950119ca25SAndreas Gohr
1960119ca25SAndreas Gohr            return;
1970119ca25SAndreas Gohr        }
1980119ca25SAndreas Gohr
1990119ca25SAndreas Gohr        $first = $prefix[0];
2000119ca25SAndreas Gohr        if (!isset($this->prefixesPsr0[$first][$prefix])) {
201*f6b6ef87SEduardo Mozart de Oliveira            $this->prefixesPsr0[$first][$prefix] = $paths;
2020119ca25SAndreas Gohr
2030119ca25SAndreas Gohr            return;
2040119ca25SAndreas Gohr        }
2050119ca25SAndreas Gohr        if ($prepend) {
2060119ca25SAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
207*f6b6ef87SEduardo Mozart de Oliveira                $paths,
2080119ca25SAndreas Gohr                $this->prefixesPsr0[$first][$prefix]
2090119ca25SAndreas Gohr            );
2100119ca25SAndreas Gohr        } else {
2110119ca25SAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
2120119ca25SAndreas Gohr                $this->prefixesPsr0[$first][$prefix],
213*f6b6ef87SEduardo Mozart de Oliveira                $paths
2140119ca25SAndreas Gohr            );
2150119ca25SAndreas Gohr        }
2160119ca25SAndreas Gohr    }
2170119ca25SAndreas Gohr
2180119ca25SAndreas Gohr    /**
2190119ca25SAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace, either
2200119ca25SAndreas Gohr     * appending or prepending to the ones previously set for this namespace.
2210119ca25SAndreas Gohr     *
2220119ca25SAndreas Gohr     * @param string              $prefix  The prefix/namespace, with trailing '\\'
223*f6b6ef87SEduardo Mozart de Oliveira     * @param list<string>|string $paths   The PSR-4 base directories
2240119ca25SAndreas Gohr     * @param bool                $prepend Whether to prepend the directories
2250119ca25SAndreas Gohr     *
2260119ca25SAndreas Gohr     * @throws \InvalidArgumentException
227fb347f35SAndreas Gohr     *
228fb347f35SAndreas Gohr     * @return void
2290119ca25SAndreas Gohr     */
2300119ca25SAndreas Gohr    public function addPsr4($prefix, $paths, $prepend = false)
2310119ca25SAndreas Gohr    {
232*f6b6ef87SEduardo Mozart de Oliveira        $paths = (array) $paths;
2330119ca25SAndreas Gohr        if (!$prefix) {
2340119ca25SAndreas Gohr            // Register directories for the root namespace.
2350119ca25SAndreas Gohr            if ($prepend) {
2360119ca25SAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
237*f6b6ef87SEduardo Mozart de Oliveira                    $paths,
2380119ca25SAndreas Gohr                    $this->fallbackDirsPsr4
2390119ca25SAndreas Gohr                );
2400119ca25SAndreas Gohr            } else {
2410119ca25SAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
2420119ca25SAndreas Gohr                    $this->fallbackDirsPsr4,
243*f6b6ef87SEduardo Mozart de Oliveira                    $paths
2440119ca25SAndreas Gohr                );
2450119ca25SAndreas Gohr            }
2460119ca25SAndreas Gohr        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
2470119ca25SAndreas Gohr            // Register directories for a new namespace.
2480119ca25SAndreas Gohr            $length = strlen($prefix);
2490119ca25SAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
2500119ca25SAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
2510119ca25SAndreas Gohr            }
2520119ca25SAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
253*f6b6ef87SEduardo Mozart de Oliveira            $this->prefixDirsPsr4[$prefix] = $paths;
2540119ca25SAndreas Gohr        } elseif ($prepend) {
2550119ca25SAndreas Gohr            // Prepend directories for an already registered namespace.
2560119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
257*f6b6ef87SEduardo Mozart de Oliveira                $paths,
2580119ca25SAndreas Gohr                $this->prefixDirsPsr4[$prefix]
2590119ca25SAndreas Gohr            );
2600119ca25SAndreas Gohr        } else {
2610119ca25SAndreas Gohr            // Append directories for an already registered namespace.
2620119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
2630119ca25SAndreas Gohr                $this->prefixDirsPsr4[$prefix],
264*f6b6ef87SEduardo Mozart de Oliveira                $paths
2650119ca25SAndreas Gohr            );
2660119ca25SAndreas Gohr        }
2670119ca25SAndreas Gohr    }
2680119ca25SAndreas Gohr
2690119ca25SAndreas Gohr    /**
2700119ca25SAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix,
2710119ca25SAndreas Gohr     * replacing any others previously set for this prefix.
2720119ca25SAndreas Gohr     *
2730119ca25SAndreas Gohr     * @param string              $prefix The prefix
274*f6b6ef87SEduardo Mozart de Oliveira     * @param list<string>|string $paths  The PSR-0 base directories
275fb347f35SAndreas Gohr     *
276fb347f35SAndreas Gohr     * @return void
2770119ca25SAndreas Gohr     */
2780119ca25SAndreas Gohr    public function set($prefix, $paths)
2790119ca25SAndreas Gohr    {
2800119ca25SAndreas Gohr        if (!$prefix) {
2810119ca25SAndreas Gohr            $this->fallbackDirsPsr0 = (array) $paths;
2820119ca25SAndreas Gohr        } else {
2830119ca25SAndreas Gohr            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
2840119ca25SAndreas Gohr        }
2850119ca25SAndreas Gohr    }
2860119ca25SAndreas Gohr
2870119ca25SAndreas Gohr    /**
2880119ca25SAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace,
2890119ca25SAndreas Gohr     * replacing any others previously set for this namespace.
2900119ca25SAndreas Gohr     *
2910119ca25SAndreas Gohr     * @param string              $prefix The prefix/namespace, with trailing '\\'
292*f6b6ef87SEduardo Mozart de Oliveira     * @param list<string>|string $paths  The PSR-4 base directories
2930119ca25SAndreas Gohr     *
2940119ca25SAndreas Gohr     * @throws \InvalidArgumentException
295fb347f35SAndreas Gohr     *
296fb347f35SAndreas Gohr     * @return void
2970119ca25SAndreas Gohr     */
2980119ca25SAndreas Gohr    public function setPsr4($prefix, $paths)
2990119ca25SAndreas Gohr    {
3000119ca25SAndreas Gohr        if (!$prefix) {
3010119ca25SAndreas Gohr            $this->fallbackDirsPsr4 = (array) $paths;
3020119ca25SAndreas Gohr        } else {
3030119ca25SAndreas Gohr            $length = strlen($prefix);
3040119ca25SAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
3050119ca25SAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
3060119ca25SAndreas Gohr            }
3070119ca25SAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
3080119ca25SAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
3090119ca25SAndreas Gohr        }
3100119ca25SAndreas Gohr    }
3110119ca25SAndreas Gohr
3120119ca25SAndreas Gohr    /**
3130119ca25SAndreas Gohr     * Turns on searching the include path for class files.
3140119ca25SAndreas Gohr     *
3150119ca25SAndreas Gohr     * @param bool $useIncludePath
316fb347f35SAndreas Gohr     *
317fb347f35SAndreas Gohr     * @return void
3180119ca25SAndreas Gohr     */
3190119ca25SAndreas Gohr    public function setUseIncludePath($useIncludePath)
3200119ca25SAndreas Gohr    {
3210119ca25SAndreas Gohr        $this->useIncludePath = $useIncludePath;
3220119ca25SAndreas Gohr    }
3230119ca25SAndreas Gohr
3240119ca25SAndreas Gohr    /**
3250119ca25SAndreas Gohr     * Can be used to check if the autoloader uses the include path to check
3260119ca25SAndreas Gohr     * for classes.
3270119ca25SAndreas Gohr     *
3280119ca25SAndreas Gohr     * @return bool
3290119ca25SAndreas Gohr     */
3300119ca25SAndreas Gohr    public function getUseIncludePath()
3310119ca25SAndreas Gohr    {
3320119ca25SAndreas Gohr        return $this->useIncludePath;
3330119ca25SAndreas Gohr    }
3340119ca25SAndreas Gohr
3350119ca25SAndreas Gohr    /**
3360119ca25SAndreas Gohr     * Turns off searching the prefix and fallback directories for classes
3370119ca25SAndreas Gohr     * that have not been registered with the class map.
3380119ca25SAndreas Gohr     *
3390119ca25SAndreas Gohr     * @param bool $classMapAuthoritative
340fb347f35SAndreas Gohr     *
341fb347f35SAndreas Gohr     * @return void
3420119ca25SAndreas Gohr     */
3430119ca25SAndreas Gohr    public function setClassMapAuthoritative($classMapAuthoritative)
3440119ca25SAndreas Gohr    {
3450119ca25SAndreas Gohr        $this->classMapAuthoritative = $classMapAuthoritative;
3460119ca25SAndreas Gohr    }
3470119ca25SAndreas Gohr
3480119ca25SAndreas Gohr    /**
3490119ca25SAndreas Gohr     * Should class lookup fail if not found in the current class map?
3500119ca25SAndreas Gohr     *
3510119ca25SAndreas Gohr     * @return bool
3520119ca25SAndreas Gohr     */
3530119ca25SAndreas Gohr    public function isClassMapAuthoritative()
3540119ca25SAndreas Gohr    {
3550119ca25SAndreas Gohr        return $this->classMapAuthoritative;
3560119ca25SAndreas Gohr    }
3570119ca25SAndreas Gohr
3580119ca25SAndreas Gohr    /**
3590119ca25SAndreas Gohr     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
3600119ca25SAndreas Gohr     *
3610119ca25SAndreas Gohr     * @param string|null $apcuPrefix
362fb347f35SAndreas Gohr     *
363fb347f35SAndreas Gohr     * @return void
3640119ca25SAndreas Gohr     */
3650119ca25SAndreas Gohr    public function setApcuPrefix($apcuPrefix)
3660119ca25SAndreas Gohr    {
367dc4d9dc6SAnna Dabrowska        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
3680119ca25SAndreas Gohr    }
3690119ca25SAndreas Gohr
3700119ca25SAndreas Gohr    /**
3710119ca25SAndreas Gohr     * The APCu prefix in use, or null if APCu caching is not enabled.
3720119ca25SAndreas Gohr     *
3730119ca25SAndreas Gohr     * @return string|null
3740119ca25SAndreas Gohr     */
3750119ca25SAndreas Gohr    public function getApcuPrefix()
3760119ca25SAndreas Gohr    {
3770119ca25SAndreas Gohr        return $this->apcuPrefix;
3780119ca25SAndreas Gohr    }
3790119ca25SAndreas Gohr
3800119ca25SAndreas Gohr    /**
3810119ca25SAndreas Gohr     * Registers this instance as an autoloader.
3820119ca25SAndreas Gohr     *
3830119ca25SAndreas Gohr     * @param bool $prepend Whether to prepend the autoloader or not
384fb347f35SAndreas Gohr     *
385fb347f35SAndreas Gohr     * @return void
3860119ca25SAndreas Gohr     */
3870119ca25SAndreas Gohr    public function register($prepend = false)
3880119ca25SAndreas Gohr    {
3890119ca25SAndreas Gohr        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
390fb347f35SAndreas Gohr
391fb347f35SAndreas Gohr        if (null === $this->vendorDir) {
392fb347f35SAndreas Gohr            return;
393fb347f35SAndreas Gohr        }
394fb347f35SAndreas Gohr
395fb347f35SAndreas Gohr        if ($prepend) {
396fb347f35SAndreas Gohr            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
397fb347f35SAndreas Gohr        } else {
398fb347f35SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
399fb347f35SAndreas Gohr            self::$registeredLoaders[$this->vendorDir] = $this;
400fb347f35SAndreas Gohr        }
4010119ca25SAndreas Gohr    }
4020119ca25SAndreas Gohr
4030119ca25SAndreas Gohr    /**
4040119ca25SAndreas Gohr     * Unregisters this instance as an autoloader.
405fb347f35SAndreas Gohr     *
406fb347f35SAndreas Gohr     * @return void
4070119ca25SAndreas Gohr     */
4080119ca25SAndreas Gohr    public function unregister()
4090119ca25SAndreas Gohr    {
4100119ca25SAndreas Gohr        spl_autoload_unregister(array($this, 'loadClass'));
411fb347f35SAndreas Gohr
412fb347f35SAndreas Gohr        if (null !== $this->vendorDir) {
413fb347f35SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
414fb347f35SAndreas Gohr        }
4150119ca25SAndreas Gohr    }
4160119ca25SAndreas Gohr
4170119ca25SAndreas Gohr    /**
4180119ca25SAndreas Gohr     * Loads the given class or interface.
4190119ca25SAndreas Gohr     *
4200119ca25SAndreas Gohr     * @param  string    $class The name of the class
421fb347f35SAndreas Gohr     * @return true|null True if loaded, null otherwise
4220119ca25SAndreas Gohr     */
4230119ca25SAndreas Gohr    public function loadClass($class)
4240119ca25SAndreas Gohr    {
4250119ca25SAndreas Gohr        if ($file = $this->findFile($class)) {
426fb347f35SAndreas Gohr            $includeFile = self::$includeFile;
427fb347f35SAndreas Gohr            $includeFile($file);
4280119ca25SAndreas Gohr
4290119ca25SAndreas Gohr            return true;
4300119ca25SAndreas Gohr        }
431fb347f35SAndreas Gohr
432fb347f35SAndreas Gohr        return null;
4330119ca25SAndreas Gohr    }
4340119ca25SAndreas Gohr
4350119ca25SAndreas Gohr    /**
4360119ca25SAndreas Gohr     * Finds the path to the file where the class is defined.
4370119ca25SAndreas Gohr     *
4380119ca25SAndreas Gohr     * @param string $class The name of the class
4390119ca25SAndreas Gohr     *
4400119ca25SAndreas Gohr     * @return string|false The path if found, false otherwise
4410119ca25SAndreas Gohr     */
4420119ca25SAndreas Gohr    public function findFile($class)
4430119ca25SAndreas Gohr    {
4440119ca25SAndreas Gohr        // class map lookup
4450119ca25SAndreas Gohr        if (isset($this->classMap[$class])) {
4460119ca25SAndreas Gohr            return $this->classMap[$class];
4470119ca25SAndreas Gohr        }
4480119ca25SAndreas Gohr        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
4490119ca25SAndreas Gohr            return false;
4500119ca25SAndreas Gohr        }
4510119ca25SAndreas Gohr        if (null !== $this->apcuPrefix) {
4520119ca25SAndreas Gohr            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
4530119ca25SAndreas Gohr            if ($hit) {
4540119ca25SAndreas Gohr                return $file;
4550119ca25SAndreas Gohr            }
4560119ca25SAndreas Gohr        }
4570119ca25SAndreas Gohr
4580119ca25SAndreas Gohr        $file = $this->findFileWithExtension($class, '.php');
4590119ca25SAndreas Gohr
4600119ca25SAndreas Gohr        // Search for Hack files if we are running on HHVM
4610119ca25SAndreas Gohr        if (false === $file && defined('HHVM_VERSION')) {
4620119ca25SAndreas Gohr            $file = $this->findFileWithExtension($class, '.hh');
4630119ca25SAndreas Gohr        }
4640119ca25SAndreas Gohr
4650119ca25SAndreas Gohr        if (null !== $this->apcuPrefix) {
4660119ca25SAndreas Gohr            apcu_add($this->apcuPrefix.$class, $file);
4670119ca25SAndreas Gohr        }
4680119ca25SAndreas Gohr
4690119ca25SAndreas Gohr        if (false === $file) {
4700119ca25SAndreas Gohr            // Remember that this class does not exist.
4710119ca25SAndreas Gohr            $this->missingClasses[$class] = true;
4720119ca25SAndreas Gohr        }
4730119ca25SAndreas Gohr
4740119ca25SAndreas Gohr        return $file;
4750119ca25SAndreas Gohr    }
4760119ca25SAndreas Gohr
477fb347f35SAndreas Gohr    /**
478*f6b6ef87SEduardo Mozart de Oliveira     * Returns the currently registered loaders keyed by their corresponding vendor directories.
479fb347f35SAndreas Gohr     *
480*f6b6ef87SEduardo Mozart de Oliveira     * @return array<string, self>
481fb347f35SAndreas Gohr     */
482fb347f35SAndreas Gohr    public static function getRegisteredLoaders()
483fb347f35SAndreas Gohr    {
484fb347f35SAndreas Gohr        return self::$registeredLoaders;
485fb347f35SAndreas Gohr    }
486fb347f35SAndreas Gohr
487fb347f35SAndreas Gohr    /**
488fb347f35SAndreas Gohr     * @param  string       $class
489fb347f35SAndreas Gohr     * @param  string       $ext
490fb347f35SAndreas Gohr     * @return string|false
491fb347f35SAndreas Gohr     */
4920119ca25SAndreas Gohr    private function findFileWithExtension($class, $ext)
4930119ca25SAndreas Gohr    {
4940119ca25SAndreas Gohr        // PSR-4 lookup
4950119ca25SAndreas Gohr        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
4960119ca25SAndreas Gohr
4970119ca25SAndreas Gohr        $first = $class[0];
4980119ca25SAndreas Gohr        if (isset($this->prefixLengthsPsr4[$first])) {
4997f027680SAndreas Gohr            $subPath = $class;
5007f027680SAndreas Gohr            while (false !== $lastPos = strrpos($subPath, '\\')) {
5017f027680SAndreas Gohr                $subPath = substr($subPath, 0, $lastPos);
5027f027680SAndreas Gohr                $search = $subPath . '\\';
5037f027680SAndreas Gohr                if (isset($this->prefixDirsPsr4[$search])) {
504e56751dfSMichael Große                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
5057f027680SAndreas Gohr                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
506e56751dfSMichael Große                        if (file_exists($file = $dir . $pathEnd)) {
5070119ca25SAndreas Gohr                            return $file;
5080119ca25SAndreas Gohr                        }
5090119ca25SAndreas Gohr                    }
5100119ca25SAndreas Gohr                }
5110119ca25SAndreas Gohr            }
5120119ca25SAndreas Gohr        }
5130119ca25SAndreas Gohr
5140119ca25SAndreas Gohr        // PSR-4 fallback dirs
5150119ca25SAndreas Gohr        foreach ($this->fallbackDirsPsr4 as $dir) {
5160119ca25SAndreas Gohr            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
5170119ca25SAndreas Gohr                return $file;
5180119ca25SAndreas Gohr            }
5190119ca25SAndreas Gohr        }
5200119ca25SAndreas Gohr
5210119ca25SAndreas Gohr        // PSR-0 lookup
5220119ca25SAndreas Gohr        if (false !== $pos = strrpos($class, '\\')) {
5230119ca25SAndreas Gohr            // namespaced class name
5240119ca25SAndreas Gohr            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
5250119ca25SAndreas Gohr                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
5260119ca25SAndreas Gohr        } else {
5270119ca25SAndreas Gohr            // PEAR-like class name
5280119ca25SAndreas Gohr            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
5290119ca25SAndreas Gohr        }
5300119ca25SAndreas Gohr
5310119ca25SAndreas Gohr        if (isset($this->prefixesPsr0[$first])) {
5320119ca25SAndreas Gohr            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
5330119ca25SAndreas Gohr                if (0 === strpos($class, $prefix)) {
5340119ca25SAndreas Gohr                    foreach ($dirs as $dir) {
5350119ca25SAndreas Gohr                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
5360119ca25SAndreas Gohr                            return $file;
5370119ca25SAndreas Gohr                        }
5380119ca25SAndreas Gohr                    }
5390119ca25SAndreas Gohr                }
5400119ca25SAndreas Gohr            }
5410119ca25SAndreas Gohr        }
5420119ca25SAndreas Gohr
5430119ca25SAndreas Gohr        // PSR-0 fallback dirs
5440119ca25SAndreas Gohr        foreach ($this->fallbackDirsPsr0 as $dir) {
5450119ca25SAndreas Gohr            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
5460119ca25SAndreas Gohr                return $file;
5470119ca25SAndreas Gohr            }
5480119ca25SAndreas Gohr        }
5490119ca25SAndreas Gohr
5500119ca25SAndreas Gohr        // PSR-0 include paths.
5510119ca25SAndreas Gohr        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
5520119ca25SAndreas Gohr            return $file;
5530119ca25SAndreas Gohr        }
5540119ca25SAndreas Gohr
5550119ca25SAndreas Gohr        return false;
5560119ca25SAndreas Gohr    }
557fb347f35SAndreas Gohr
558fb347f35SAndreas Gohr    /**
559fb347f35SAndreas Gohr     * @return void
560fb347f35SAndreas Gohr     */
561fb347f35SAndreas Gohr    private static function initializeIncludeClosure()
562fb347f35SAndreas Gohr    {
563fb347f35SAndreas Gohr        if (self::$includeFile !== null) {
564fb347f35SAndreas Gohr            return;
5650119ca25SAndreas Gohr        }
5660119ca25SAndreas Gohr
5670119ca25SAndreas Gohr        /**
5680119ca25SAndreas Gohr         * Scope isolated include.
5690119ca25SAndreas Gohr         *
5700119ca25SAndreas Gohr         * Prevents access to $this/self from included files.
571fb347f35SAndreas Gohr         *
572fb347f35SAndreas Gohr         * @param  string $file
573fb347f35SAndreas Gohr         * @return void
5740119ca25SAndreas Gohr         */
575fb347f35SAndreas Gohr        self::$includeFile = \Closure::bind(static function($file) {
5760119ca25SAndreas Gohr            include $file;
577fb347f35SAndreas Gohr        }, null, null);
578fb347f35SAndreas Gohr    }
5790119ca25SAndreas Gohr}
580