xref: /plugin/combo/vendor/composer/ClassLoader.php (revision 83c6863253ba0b92605aa8dceca974358d439aaa)
137748cd8SNickeau<?php
237748cd8SNickeau
337748cd8SNickeau/*
437748cd8SNickeau * This file is part of Composer.
537748cd8SNickeau *
637748cd8SNickeau * (c) Nils Adermann <naderman@naderman.de>
737748cd8SNickeau *     Jordi Boggiano <j.boggiano@seld.be>
837748cd8SNickeau *
937748cd8SNickeau * For the full copyright and license information, please view the LICENSE
1037748cd8SNickeau * file that was distributed with this source code.
1137748cd8SNickeau */
1237748cd8SNickeau
1337748cd8SNickeaunamespace Composer\Autoload;
1437748cd8SNickeau
1537748cd8SNickeau/**
1637748cd8SNickeau * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
1737748cd8SNickeau *
1837748cd8SNickeau *     $loader = new \Composer\Autoload\ClassLoader();
1937748cd8SNickeau *
2037748cd8SNickeau *     // register classes with namespaces
2137748cd8SNickeau *     $loader->add('Symfony\Component', __DIR__.'/component');
2237748cd8SNickeau *     $loader->add('Symfony',           __DIR__.'/framework');
2337748cd8SNickeau *
2437748cd8SNickeau *     // activate the autoloader
2537748cd8SNickeau *     $loader->register();
2637748cd8SNickeau *
2737748cd8SNickeau *     // to enable searching the include path (eg. for PEAR packages)
2837748cd8SNickeau *     $loader->setUseIncludePath(true);
2937748cd8SNickeau *
3037748cd8SNickeau * In this example, if you try to use a class in the Symfony\Component
3137748cd8SNickeau * namespace or one of its children (Symfony\Component\Console for instance),
3237748cd8SNickeau * the autoloader will first look for the class under the component/
3337748cd8SNickeau * directory, and it will then fallback to the framework/ directory if not
3437748cd8SNickeau * found before giving up.
3537748cd8SNickeau *
3637748cd8SNickeau * This class is loosely based on the Symfony UniversalClassLoader.
3737748cd8SNickeau *
3837748cd8SNickeau * @author Fabien Potencier <fabien@symfony.com>
3937748cd8SNickeau * @author Jordi Boggiano <j.boggiano@seld.be>
4037748cd8SNickeau * @see    https://www.php-fig.org/psr/psr-0/
4137748cd8SNickeau * @see    https://www.php-fig.org/psr/psr-4/
4237748cd8SNickeau */
4337748cd8SNickeauclass ClassLoader
4437748cd8SNickeau{
45*83c68632SNico    /** @var \Closure(string):void */
46*83c68632SNico    private static $includeFile;
47*83c68632SNico
48*83c68632SNico    /** @var string|null */
4937748cd8SNickeau    private $vendorDir;
5037748cd8SNickeau
5137748cd8SNickeau    // PSR-4
52*83c68632SNico    /**
53*83c68632SNico     * @var array<string, array<string, int>>
54*83c68632SNico     */
5537748cd8SNickeau    private $prefixLengthsPsr4 = array();
56*83c68632SNico    /**
57*83c68632SNico     * @var array<string, list<string>>
58*83c68632SNico     */
5937748cd8SNickeau    private $prefixDirsPsr4 = array();
60*83c68632SNico    /**
61*83c68632SNico     * @var list<string>
62*83c68632SNico     */
6337748cd8SNickeau    private $fallbackDirsPsr4 = array();
6437748cd8SNickeau
6537748cd8SNickeau    // PSR-0
66*83c68632SNico    /**
67*83c68632SNico     * List of PSR-0 prefixes
68*83c68632SNico     *
69*83c68632SNico     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
70*83c68632SNico     *
71*83c68632SNico     * @var array<string, array<string, list<string>>>
72*83c68632SNico     */
7337748cd8SNickeau    private $prefixesPsr0 = array();
74*83c68632SNico    /**
75*83c68632SNico     * @var list<string>
76*83c68632SNico     */
7737748cd8SNickeau    private $fallbackDirsPsr0 = array();
7837748cd8SNickeau
79*83c68632SNico    /** @var bool */
8037748cd8SNickeau    private $useIncludePath = false;
81*83c68632SNico
82*83c68632SNico    /**
83*83c68632SNico     * @var array<string, string>
84*83c68632SNico     */
8537748cd8SNickeau    private $classMap = array();
86*83c68632SNico
87*83c68632SNico    /** @var bool */
8837748cd8SNickeau    private $classMapAuthoritative = false;
89*83c68632SNico
90*83c68632SNico    /**
91*83c68632SNico     * @var array<string, bool>
92*83c68632SNico     */
9337748cd8SNickeau    private $missingClasses = array();
94*83c68632SNico
95*83c68632SNico    /** @var string|null */
9637748cd8SNickeau    private $apcuPrefix;
9737748cd8SNickeau
98*83c68632SNico    /**
99*83c68632SNico     * @var array<string, self>
100*83c68632SNico     */
10137748cd8SNickeau    private static $registeredLoaders = array();
10237748cd8SNickeau
103*83c68632SNico    /**
104*83c68632SNico     * @param string|null $vendorDir
105*83c68632SNico     */
10637748cd8SNickeau    public function __construct($vendorDir = null)
10737748cd8SNickeau    {
10837748cd8SNickeau        $this->vendorDir = $vendorDir;
109*83c68632SNico        self::initializeIncludeClosure();
11037748cd8SNickeau    }
11137748cd8SNickeau
112*83c68632SNico    /**
113*83c68632SNico     * @return array<string, list<string>>
114*83c68632SNico     */
11537748cd8SNickeau    public function getPrefixes()
11637748cd8SNickeau    {
11737748cd8SNickeau        if (!empty($this->prefixesPsr0)) {
11837748cd8SNickeau            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
11937748cd8SNickeau        }
12037748cd8SNickeau
12137748cd8SNickeau        return array();
12237748cd8SNickeau    }
12337748cd8SNickeau
124*83c68632SNico    /**
125*83c68632SNico     * @return array<string, list<string>>
126*83c68632SNico     */
12737748cd8SNickeau    public function getPrefixesPsr4()
12837748cd8SNickeau    {
12937748cd8SNickeau        return $this->prefixDirsPsr4;
13037748cd8SNickeau    }
13137748cd8SNickeau
132*83c68632SNico    /**
133*83c68632SNico     * @return list<string>
134*83c68632SNico     */
13537748cd8SNickeau    public function getFallbackDirs()
13637748cd8SNickeau    {
13737748cd8SNickeau        return $this->fallbackDirsPsr0;
13837748cd8SNickeau    }
13937748cd8SNickeau
140*83c68632SNico    /**
141*83c68632SNico     * @return list<string>
142*83c68632SNico     */
14337748cd8SNickeau    public function getFallbackDirsPsr4()
14437748cd8SNickeau    {
14537748cd8SNickeau        return $this->fallbackDirsPsr4;
14637748cd8SNickeau    }
14737748cd8SNickeau
148*83c68632SNico    /**
149*83c68632SNico     * @return array<string, string> Array of classname => path
150*83c68632SNico     */
15137748cd8SNickeau    public function getClassMap()
15237748cd8SNickeau    {
15337748cd8SNickeau        return $this->classMap;
15437748cd8SNickeau    }
15537748cd8SNickeau
15637748cd8SNickeau    /**
157*83c68632SNico     * @param array<string, string> $classMap Class to filename map
158*83c68632SNico     *
159*83c68632SNico     * @return void
16037748cd8SNickeau     */
16137748cd8SNickeau    public function addClassMap(array $classMap)
16237748cd8SNickeau    {
16337748cd8SNickeau        if ($this->classMap) {
16437748cd8SNickeau            $this->classMap = array_merge($this->classMap, $classMap);
16537748cd8SNickeau        } else {
16637748cd8SNickeau            $this->classMap = $classMap;
16737748cd8SNickeau        }
16837748cd8SNickeau    }
16937748cd8SNickeau
17037748cd8SNickeau    /**
17137748cd8SNickeau     * Registers a set of PSR-0 directories for a given prefix, either
17237748cd8SNickeau     * appending or prepending to the ones previously set for this prefix.
17337748cd8SNickeau     *
17437748cd8SNickeau     * @param string              $prefix  The prefix
175*83c68632SNico     * @param list<string>|string $paths   The PSR-0 root directories
17637748cd8SNickeau     * @param bool                $prepend Whether to prepend the directories
177*83c68632SNico     *
178*83c68632SNico     * @return void
17937748cd8SNickeau     */
18037748cd8SNickeau    public function add($prefix, $paths, $prepend = false)
18137748cd8SNickeau    {
182*83c68632SNico        $paths = (array) $paths;
18337748cd8SNickeau        if (!$prefix) {
18437748cd8SNickeau            if ($prepend) {
18537748cd8SNickeau                $this->fallbackDirsPsr0 = array_merge(
186*83c68632SNico                    $paths,
18737748cd8SNickeau                    $this->fallbackDirsPsr0
18837748cd8SNickeau                );
18937748cd8SNickeau            } else {
19037748cd8SNickeau                $this->fallbackDirsPsr0 = array_merge(
19137748cd8SNickeau                    $this->fallbackDirsPsr0,
192*83c68632SNico                    $paths
19337748cd8SNickeau                );
19437748cd8SNickeau            }
19537748cd8SNickeau
19637748cd8SNickeau            return;
19737748cd8SNickeau        }
19837748cd8SNickeau
19937748cd8SNickeau        $first = $prefix[0];
20037748cd8SNickeau        if (!isset($this->prefixesPsr0[$first][$prefix])) {
201*83c68632SNico            $this->prefixesPsr0[$first][$prefix] = $paths;
20237748cd8SNickeau
20337748cd8SNickeau            return;
20437748cd8SNickeau        }
20537748cd8SNickeau        if ($prepend) {
20637748cd8SNickeau            $this->prefixesPsr0[$first][$prefix] = array_merge(
207*83c68632SNico                $paths,
20837748cd8SNickeau                $this->prefixesPsr0[$first][$prefix]
20937748cd8SNickeau            );
21037748cd8SNickeau        } else {
21137748cd8SNickeau            $this->prefixesPsr0[$first][$prefix] = array_merge(
21237748cd8SNickeau                $this->prefixesPsr0[$first][$prefix],
213*83c68632SNico                $paths
21437748cd8SNickeau            );
21537748cd8SNickeau        }
21637748cd8SNickeau    }
21737748cd8SNickeau
21837748cd8SNickeau    /**
21937748cd8SNickeau     * Registers a set of PSR-4 directories for a given namespace, either
22037748cd8SNickeau     * appending or prepending to the ones previously set for this namespace.
22137748cd8SNickeau     *
22237748cd8SNickeau     * @param string              $prefix  The prefix/namespace, with trailing '\\'
223*83c68632SNico     * @param list<string>|string $paths   The PSR-4 base directories
22437748cd8SNickeau     * @param bool                $prepend Whether to prepend the directories
22537748cd8SNickeau     *
22637748cd8SNickeau     * @throws \InvalidArgumentException
227*83c68632SNico     *
228*83c68632SNico     * @return void
22937748cd8SNickeau     */
23037748cd8SNickeau    public function addPsr4($prefix, $paths, $prepend = false)
23137748cd8SNickeau    {
232*83c68632SNico        $paths = (array) $paths;
23337748cd8SNickeau        if (!$prefix) {
23437748cd8SNickeau            // Register directories for the root namespace.
23537748cd8SNickeau            if ($prepend) {
23637748cd8SNickeau                $this->fallbackDirsPsr4 = array_merge(
237*83c68632SNico                    $paths,
23837748cd8SNickeau                    $this->fallbackDirsPsr4
23937748cd8SNickeau                );
24037748cd8SNickeau            } else {
24137748cd8SNickeau                $this->fallbackDirsPsr4 = array_merge(
24237748cd8SNickeau                    $this->fallbackDirsPsr4,
243*83c68632SNico                    $paths
24437748cd8SNickeau                );
24537748cd8SNickeau            }
24637748cd8SNickeau        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
24737748cd8SNickeau            // Register directories for a new namespace.
24837748cd8SNickeau            $length = strlen($prefix);
24937748cd8SNickeau            if ('\\' !== $prefix[$length - 1]) {
25037748cd8SNickeau                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
25137748cd8SNickeau            }
25237748cd8SNickeau            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
253*83c68632SNico            $this->prefixDirsPsr4[$prefix] = $paths;
25437748cd8SNickeau        } elseif ($prepend) {
25537748cd8SNickeau            // Prepend directories for an already registered namespace.
25637748cd8SNickeau            $this->prefixDirsPsr4[$prefix] = array_merge(
257*83c68632SNico                $paths,
25837748cd8SNickeau                $this->prefixDirsPsr4[$prefix]
25937748cd8SNickeau            );
26037748cd8SNickeau        } else {
26137748cd8SNickeau            // Append directories for an already registered namespace.
26237748cd8SNickeau            $this->prefixDirsPsr4[$prefix] = array_merge(
26337748cd8SNickeau                $this->prefixDirsPsr4[$prefix],
264*83c68632SNico                $paths
26537748cd8SNickeau            );
26637748cd8SNickeau        }
26737748cd8SNickeau    }
26837748cd8SNickeau
26937748cd8SNickeau    /**
27037748cd8SNickeau     * Registers a set of PSR-0 directories for a given prefix,
27137748cd8SNickeau     * replacing any others previously set for this prefix.
27237748cd8SNickeau     *
27337748cd8SNickeau     * @param string              $prefix The prefix
274*83c68632SNico     * @param list<string>|string $paths  The PSR-0 base directories
275*83c68632SNico     *
276*83c68632SNico     * @return void
27737748cd8SNickeau     */
27837748cd8SNickeau    public function set($prefix, $paths)
27937748cd8SNickeau    {
28037748cd8SNickeau        if (!$prefix) {
28137748cd8SNickeau            $this->fallbackDirsPsr0 = (array) $paths;
28237748cd8SNickeau        } else {
28337748cd8SNickeau            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
28437748cd8SNickeau        }
28537748cd8SNickeau    }
28637748cd8SNickeau
28737748cd8SNickeau    /**
28837748cd8SNickeau     * Registers a set of PSR-4 directories for a given namespace,
28937748cd8SNickeau     * replacing any others previously set for this namespace.
29037748cd8SNickeau     *
29137748cd8SNickeau     * @param string              $prefix The prefix/namespace, with trailing '\\'
292*83c68632SNico     * @param list<string>|string $paths  The PSR-4 base directories
29337748cd8SNickeau     *
29437748cd8SNickeau     * @throws \InvalidArgumentException
295*83c68632SNico     *
296*83c68632SNico     * @return void
29737748cd8SNickeau     */
29837748cd8SNickeau    public function setPsr4($prefix, $paths)
29937748cd8SNickeau    {
30037748cd8SNickeau        if (!$prefix) {
30137748cd8SNickeau            $this->fallbackDirsPsr4 = (array) $paths;
30237748cd8SNickeau        } else {
30337748cd8SNickeau            $length = strlen($prefix);
30437748cd8SNickeau            if ('\\' !== $prefix[$length - 1]) {
30537748cd8SNickeau                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
30637748cd8SNickeau            }
30737748cd8SNickeau            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
30837748cd8SNickeau            $this->prefixDirsPsr4[$prefix] = (array) $paths;
30937748cd8SNickeau        }
31037748cd8SNickeau    }
31137748cd8SNickeau
31237748cd8SNickeau    /**
31337748cd8SNickeau     * Turns on searching the include path for class files.
31437748cd8SNickeau     *
31537748cd8SNickeau     * @param bool $useIncludePath
316*83c68632SNico     *
317*83c68632SNico     * @return void
31837748cd8SNickeau     */
31937748cd8SNickeau    public function setUseIncludePath($useIncludePath)
32037748cd8SNickeau    {
32137748cd8SNickeau        $this->useIncludePath = $useIncludePath;
32237748cd8SNickeau    }
32337748cd8SNickeau
32437748cd8SNickeau    /**
32537748cd8SNickeau     * Can be used to check if the autoloader uses the include path to check
32637748cd8SNickeau     * for classes.
32737748cd8SNickeau     *
32837748cd8SNickeau     * @return bool
32937748cd8SNickeau     */
33037748cd8SNickeau    public function getUseIncludePath()
33137748cd8SNickeau    {
33237748cd8SNickeau        return $this->useIncludePath;
33337748cd8SNickeau    }
33437748cd8SNickeau
33537748cd8SNickeau    /**
33637748cd8SNickeau     * Turns off searching the prefix and fallback directories for classes
33737748cd8SNickeau     * that have not been registered with the class map.
33837748cd8SNickeau     *
33937748cd8SNickeau     * @param bool $classMapAuthoritative
340*83c68632SNico     *
341*83c68632SNico     * @return void
34237748cd8SNickeau     */
34337748cd8SNickeau    public function setClassMapAuthoritative($classMapAuthoritative)
34437748cd8SNickeau    {
34537748cd8SNickeau        $this->classMapAuthoritative = $classMapAuthoritative;
34637748cd8SNickeau    }
34737748cd8SNickeau
34837748cd8SNickeau    /**
34937748cd8SNickeau     * Should class lookup fail if not found in the current class map?
35037748cd8SNickeau     *
35137748cd8SNickeau     * @return bool
35237748cd8SNickeau     */
35337748cd8SNickeau    public function isClassMapAuthoritative()
35437748cd8SNickeau    {
35537748cd8SNickeau        return $this->classMapAuthoritative;
35637748cd8SNickeau    }
35737748cd8SNickeau
35837748cd8SNickeau    /**
35937748cd8SNickeau     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
36037748cd8SNickeau     *
36137748cd8SNickeau     * @param string|null $apcuPrefix
362*83c68632SNico     *
363*83c68632SNico     * @return void
36437748cd8SNickeau     */
36537748cd8SNickeau    public function setApcuPrefix($apcuPrefix)
36637748cd8SNickeau    {
36737748cd8SNickeau        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
36837748cd8SNickeau    }
36937748cd8SNickeau
37037748cd8SNickeau    /**
37137748cd8SNickeau     * The APCu prefix in use, or null if APCu caching is not enabled.
37237748cd8SNickeau     *
37337748cd8SNickeau     * @return string|null
37437748cd8SNickeau     */
37537748cd8SNickeau    public function getApcuPrefix()
37637748cd8SNickeau    {
37737748cd8SNickeau        return $this->apcuPrefix;
37837748cd8SNickeau    }
37937748cd8SNickeau
38037748cd8SNickeau    /**
38137748cd8SNickeau     * Registers this instance as an autoloader.
38237748cd8SNickeau     *
38337748cd8SNickeau     * @param bool $prepend Whether to prepend the autoloader or not
384*83c68632SNico     *
385*83c68632SNico     * @return void
38637748cd8SNickeau     */
38737748cd8SNickeau    public function register($prepend = false)
38837748cd8SNickeau    {
38937748cd8SNickeau        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
39037748cd8SNickeau
39137748cd8SNickeau        if (null === $this->vendorDir) {
39237748cd8SNickeau            return;
39337748cd8SNickeau        }
39437748cd8SNickeau
39537748cd8SNickeau        if ($prepend) {
39637748cd8SNickeau            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
39737748cd8SNickeau        } else {
39837748cd8SNickeau            unset(self::$registeredLoaders[$this->vendorDir]);
39937748cd8SNickeau            self::$registeredLoaders[$this->vendorDir] = $this;
40037748cd8SNickeau        }
40137748cd8SNickeau    }
40237748cd8SNickeau
40337748cd8SNickeau    /**
40437748cd8SNickeau     * Unregisters this instance as an autoloader.
405*83c68632SNico     *
406*83c68632SNico     * @return void
40737748cd8SNickeau     */
40837748cd8SNickeau    public function unregister()
40937748cd8SNickeau    {
41037748cd8SNickeau        spl_autoload_unregister(array($this, 'loadClass'));
41137748cd8SNickeau
41237748cd8SNickeau        if (null !== $this->vendorDir) {
41337748cd8SNickeau            unset(self::$registeredLoaders[$this->vendorDir]);
41437748cd8SNickeau        }
41537748cd8SNickeau    }
41637748cd8SNickeau
41737748cd8SNickeau    /**
41837748cd8SNickeau     * Loads the given class or interface.
41937748cd8SNickeau     *
42037748cd8SNickeau     * @param  string    $class The name of the class
421*83c68632SNico     * @return true|null True if loaded, null otherwise
42237748cd8SNickeau     */
42337748cd8SNickeau    public function loadClass($class)
42437748cd8SNickeau    {
42537748cd8SNickeau        if ($file = $this->findFile($class)) {
426*83c68632SNico            $includeFile = self::$includeFile;
427*83c68632SNico            $includeFile($file);
42837748cd8SNickeau
42937748cd8SNickeau            return true;
43037748cd8SNickeau        }
431*83c68632SNico
432*83c68632SNico        return null;
43337748cd8SNickeau    }
43437748cd8SNickeau
43537748cd8SNickeau    /**
43637748cd8SNickeau     * Finds the path to the file where the class is defined.
43737748cd8SNickeau     *
43837748cd8SNickeau     * @param string $class The name of the class
43937748cd8SNickeau     *
44037748cd8SNickeau     * @return string|false The path if found, false otherwise
44137748cd8SNickeau     */
44237748cd8SNickeau    public function findFile($class)
44337748cd8SNickeau    {
44437748cd8SNickeau        // class map lookup
44537748cd8SNickeau        if (isset($this->classMap[$class])) {
44637748cd8SNickeau            return $this->classMap[$class];
44737748cd8SNickeau        }
44837748cd8SNickeau        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
44937748cd8SNickeau            return false;
45037748cd8SNickeau        }
45137748cd8SNickeau        if (null !== $this->apcuPrefix) {
45237748cd8SNickeau            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
45337748cd8SNickeau            if ($hit) {
45437748cd8SNickeau                return $file;
45537748cd8SNickeau            }
45637748cd8SNickeau        }
45737748cd8SNickeau
45837748cd8SNickeau        $file = $this->findFileWithExtension($class, '.php');
45937748cd8SNickeau
46037748cd8SNickeau        // Search for Hack files if we are running on HHVM
46137748cd8SNickeau        if (false === $file && defined('HHVM_VERSION')) {
46237748cd8SNickeau            $file = $this->findFileWithExtension($class, '.hh');
46337748cd8SNickeau        }
46437748cd8SNickeau
46537748cd8SNickeau        if (null !== $this->apcuPrefix) {
46637748cd8SNickeau            apcu_add($this->apcuPrefix.$class, $file);
46737748cd8SNickeau        }
46837748cd8SNickeau
46937748cd8SNickeau        if (false === $file) {
47037748cd8SNickeau            // Remember that this class does not exist.
47137748cd8SNickeau            $this->missingClasses[$class] = true;
47237748cd8SNickeau        }
47337748cd8SNickeau
47437748cd8SNickeau        return $file;
47537748cd8SNickeau    }
47637748cd8SNickeau
47737748cd8SNickeau    /**
478*83c68632SNico     * Returns the currently registered loaders keyed by their corresponding vendor directories.
47937748cd8SNickeau     *
480*83c68632SNico     * @return array<string, self>
48137748cd8SNickeau     */
48237748cd8SNickeau    public static function getRegisteredLoaders()
48337748cd8SNickeau    {
48437748cd8SNickeau        return self::$registeredLoaders;
48537748cd8SNickeau    }
48637748cd8SNickeau
487*83c68632SNico    /**
488*83c68632SNico     * @param  string       $class
489*83c68632SNico     * @param  string       $ext
490*83c68632SNico     * @return string|false
491*83c68632SNico     */
49237748cd8SNickeau    private function findFileWithExtension($class, $ext)
49337748cd8SNickeau    {
49437748cd8SNickeau        // PSR-4 lookup
49537748cd8SNickeau        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
49637748cd8SNickeau
49737748cd8SNickeau        $first = $class[0];
49837748cd8SNickeau        if (isset($this->prefixLengthsPsr4[$first])) {
49937748cd8SNickeau            $subPath = $class;
50037748cd8SNickeau            while (false !== $lastPos = strrpos($subPath, '\\')) {
50137748cd8SNickeau                $subPath = substr($subPath, 0, $lastPos);
50237748cd8SNickeau                $search = $subPath . '\\';
50337748cd8SNickeau                if (isset($this->prefixDirsPsr4[$search])) {
50437748cd8SNickeau                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
50537748cd8SNickeau                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
50637748cd8SNickeau                        if (file_exists($file = $dir . $pathEnd)) {
50737748cd8SNickeau                            return $file;
50837748cd8SNickeau                        }
50937748cd8SNickeau                    }
51037748cd8SNickeau                }
51137748cd8SNickeau            }
51237748cd8SNickeau        }
51337748cd8SNickeau
51437748cd8SNickeau        // PSR-4 fallback dirs
51537748cd8SNickeau        foreach ($this->fallbackDirsPsr4 as $dir) {
51637748cd8SNickeau            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
51737748cd8SNickeau                return $file;
51837748cd8SNickeau            }
51937748cd8SNickeau        }
52037748cd8SNickeau
52137748cd8SNickeau        // PSR-0 lookup
52237748cd8SNickeau        if (false !== $pos = strrpos($class, '\\')) {
52337748cd8SNickeau            // namespaced class name
52437748cd8SNickeau            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
52537748cd8SNickeau                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
52637748cd8SNickeau        } else {
52737748cd8SNickeau            // PEAR-like class name
52837748cd8SNickeau            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
52937748cd8SNickeau        }
53037748cd8SNickeau
53137748cd8SNickeau        if (isset($this->prefixesPsr0[$first])) {
53237748cd8SNickeau            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
53337748cd8SNickeau                if (0 === strpos($class, $prefix)) {
53437748cd8SNickeau                    foreach ($dirs as $dir) {
53537748cd8SNickeau                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
53637748cd8SNickeau                            return $file;
53737748cd8SNickeau                        }
53837748cd8SNickeau                    }
53937748cd8SNickeau                }
54037748cd8SNickeau            }
54137748cd8SNickeau        }
54237748cd8SNickeau
54337748cd8SNickeau        // PSR-0 fallback dirs
54437748cd8SNickeau        foreach ($this->fallbackDirsPsr0 as $dir) {
54537748cd8SNickeau            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
54637748cd8SNickeau                return $file;
54737748cd8SNickeau            }
54837748cd8SNickeau        }
54937748cd8SNickeau
55037748cd8SNickeau        // PSR-0 include paths.
55137748cd8SNickeau        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
55237748cd8SNickeau            return $file;
55337748cd8SNickeau        }
55437748cd8SNickeau
55537748cd8SNickeau        return false;
55637748cd8SNickeau    }
557*83c68632SNico
558*83c68632SNico    /**
559*83c68632SNico     * @return void
560*83c68632SNico     */
561*83c68632SNico    private static function initializeIncludeClosure()
562*83c68632SNico    {
563*83c68632SNico        if (self::$includeFile !== null) {
564*83c68632SNico            return;
56537748cd8SNickeau        }
56637748cd8SNickeau
56737748cd8SNickeau        /**
56837748cd8SNickeau         * Scope isolated include.
56937748cd8SNickeau         *
57037748cd8SNickeau         * Prevents access to $this/self from included files.
571*83c68632SNico         *
572*83c68632SNico         * @param  string $file
573*83c68632SNico         * @return void
57437748cd8SNickeau         */
575*83c68632SNico        self::$includeFile = \Closure::bind(static function($file) {
57637748cd8SNickeau            include $file;
577*83c68632SNico        }, null, null);
578*83c68632SNico    }
57937748cd8SNickeau}
580