xref: /dokuwiki/vendor/composer/ClassLoader.php (revision 6cb05674d342c24351edf98467e681cbaabf9d0b)
1605f8e8dSAndreas Gohr<?php
2605f8e8dSAndreas Gohr
3605f8e8dSAndreas Gohr/*
4605f8e8dSAndreas Gohr * This file is part of Composer.
5605f8e8dSAndreas Gohr *
6605f8e8dSAndreas Gohr * (c) Nils Adermann <naderman@naderman.de>
7605f8e8dSAndreas Gohr *     Jordi Boggiano <j.boggiano@seld.be>
8605f8e8dSAndreas Gohr *
9605f8e8dSAndreas Gohr * For the full copyright and license information, please view the LICENSE
10605f8e8dSAndreas Gohr * file that was distributed with this source code.
11605f8e8dSAndreas Gohr */
12605f8e8dSAndreas Gohr
13605f8e8dSAndreas Gohrnamespace Composer\Autoload;
14605f8e8dSAndreas Gohr
15605f8e8dSAndreas Gohr/**
167a33d2f8SNiklas Keller * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17605f8e8dSAndreas Gohr *
18605f8e8dSAndreas Gohr *     $loader = new \Composer\Autoload\ClassLoader();
19605f8e8dSAndreas Gohr *
20605f8e8dSAndreas Gohr *     // register classes with namespaces
21605f8e8dSAndreas Gohr *     $loader->add('Symfony\Component', __DIR__.'/component');
22605f8e8dSAndreas Gohr *     $loader->add('Symfony',           __DIR__.'/framework');
23605f8e8dSAndreas Gohr *
24605f8e8dSAndreas Gohr *     // activate the autoloader
25605f8e8dSAndreas Gohr *     $loader->register();
26605f8e8dSAndreas Gohr *
27605f8e8dSAndreas Gohr *     // to enable searching the include path (eg. for PEAR packages)
28605f8e8dSAndreas Gohr *     $loader->setUseIncludePath(true);
29605f8e8dSAndreas Gohr *
30605f8e8dSAndreas Gohr * In this example, if you try to use a class in the Symfony\Component
31605f8e8dSAndreas Gohr * namespace or one of its children (Symfony\Component\Console for instance),
32605f8e8dSAndreas Gohr * the autoloader will first look for the class under the component/
33605f8e8dSAndreas Gohr * directory, and it will then fallback to the framework/ directory if not
34605f8e8dSAndreas Gohr * found before giving up.
35605f8e8dSAndreas Gohr *
36605f8e8dSAndreas Gohr * This class is loosely based on the Symfony UniversalClassLoader.
37605f8e8dSAndreas Gohr *
38605f8e8dSAndreas Gohr * @author Fabien Potencier <fabien@symfony.com>
39605f8e8dSAndreas Gohr * @author Jordi Boggiano <j.boggiano@seld.be>
40*6cb05674SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-0/
41*6cb05674SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-4/
42605f8e8dSAndreas Gohr */
43605f8e8dSAndreas Gohrclass ClassLoader
44605f8e8dSAndreas Gohr{
45*6cb05674SAndreas Gohr    private $vendorDir;
46*6cb05674SAndreas Gohr
47605f8e8dSAndreas Gohr    // PSR-4
48605f8e8dSAndreas Gohr    private $prefixLengthsPsr4 = array();
49605f8e8dSAndreas Gohr    private $prefixDirsPsr4 = array();
50605f8e8dSAndreas Gohr    private $fallbackDirsPsr4 = array();
51605f8e8dSAndreas Gohr
52605f8e8dSAndreas Gohr    // PSR-0
53605f8e8dSAndreas Gohr    private $prefixesPsr0 = array();
54605f8e8dSAndreas Gohr    private $fallbackDirsPsr0 = array();
55605f8e8dSAndreas Gohr
56605f8e8dSAndreas Gohr    private $useIncludePath = false;
57605f8e8dSAndreas Gohr    private $classMap = array();
58605f8e8dSAndreas Gohr    private $classMapAuthoritative = false;
597a33d2f8SNiklas Keller    private $missingClasses = array();
60e0dd796dSAndreas Gohr    private $apcuPrefix;
61605f8e8dSAndreas Gohr
62*6cb05674SAndreas Gohr    private static $registeredLoaders = array();
63*6cb05674SAndreas Gohr
64*6cb05674SAndreas Gohr    public function __construct($vendorDir = null)
65*6cb05674SAndreas Gohr    {
66*6cb05674SAndreas Gohr        $this->vendorDir = $vendorDir;
67*6cb05674SAndreas Gohr    }
68*6cb05674SAndreas Gohr
69605f8e8dSAndreas Gohr    public function getPrefixes()
70605f8e8dSAndreas Gohr    {
71605f8e8dSAndreas Gohr        if (!empty($this->prefixesPsr0)) {
72a3bfbb3cSAndreas Gohr            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
73605f8e8dSAndreas Gohr        }
74605f8e8dSAndreas Gohr
75605f8e8dSAndreas Gohr        return array();
76605f8e8dSAndreas Gohr    }
77605f8e8dSAndreas Gohr
78605f8e8dSAndreas Gohr    public function getPrefixesPsr4()
79605f8e8dSAndreas Gohr    {
80605f8e8dSAndreas Gohr        return $this->prefixDirsPsr4;
81605f8e8dSAndreas Gohr    }
82605f8e8dSAndreas Gohr
83605f8e8dSAndreas Gohr    public function getFallbackDirs()
84605f8e8dSAndreas Gohr    {
85605f8e8dSAndreas Gohr        return $this->fallbackDirsPsr0;
86605f8e8dSAndreas Gohr    }
87605f8e8dSAndreas Gohr
88605f8e8dSAndreas Gohr    public function getFallbackDirsPsr4()
89605f8e8dSAndreas Gohr    {
90605f8e8dSAndreas Gohr        return $this->fallbackDirsPsr4;
91605f8e8dSAndreas Gohr    }
92605f8e8dSAndreas Gohr
93605f8e8dSAndreas Gohr    public function getClassMap()
94605f8e8dSAndreas Gohr    {
95605f8e8dSAndreas Gohr        return $this->classMap;
96605f8e8dSAndreas Gohr    }
97605f8e8dSAndreas Gohr
98605f8e8dSAndreas Gohr    /**
99605f8e8dSAndreas Gohr     * @param array $classMap Class to filename map
100605f8e8dSAndreas Gohr     */
101605f8e8dSAndreas Gohr    public function addClassMap(array $classMap)
102605f8e8dSAndreas Gohr    {
103605f8e8dSAndreas Gohr        if ($this->classMap) {
104605f8e8dSAndreas Gohr            $this->classMap = array_merge($this->classMap, $classMap);
105605f8e8dSAndreas Gohr        } else {
106605f8e8dSAndreas Gohr            $this->classMap = $classMap;
107605f8e8dSAndreas Gohr        }
108605f8e8dSAndreas Gohr    }
109605f8e8dSAndreas Gohr
110605f8e8dSAndreas Gohr    /**
111605f8e8dSAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix, either
112605f8e8dSAndreas Gohr     * appending or prepending to the ones previously set for this prefix.
113605f8e8dSAndreas Gohr     *
114605f8e8dSAndreas Gohr     * @param string       $prefix  The prefix
115605f8e8dSAndreas Gohr     * @param array|string $paths   The PSR-0 root directories
116605f8e8dSAndreas Gohr     * @param bool         $prepend Whether to prepend the directories
117605f8e8dSAndreas Gohr     */
118605f8e8dSAndreas Gohr    public function add($prefix, $paths, $prepend = false)
119605f8e8dSAndreas Gohr    {
120605f8e8dSAndreas Gohr        if (!$prefix) {
121605f8e8dSAndreas Gohr            if ($prepend) {
122605f8e8dSAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
123605f8e8dSAndreas Gohr                    (array) $paths,
124605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr0
125605f8e8dSAndreas Gohr                );
126605f8e8dSAndreas Gohr            } else {
127605f8e8dSAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
128605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr0,
129605f8e8dSAndreas Gohr                    (array) $paths
130605f8e8dSAndreas Gohr                );
131605f8e8dSAndreas Gohr            }
132605f8e8dSAndreas Gohr
133605f8e8dSAndreas Gohr            return;
134605f8e8dSAndreas Gohr        }
135605f8e8dSAndreas Gohr
136605f8e8dSAndreas Gohr        $first = $prefix[0];
137605f8e8dSAndreas Gohr        if (!isset($this->prefixesPsr0[$first][$prefix])) {
138605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
139605f8e8dSAndreas Gohr
140605f8e8dSAndreas Gohr            return;
141605f8e8dSAndreas Gohr        }
142605f8e8dSAndreas Gohr        if ($prepend) {
143605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
144605f8e8dSAndreas Gohr                (array) $paths,
145605f8e8dSAndreas Gohr                $this->prefixesPsr0[$first][$prefix]
146605f8e8dSAndreas Gohr            );
147605f8e8dSAndreas Gohr        } else {
148605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
149605f8e8dSAndreas Gohr                $this->prefixesPsr0[$first][$prefix],
150605f8e8dSAndreas Gohr                (array) $paths
151605f8e8dSAndreas Gohr            );
152605f8e8dSAndreas Gohr        }
153605f8e8dSAndreas Gohr    }
154605f8e8dSAndreas Gohr
155605f8e8dSAndreas Gohr    /**
156605f8e8dSAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace, either
157605f8e8dSAndreas Gohr     * appending or prepending to the ones previously set for this namespace.
158605f8e8dSAndreas Gohr     *
159605f8e8dSAndreas Gohr     * @param string       $prefix  The prefix/namespace, with trailing '\\'
1607a33d2f8SNiklas Keller     * @param array|string $paths   The PSR-4 base directories
161605f8e8dSAndreas Gohr     * @param bool         $prepend Whether to prepend the directories
162605f8e8dSAndreas Gohr     *
163605f8e8dSAndreas Gohr     * @throws \InvalidArgumentException
164605f8e8dSAndreas Gohr     */
165605f8e8dSAndreas Gohr    public function addPsr4($prefix, $paths, $prepend = false)
166605f8e8dSAndreas Gohr    {
167605f8e8dSAndreas Gohr        if (!$prefix) {
168605f8e8dSAndreas Gohr            // Register directories for the root namespace.
169605f8e8dSAndreas Gohr            if ($prepend) {
170605f8e8dSAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
171605f8e8dSAndreas Gohr                    (array) $paths,
172605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr4
173605f8e8dSAndreas Gohr                );
174605f8e8dSAndreas Gohr            } else {
175605f8e8dSAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
176605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr4,
177605f8e8dSAndreas Gohr                    (array) $paths
178605f8e8dSAndreas Gohr                );
179605f8e8dSAndreas Gohr            }
180605f8e8dSAndreas Gohr        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
181605f8e8dSAndreas Gohr            // Register directories for a new namespace.
182605f8e8dSAndreas Gohr            $length = strlen($prefix);
183605f8e8dSAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
184605f8e8dSAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
185605f8e8dSAndreas Gohr            }
186605f8e8dSAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
187605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
188605f8e8dSAndreas Gohr        } elseif ($prepend) {
189605f8e8dSAndreas Gohr            // Prepend directories for an already registered namespace.
190605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
191605f8e8dSAndreas Gohr                (array) $paths,
192605f8e8dSAndreas Gohr                $this->prefixDirsPsr4[$prefix]
193605f8e8dSAndreas Gohr            );
194605f8e8dSAndreas Gohr        } else {
195605f8e8dSAndreas Gohr            // Append directories for an already registered namespace.
196605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
197605f8e8dSAndreas Gohr                $this->prefixDirsPsr4[$prefix],
198605f8e8dSAndreas Gohr                (array) $paths
199605f8e8dSAndreas Gohr            );
200605f8e8dSAndreas Gohr        }
201605f8e8dSAndreas Gohr    }
202605f8e8dSAndreas Gohr
203605f8e8dSAndreas Gohr    /**
204605f8e8dSAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix,
205605f8e8dSAndreas Gohr     * replacing any others previously set for this prefix.
206605f8e8dSAndreas Gohr     *
207605f8e8dSAndreas Gohr     * @param string       $prefix The prefix
208605f8e8dSAndreas Gohr     * @param array|string $paths  The PSR-0 base directories
209605f8e8dSAndreas Gohr     */
210605f8e8dSAndreas Gohr    public function set($prefix, $paths)
211605f8e8dSAndreas Gohr    {
212605f8e8dSAndreas Gohr        if (!$prefix) {
213605f8e8dSAndreas Gohr            $this->fallbackDirsPsr0 = (array) $paths;
214605f8e8dSAndreas Gohr        } else {
215605f8e8dSAndreas Gohr            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
216605f8e8dSAndreas Gohr        }
217605f8e8dSAndreas Gohr    }
218605f8e8dSAndreas Gohr
219605f8e8dSAndreas Gohr    /**
220605f8e8dSAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace,
221605f8e8dSAndreas Gohr     * replacing any others previously set for this namespace.
222605f8e8dSAndreas Gohr     *
223605f8e8dSAndreas Gohr     * @param string       $prefix The prefix/namespace, with trailing '\\'
224605f8e8dSAndreas Gohr     * @param array|string $paths  The PSR-4 base directories
225605f8e8dSAndreas Gohr     *
226605f8e8dSAndreas Gohr     * @throws \InvalidArgumentException
227605f8e8dSAndreas Gohr     */
228605f8e8dSAndreas Gohr    public function setPsr4($prefix, $paths)
229605f8e8dSAndreas Gohr    {
230605f8e8dSAndreas Gohr        if (!$prefix) {
231605f8e8dSAndreas Gohr            $this->fallbackDirsPsr4 = (array) $paths;
232605f8e8dSAndreas Gohr        } else {
233605f8e8dSAndreas Gohr            $length = strlen($prefix);
234605f8e8dSAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
235605f8e8dSAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
236605f8e8dSAndreas Gohr            }
237605f8e8dSAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
238605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
239605f8e8dSAndreas Gohr        }
240605f8e8dSAndreas Gohr    }
241605f8e8dSAndreas Gohr
242605f8e8dSAndreas Gohr    /**
243605f8e8dSAndreas Gohr     * Turns on searching the include path for class files.
244605f8e8dSAndreas Gohr     *
245605f8e8dSAndreas Gohr     * @param bool $useIncludePath
246605f8e8dSAndreas Gohr     */
247605f8e8dSAndreas Gohr    public function setUseIncludePath($useIncludePath)
248605f8e8dSAndreas Gohr    {
249605f8e8dSAndreas Gohr        $this->useIncludePath = $useIncludePath;
250605f8e8dSAndreas Gohr    }
251605f8e8dSAndreas Gohr
252605f8e8dSAndreas Gohr    /**
253605f8e8dSAndreas Gohr     * Can be used to check if the autoloader uses the include path to check
254605f8e8dSAndreas Gohr     * for classes.
255605f8e8dSAndreas Gohr     *
256605f8e8dSAndreas Gohr     * @return bool
257605f8e8dSAndreas Gohr     */
258605f8e8dSAndreas Gohr    public function getUseIncludePath()
259605f8e8dSAndreas Gohr    {
260605f8e8dSAndreas Gohr        return $this->useIncludePath;
261605f8e8dSAndreas Gohr    }
262605f8e8dSAndreas Gohr
263605f8e8dSAndreas Gohr    /**
264605f8e8dSAndreas Gohr     * Turns off searching the prefix and fallback directories for classes
265605f8e8dSAndreas Gohr     * that have not been registered with the class map.
266605f8e8dSAndreas Gohr     *
267605f8e8dSAndreas Gohr     * @param bool $classMapAuthoritative
268605f8e8dSAndreas Gohr     */
269605f8e8dSAndreas Gohr    public function setClassMapAuthoritative($classMapAuthoritative)
270605f8e8dSAndreas Gohr    {
271605f8e8dSAndreas Gohr        $this->classMapAuthoritative = $classMapAuthoritative;
272605f8e8dSAndreas Gohr    }
273605f8e8dSAndreas Gohr
274605f8e8dSAndreas Gohr    /**
275605f8e8dSAndreas Gohr     * Should class lookup fail if not found in the current class map?
276605f8e8dSAndreas Gohr     *
277605f8e8dSAndreas Gohr     * @return bool
278605f8e8dSAndreas Gohr     */
279605f8e8dSAndreas Gohr    public function isClassMapAuthoritative()
280605f8e8dSAndreas Gohr    {
281605f8e8dSAndreas Gohr        return $this->classMapAuthoritative;
282605f8e8dSAndreas Gohr    }
283605f8e8dSAndreas Gohr
284605f8e8dSAndreas Gohr    /**
285e0dd796dSAndreas Gohr     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
286e0dd796dSAndreas Gohr     *
287e0dd796dSAndreas Gohr     * @param string|null $apcuPrefix
288e0dd796dSAndreas Gohr     */
289e0dd796dSAndreas Gohr    public function setApcuPrefix($apcuPrefix)
290e0dd796dSAndreas Gohr    {
291e43cd7e1SAndreas Gohr        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
292e0dd796dSAndreas Gohr    }
293e0dd796dSAndreas Gohr
294e0dd796dSAndreas Gohr    /**
295e0dd796dSAndreas Gohr     * The APCu prefix in use, or null if APCu caching is not enabled.
296e0dd796dSAndreas Gohr     *
297e0dd796dSAndreas Gohr     * @return string|null
298e0dd796dSAndreas Gohr     */
299e0dd796dSAndreas Gohr    public function getApcuPrefix()
300e0dd796dSAndreas Gohr    {
301e0dd796dSAndreas Gohr        return $this->apcuPrefix;
302e0dd796dSAndreas Gohr    }
303e0dd796dSAndreas Gohr
304e0dd796dSAndreas Gohr    /**
305605f8e8dSAndreas Gohr     * Registers this instance as an autoloader.
306605f8e8dSAndreas Gohr     *
307605f8e8dSAndreas Gohr     * @param bool $prepend Whether to prepend the autoloader or not
308605f8e8dSAndreas Gohr     */
309605f8e8dSAndreas Gohr    public function register($prepend = false)
310605f8e8dSAndreas Gohr    {
311605f8e8dSAndreas Gohr        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
312*6cb05674SAndreas Gohr
313*6cb05674SAndreas Gohr        if (null === $this->vendorDir) {
314*6cb05674SAndreas Gohr            return;
315*6cb05674SAndreas Gohr        }
316*6cb05674SAndreas Gohr
317*6cb05674SAndreas Gohr        if ($prepend) {
318*6cb05674SAndreas Gohr            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
319*6cb05674SAndreas Gohr        } else {
320*6cb05674SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
321*6cb05674SAndreas Gohr            self::$registeredLoaders[$this->vendorDir] = $this;
322*6cb05674SAndreas Gohr        }
323605f8e8dSAndreas Gohr    }
324605f8e8dSAndreas Gohr
325605f8e8dSAndreas Gohr    /**
326605f8e8dSAndreas Gohr     * Unregisters this instance as an autoloader.
327605f8e8dSAndreas Gohr     */
328605f8e8dSAndreas Gohr    public function unregister()
329605f8e8dSAndreas Gohr    {
330605f8e8dSAndreas Gohr        spl_autoload_unregister(array($this, 'loadClass'));
331*6cb05674SAndreas Gohr
332*6cb05674SAndreas Gohr        if (null !== $this->vendorDir) {
333*6cb05674SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
334*6cb05674SAndreas Gohr        }
335605f8e8dSAndreas Gohr    }
336605f8e8dSAndreas Gohr
337605f8e8dSAndreas Gohr    /**
338605f8e8dSAndreas Gohr     * Loads the given class or interface.
339605f8e8dSAndreas Gohr     *
340605f8e8dSAndreas Gohr     * @param  string    $class The name of the class
341605f8e8dSAndreas Gohr     * @return bool|null True if loaded, null otherwise
342605f8e8dSAndreas Gohr     */
343605f8e8dSAndreas Gohr    public function loadClass($class)
344605f8e8dSAndreas Gohr    {
345605f8e8dSAndreas Gohr        if ($file = $this->findFile($class)) {
346605f8e8dSAndreas Gohr            includeFile($file);
347605f8e8dSAndreas Gohr
348605f8e8dSAndreas Gohr            return true;
349605f8e8dSAndreas Gohr        }
350605f8e8dSAndreas Gohr    }
351605f8e8dSAndreas Gohr
352605f8e8dSAndreas Gohr    /**
353605f8e8dSAndreas Gohr     * Finds the path to the file where the class is defined.
354605f8e8dSAndreas Gohr     *
355605f8e8dSAndreas Gohr     * @param string $class The name of the class
356605f8e8dSAndreas Gohr     *
357605f8e8dSAndreas Gohr     * @return string|false The path if found, false otherwise
358605f8e8dSAndreas Gohr     */
359605f8e8dSAndreas Gohr    public function findFile($class)
360605f8e8dSAndreas Gohr    {
361605f8e8dSAndreas Gohr        // class map lookup
362605f8e8dSAndreas Gohr        if (isset($this->classMap[$class])) {
363605f8e8dSAndreas Gohr            return $this->classMap[$class];
364605f8e8dSAndreas Gohr        }
3657a33d2f8SNiklas Keller        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
366605f8e8dSAndreas Gohr            return false;
367605f8e8dSAndreas Gohr        }
368e0dd796dSAndreas Gohr        if (null !== $this->apcuPrefix) {
369e0dd796dSAndreas Gohr            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
370e0dd796dSAndreas Gohr            if ($hit) {
371e0dd796dSAndreas Gohr                return $file;
372e0dd796dSAndreas Gohr            }
373e0dd796dSAndreas Gohr        }
374605f8e8dSAndreas Gohr
375605f8e8dSAndreas Gohr        $file = $this->findFileWithExtension($class, '.php');
376605f8e8dSAndreas Gohr
377605f8e8dSAndreas Gohr        // Search for Hack files if we are running on HHVM
3787a33d2f8SNiklas Keller        if (false === $file && defined('HHVM_VERSION')) {
379605f8e8dSAndreas Gohr            $file = $this->findFileWithExtension($class, '.hh');
380605f8e8dSAndreas Gohr        }
381605f8e8dSAndreas Gohr
382e0dd796dSAndreas Gohr        if (null !== $this->apcuPrefix) {
383e0dd796dSAndreas Gohr            apcu_add($this->apcuPrefix.$class, $file);
384e0dd796dSAndreas Gohr        }
385e0dd796dSAndreas Gohr
3867a33d2f8SNiklas Keller        if (false === $file) {
387605f8e8dSAndreas Gohr            // Remember that this class does not exist.
3887a33d2f8SNiklas Keller            $this->missingClasses[$class] = true;
389605f8e8dSAndreas Gohr        }
390605f8e8dSAndreas Gohr
391605f8e8dSAndreas Gohr        return $file;
392605f8e8dSAndreas Gohr    }
393605f8e8dSAndreas Gohr
394*6cb05674SAndreas Gohr    /**
395*6cb05674SAndreas Gohr     * Returns the currently registered loaders indexed by their corresponding vendor directories.
396*6cb05674SAndreas Gohr     *
397*6cb05674SAndreas Gohr     * @return self[]
398*6cb05674SAndreas Gohr     */
399*6cb05674SAndreas Gohr    public static function getRegisteredLoaders()
400*6cb05674SAndreas Gohr    {
401*6cb05674SAndreas Gohr        return self::$registeredLoaders;
402*6cb05674SAndreas Gohr    }
403*6cb05674SAndreas Gohr
404605f8e8dSAndreas Gohr    private function findFileWithExtension($class, $ext)
405605f8e8dSAndreas Gohr    {
406605f8e8dSAndreas Gohr        // PSR-4 lookup
407605f8e8dSAndreas Gohr        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
408605f8e8dSAndreas Gohr
409605f8e8dSAndreas Gohr        $first = $class[0];
410605f8e8dSAndreas Gohr        if (isset($this->prefixLengthsPsr4[$first])) {
411e0dd796dSAndreas Gohr            $subPath = $class;
412e0dd796dSAndreas Gohr            while (false !== $lastPos = strrpos($subPath, '\\')) {
413e0dd796dSAndreas Gohr                $subPath = substr($subPath, 0, $lastPos);
414e0dd796dSAndreas Gohr                $search = $subPath . '\\';
415e0dd796dSAndreas Gohr                if (isset($this->prefixDirsPsr4[$search])) {
416e43cd7e1SAndreas Gohr                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
417e0dd796dSAndreas Gohr                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
418e43cd7e1SAndreas Gohr                        if (file_exists($file = $dir . $pathEnd)) {
419605f8e8dSAndreas Gohr                            return $file;
420605f8e8dSAndreas Gohr                        }
421605f8e8dSAndreas Gohr                    }
422605f8e8dSAndreas Gohr                }
423605f8e8dSAndreas Gohr            }
424605f8e8dSAndreas Gohr        }
425605f8e8dSAndreas Gohr
426605f8e8dSAndreas Gohr        // PSR-4 fallback dirs
427605f8e8dSAndreas Gohr        foreach ($this->fallbackDirsPsr4 as $dir) {
4287a33d2f8SNiklas Keller            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
429605f8e8dSAndreas Gohr                return $file;
430605f8e8dSAndreas Gohr            }
431605f8e8dSAndreas Gohr        }
432605f8e8dSAndreas Gohr
433605f8e8dSAndreas Gohr        // PSR-0 lookup
434605f8e8dSAndreas Gohr        if (false !== $pos = strrpos($class, '\\')) {
435605f8e8dSAndreas Gohr            // namespaced class name
436605f8e8dSAndreas Gohr            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
437605f8e8dSAndreas Gohr                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
438605f8e8dSAndreas Gohr        } else {
439605f8e8dSAndreas Gohr            // PEAR-like class name
440605f8e8dSAndreas Gohr            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
441605f8e8dSAndreas Gohr        }
442605f8e8dSAndreas Gohr
443605f8e8dSAndreas Gohr        if (isset($this->prefixesPsr0[$first])) {
444605f8e8dSAndreas Gohr            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
445605f8e8dSAndreas Gohr                if (0 === strpos($class, $prefix)) {
446605f8e8dSAndreas Gohr                    foreach ($dirs as $dir) {
4477a33d2f8SNiklas Keller                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
448605f8e8dSAndreas Gohr                            return $file;
449605f8e8dSAndreas Gohr                        }
450605f8e8dSAndreas Gohr                    }
451605f8e8dSAndreas Gohr                }
452605f8e8dSAndreas Gohr            }
453605f8e8dSAndreas Gohr        }
454605f8e8dSAndreas Gohr
455605f8e8dSAndreas Gohr        // PSR-0 fallback dirs
456605f8e8dSAndreas Gohr        foreach ($this->fallbackDirsPsr0 as $dir) {
4577a33d2f8SNiklas Keller            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
458605f8e8dSAndreas Gohr                return $file;
459605f8e8dSAndreas Gohr            }
460605f8e8dSAndreas Gohr        }
461605f8e8dSAndreas Gohr
462605f8e8dSAndreas Gohr        // PSR-0 include paths.
463605f8e8dSAndreas Gohr        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
464605f8e8dSAndreas Gohr            return $file;
465605f8e8dSAndreas Gohr        }
4667a33d2f8SNiklas Keller
4677a33d2f8SNiklas Keller        return false;
468605f8e8dSAndreas Gohr    }
469605f8e8dSAndreas Gohr}
470605f8e8dSAndreas Gohr
471605f8e8dSAndreas Gohr/**
472605f8e8dSAndreas Gohr * Scope isolated include.
473605f8e8dSAndreas Gohr *
474605f8e8dSAndreas Gohr * Prevents access to $this/self from included files.
475605f8e8dSAndreas Gohr */
476605f8e8dSAndreas Gohrfunction includeFile($file)
477605f8e8dSAndreas Gohr{
478605f8e8dSAndreas Gohr    include $file;
479605f8e8dSAndreas Gohr}
480