xref: /dokuwiki/vendor/composer/ClassLoader.php (revision d3233986baa7dfe44490b805ae2e4296fad59401)
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>
406cb05674SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-0/
416cb05674SAndreas Gohr * @see    https://www.php-fig.org/psr/psr-4/
42605f8e8dSAndreas Gohr */
43605f8e8dSAndreas Gohrclass ClassLoader
44605f8e8dSAndreas Gohr{
45*d3233986SAndreas Gohr    /** @var ?string */
466cb05674SAndreas Gohr    private $vendorDir;
476cb05674SAndreas Gohr
48605f8e8dSAndreas Gohr    // PSR-4
49*d3233986SAndreas Gohr    /**
50*d3233986SAndreas Gohr     * @var array[]
51*d3233986SAndreas Gohr     * @psalm-var array<string, array<string, int>>
52*d3233986SAndreas Gohr     */
53605f8e8dSAndreas Gohr    private $prefixLengthsPsr4 = array();
54*d3233986SAndreas Gohr    /**
55*d3233986SAndreas Gohr     * @var array[]
56*d3233986SAndreas Gohr     * @psalm-var array<string, array<int, string>>
57*d3233986SAndreas Gohr     */
58605f8e8dSAndreas Gohr    private $prefixDirsPsr4 = array();
59*d3233986SAndreas Gohr    /**
60*d3233986SAndreas Gohr     * @var array[]
61*d3233986SAndreas Gohr     * @psalm-var array<string, string>
62*d3233986SAndreas Gohr     */
63605f8e8dSAndreas Gohr    private $fallbackDirsPsr4 = array();
64605f8e8dSAndreas Gohr
65605f8e8dSAndreas Gohr    // PSR-0
66*d3233986SAndreas Gohr    /**
67*d3233986SAndreas Gohr     * @var array[]
68*d3233986SAndreas Gohr     * @psalm-var array<string, array<string, string[]>>
69*d3233986SAndreas Gohr     */
70605f8e8dSAndreas Gohr    private $prefixesPsr0 = array();
71*d3233986SAndreas Gohr    /**
72*d3233986SAndreas Gohr     * @var array[]
73*d3233986SAndreas Gohr     * @psalm-var array<string, string>
74*d3233986SAndreas Gohr     */
75605f8e8dSAndreas Gohr    private $fallbackDirsPsr0 = array();
76605f8e8dSAndreas Gohr
77*d3233986SAndreas Gohr    /** @var bool */
78605f8e8dSAndreas Gohr    private $useIncludePath = false;
79*d3233986SAndreas Gohr
80*d3233986SAndreas Gohr    /**
81*d3233986SAndreas Gohr     * @var string[]
82*d3233986SAndreas Gohr     * @psalm-var array<string, string>
83*d3233986SAndreas Gohr     */
84605f8e8dSAndreas Gohr    private $classMap = array();
85*d3233986SAndreas Gohr
86*d3233986SAndreas Gohr    /** @var bool */
87605f8e8dSAndreas Gohr    private $classMapAuthoritative = false;
88*d3233986SAndreas Gohr
89*d3233986SAndreas Gohr    /**
90*d3233986SAndreas Gohr     * @var bool[]
91*d3233986SAndreas Gohr     * @psalm-var array<string, bool>
92*d3233986SAndreas Gohr     */
937a33d2f8SNiklas Keller    private $missingClasses = array();
94*d3233986SAndreas Gohr
95*d3233986SAndreas Gohr    /** @var ?string */
96e0dd796dSAndreas Gohr    private $apcuPrefix;
97605f8e8dSAndreas Gohr
98*d3233986SAndreas Gohr    /**
99*d3233986SAndreas Gohr     * @var self[]
100*d3233986SAndreas Gohr     */
1016cb05674SAndreas Gohr    private static $registeredLoaders = array();
1026cb05674SAndreas Gohr
103*d3233986SAndreas Gohr    /**
104*d3233986SAndreas Gohr     * @param ?string $vendorDir
105*d3233986SAndreas Gohr     */
1066cb05674SAndreas Gohr    public function __construct($vendorDir = null)
1076cb05674SAndreas Gohr    {
1086cb05674SAndreas Gohr        $this->vendorDir = $vendorDir;
1096cb05674SAndreas Gohr    }
1106cb05674SAndreas Gohr
111*d3233986SAndreas Gohr    /**
112*d3233986SAndreas Gohr     * @return string[]
113*d3233986SAndreas Gohr     */
114605f8e8dSAndreas Gohr    public function getPrefixes()
115605f8e8dSAndreas Gohr    {
116605f8e8dSAndreas Gohr        if (!empty($this->prefixesPsr0)) {
117a3bfbb3cSAndreas Gohr            return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
118605f8e8dSAndreas Gohr        }
119605f8e8dSAndreas Gohr
120605f8e8dSAndreas Gohr        return array();
121605f8e8dSAndreas Gohr    }
122605f8e8dSAndreas Gohr
123*d3233986SAndreas Gohr    /**
124*d3233986SAndreas Gohr     * @return array[]
125*d3233986SAndreas Gohr     * @psalm-return array<string, array<int, string>>
126*d3233986SAndreas Gohr     */
127605f8e8dSAndreas Gohr    public function getPrefixesPsr4()
128605f8e8dSAndreas Gohr    {
129605f8e8dSAndreas Gohr        return $this->prefixDirsPsr4;
130605f8e8dSAndreas Gohr    }
131605f8e8dSAndreas Gohr
132*d3233986SAndreas Gohr    /**
133*d3233986SAndreas Gohr     * @return array[]
134*d3233986SAndreas Gohr     * @psalm-return array<string, string>
135*d3233986SAndreas Gohr     */
136605f8e8dSAndreas Gohr    public function getFallbackDirs()
137605f8e8dSAndreas Gohr    {
138605f8e8dSAndreas Gohr        return $this->fallbackDirsPsr0;
139605f8e8dSAndreas Gohr    }
140605f8e8dSAndreas Gohr
141*d3233986SAndreas Gohr    /**
142*d3233986SAndreas Gohr     * @return array[]
143*d3233986SAndreas Gohr     * @psalm-return array<string, string>
144*d3233986SAndreas Gohr     */
145605f8e8dSAndreas Gohr    public function getFallbackDirsPsr4()
146605f8e8dSAndreas Gohr    {
147605f8e8dSAndreas Gohr        return $this->fallbackDirsPsr4;
148605f8e8dSAndreas Gohr    }
149605f8e8dSAndreas Gohr
150*d3233986SAndreas Gohr    /**
151*d3233986SAndreas Gohr     * @return string[] Array of classname => path
152*d3233986SAndreas Gohr     * @psalm-return array<string, string>
153*d3233986SAndreas Gohr     */
154605f8e8dSAndreas Gohr    public function getClassMap()
155605f8e8dSAndreas Gohr    {
156605f8e8dSAndreas Gohr        return $this->classMap;
157605f8e8dSAndreas Gohr    }
158605f8e8dSAndreas Gohr
159605f8e8dSAndreas Gohr    /**
160*d3233986SAndreas Gohr     * @param string[] $classMap Class to filename map
161*d3233986SAndreas Gohr     * @psalm-param array<string, string> $classMap
162*d3233986SAndreas Gohr     *
163*d3233986SAndreas Gohr     * @return void
164605f8e8dSAndreas Gohr     */
165605f8e8dSAndreas Gohr    public function addClassMap(array $classMap)
166605f8e8dSAndreas Gohr    {
167605f8e8dSAndreas Gohr        if ($this->classMap) {
168605f8e8dSAndreas Gohr            $this->classMap = array_merge($this->classMap, $classMap);
169605f8e8dSAndreas Gohr        } else {
170605f8e8dSAndreas Gohr            $this->classMap = $classMap;
171605f8e8dSAndreas Gohr        }
172605f8e8dSAndreas Gohr    }
173605f8e8dSAndreas Gohr
174605f8e8dSAndreas Gohr    /**
175605f8e8dSAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix, either
176605f8e8dSAndreas Gohr     * appending or prepending to the ones previously set for this prefix.
177605f8e8dSAndreas Gohr     *
178605f8e8dSAndreas Gohr     * @param string          $prefix  The prefix
179*d3233986SAndreas Gohr     * @param string[]|string $paths   The PSR-0 root directories
180605f8e8dSAndreas Gohr     * @param bool            $prepend Whether to prepend the directories
181*d3233986SAndreas Gohr     *
182*d3233986SAndreas Gohr     * @return void
183605f8e8dSAndreas Gohr     */
184605f8e8dSAndreas Gohr    public function add($prefix, $paths, $prepend = false)
185605f8e8dSAndreas Gohr    {
186605f8e8dSAndreas Gohr        if (!$prefix) {
187605f8e8dSAndreas Gohr            if ($prepend) {
188605f8e8dSAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
189605f8e8dSAndreas Gohr                    (array) $paths,
190605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr0
191605f8e8dSAndreas Gohr                );
192605f8e8dSAndreas Gohr            } else {
193605f8e8dSAndreas Gohr                $this->fallbackDirsPsr0 = array_merge(
194605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr0,
195605f8e8dSAndreas Gohr                    (array) $paths
196605f8e8dSAndreas Gohr                );
197605f8e8dSAndreas Gohr            }
198605f8e8dSAndreas Gohr
199605f8e8dSAndreas Gohr            return;
200605f8e8dSAndreas Gohr        }
201605f8e8dSAndreas Gohr
202605f8e8dSAndreas Gohr        $first = $prefix[0];
203605f8e8dSAndreas Gohr        if (!isset($this->prefixesPsr0[$first][$prefix])) {
204605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
205605f8e8dSAndreas Gohr
206605f8e8dSAndreas Gohr            return;
207605f8e8dSAndreas Gohr        }
208605f8e8dSAndreas Gohr        if ($prepend) {
209605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
210605f8e8dSAndreas Gohr                (array) $paths,
211605f8e8dSAndreas Gohr                $this->prefixesPsr0[$first][$prefix]
212605f8e8dSAndreas Gohr            );
213605f8e8dSAndreas Gohr        } else {
214605f8e8dSAndreas Gohr            $this->prefixesPsr0[$first][$prefix] = array_merge(
215605f8e8dSAndreas Gohr                $this->prefixesPsr0[$first][$prefix],
216605f8e8dSAndreas Gohr                (array) $paths
217605f8e8dSAndreas Gohr            );
218605f8e8dSAndreas Gohr        }
219605f8e8dSAndreas Gohr    }
220605f8e8dSAndreas Gohr
221605f8e8dSAndreas Gohr    /**
222605f8e8dSAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace, either
223605f8e8dSAndreas Gohr     * appending or prepending to the ones previously set for this namespace.
224605f8e8dSAndreas Gohr     *
225605f8e8dSAndreas Gohr     * @param string          $prefix  The prefix/namespace, with trailing '\\'
226*d3233986SAndreas Gohr     * @param string[]|string $paths   The PSR-4 base directories
227605f8e8dSAndreas Gohr     * @param bool            $prepend Whether to prepend the directories
228605f8e8dSAndreas Gohr     *
229605f8e8dSAndreas Gohr     * @throws \InvalidArgumentException
230*d3233986SAndreas Gohr     *
231*d3233986SAndreas Gohr     * @return void
232605f8e8dSAndreas Gohr     */
233605f8e8dSAndreas Gohr    public function addPsr4($prefix, $paths, $prepend = false)
234605f8e8dSAndreas Gohr    {
235605f8e8dSAndreas Gohr        if (!$prefix) {
236605f8e8dSAndreas Gohr            // Register directories for the root namespace.
237605f8e8dSAndreas Gohr            if ($prepend) {
238605f8e8dSAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
239605f8e8dSAndreas Gohr                    (array) $paths,
240605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr4
241605f8e8dSAndreas Gohr                );
242605f8e8dSAndreas Gohr            } else {
243605f8e8dSAndreas Gohr                $this->fallbackDirsPsr4 = array_merge(
244605f8e8dSAndreas Gohr                    $this->fallbackDirsPsr4,
245605f8e8dSAndreas Gohr                    (array) $paths
246605f8e8dSAndreas Gohr                );
247605f8e8dSAndreas Gohr            }
248605f8e8dSAndreas Gohr        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
249605f8e8dSAndreas Gohr            // Register directories for a new namespace.
250605f8e8dSAndreas Gohr            $length = strlen($prefix);
251605f8e8dSAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
252605f8e8dSAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
253605f8e8dSAndreas Gohr            }
254605f8e8dSAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
255605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
256605f8e8dSAndreas Gohr        } elseif ($prepend) {
257605f8e8dSAndreas Gohr            // Prepend directories for an already registered namespace.
258605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
259605f8e8dSAndreas Gohr                (array) $paths,
260605f8e8dSAndreas Gohr                $this->prefixDirsPsr4[$prefix]
261605f8e8dSAndreas Gohr            );
262605f8e8dSAndreas Gohr        } else {
263605f8e8dSAndreas Gohr            // Append directories for an already registered namespace.
264605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = array_merge(
265605f8e8dSAndreas Gohr                $this->prefixDirsPsr4[$prefix],
266605f8e8dSAndreas Gohr                (array) $paths
267605f8e8dSAndreas Gohr            );
268605f8e8dSAndreas Gohr        }
269605f8e8dSAndreas Gohr    }
270605f8e8dSAndreas Gohr
271605f8e8dSAndreas Gohr    /**
272605f8e8dSAndreas Gohr     * Registers a set of PSR-0 directories for a given prefix,
273605f8e8dSAndreas Gohr     * replacing any others previously set for this prefix.
274605f8e8dSAndreas Gohr     *
275605f8e8dSAndreas Gohr     * @param string          $prefix The prefix
276*d3233986SAndreas Gohr     * @param string[]|string $paths  The PSR-0 base directories
277*d3233986SAndreas Gohr     *
278*d3233986SAndreas Gohr     * @return void
279605f8e8dSAndreas Gohr     */
280605f8e8dSAndreas Gohr    public function set($prefix, $paths)
281605f8e8dSAndreas Gohr    {
282605f8e8dSAndreas Gohr        if (!$prefix) {
283605f8e8dSAndreas Gohr            $this->fallbackDirsPsr0 = (array) $paths;
284605f8e8dSAndreas Gohr        } else {
285605f8e8dSAndreas Gohr            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
286605f8e8dSAndreas Gohr        }
287605f8e8dSAndreas Gohr    }
288605f8e8dSAndreas Gohr
289605f8e8dSAndreas Gohr    /**
290605f8e8dSAndreas Gohr     * Registers a set of PSR-4 directories for a given namespace,
291605f8e8dSAndreas Gohr     * replacing any others previously set for this namespace.
292605f8e8dSAndreas Gohr     *
293605f8e8dSAndreas Gohr     * @param string          $prefix The prefix/namespace, with trailing '\\'
294*d3233986SAndreas Gohr     * @param string[]|string $paths  The PSR-4 base directories
295605f8e8dSAndreas Gohr     *
296605f8e8dSAndreas Gohr     * @throws \InvalidArgumentException
297*d3233986SAndreas Gohr     *
298*d3233986SAndreas Gohr     * @return void
299605f8e8dSAndreas Gohr     */
300605f8e8dSAndreas Gohr    public function setPsr4($prefix, $paths)
301605f8e8dSAndreas Gohr    {
302605f8e8dSAndreas Gohr        if (!$prefix) {
303605f8e8dSAndreas Gohr            $this->fallbackDirsPsr4 = (array) $paths;
304605f8e8dSAndreas Gohr        } else {
305605f8e8dSAndreas Gohr            $length = strlen($prefix);
306605f8e8dSAndreas Gohr            if ('\\' !== $prefix[$length - 1]) {
307605f8e8dSAndreas Gohr                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
308605f8e8dSAndreas Gohr            }
309605f8e8dSAndreas Gohr            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
310605f8e8dSAndreas Gohr            $this->prefixDirsPsr4[$prefix] = (array) $paths;
311605f8e8dSAndreas Gohr        }
312605f8e8dSAndreas Gohr    }
313605f8e8dSAndreas Gohr
314605f8e8dSAndreas Gohr    /**
315605f8e8dSAndreas Gohr     * Turns on searching the include path for class files.
316605f8e8dSAndreas Gohr     *
317605f8e8dSAndreas Gohr     * @param bool $useIncludePath
318*d3233986SAndreas Gohr     *
319*d3233986SAndreas Gohr     * @return void
320605f8e8dSAndreas Gohr     */
321605f8e8dSAndreas Gohr    public function setUseIncludePath($useIncludePath)
322605f8e8dSAndreas Gohr    {
323605f8e8dSAndreas Gohr        $this->useIncludePath = $useIncludePath;
324605f8e8dSAndreas Gohr    }
325605f8e8dSAndreas Gohr
326605f8e8dSAndreas Gohr    /**
327605f8e8dSAndreas Gohr     * Can be used to check if the autoloader uses the include path to check
328605f8e8dSAndreas Gohr     * for classes.
329605f8e8dSAndreas Gohr     *
330605f8e8dSAndreas Gohr     * @return bool
331605f8e8dSAndreas Gohr     */
332605f8e8dSAndreas Gohr    public function getUseIncludePath()
333605f8e8dSAndreas Gohr    {
334605f8e8dSAndreas Gohr        return $this->useIncludePath;
335605f8e8dSAndreas Gohr    }
336605f8e8dSAndreas Gohr
337605f8e8dSAndreas Gohr    /**
338605f8e8dSAndreas Gohr     * Turns off searching the prefix and fallback directories for classes
339605f8e8dSAndreas Gohr     * that have not been registered with the class map.
340605f8e8dSAndreas Gohr     *
341605f8e8dSAndreas Gohr     * @param bool $classMapAuthoritative
342*d3233986SAndreas Gohr     *
343*d3233986SAndreas Gohr     * @return void
344605f8e8dSAndreas Gohr     */
345605f8e8dSAndreas Gohr    public function setClassMapAuthoritative($classMapAuthoritative)
346605f8e8dSAndreas Gohr    {
347605f8e8dSAndreas Gohr        $this->classMapAuthoritative = $classMapAuthoritative;
348605f8e8dSAndreas Gohr    }
349605f8e8dSAndreas Gohr
350605f8e8dSAndreas Gohr    /**
351605f8e8dSAndreas Gohr     * Should class lookup fail if not found in the current class map?
352605f8e8dSAndreas Gohr     *
353605f8e8dSAndreas Gohr     * @return bool
354605f8e8dSAndreas Gohr     */
355605f8e8dSAndreas Gohr    public function isClassMapAuthoritative()
356605f8e8dSAndreas Gohr    {
357605f8e8dSAndreas Gohr        return $this->classMapAuthoritative;
358605f8e8dSAndreas Gohr    }
359605f8e8dSAndreas Gohr
360605f8e8dSAndreas Gohr    /**
361e0dd796dSAndreas Gohr     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362e0dd796dSAndreas Gohr     *
363e0dd796dSAndreas Gohr     * @param string|null $apcuPrefix
364*d3233986SAndreas Gohr     *
365*d3233986SAndreas Gohr     * @return void
366e0dd796dSAndreas Gohr     */
367e0dd796dSAndreas Gohr    public function setApcuPrefix($apcuPrefix)
368e0dd796dSAndreas Gohr    {
369e43cd7e1SAndreas Gohr        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
370e0dd796dSAndreas Gohr    }
371e0dd796dSAndreas Gohr
372e0dd796dSAndreas Gohr    /**
373e0dd796dSAndreas Gohr     * The APCu prefix in use, or null if APCu caching is not enabled.
374e0dd796dSAndreas Gohr     *
375e0dd796dSAndreas Gohr     * @return string|null
376e0dd796dSAndreas Gohr     */
377e0dd796dSAndreas Gohr    public function getApcuPrefix()
378e0dd796dSAndreas Gohr    {
379e0dd796dSAndreas Gohr        return $this->apcuPrefix;
380e0dd796dSAndreas Gohr    }
381e0dd796dSAndreas Gohr
382e0dd796dSAndreas Gohr    /**
383605f8e8dSAndreas Gohr     * Registers this instance as an autoloader.
384605f8e8dSAndreas Gohr     *
385605f8e8dSAndreas Gohr     * @param bool $prepend Whether to prepend the autoloader or not
386*d3233986SAndreas Gohr     *
387*d3233986SAndreas Gohr     * @return void
388605f8e8dSAndreas Gohr     */
389605f8e8dSAndreas Gohr    public function register($prepend = false)
390605f8e8dSAndreas Gohr    {
391605f8e8dSAndreas Gohr        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
3926cb05674SAndreas Gohr
3936cb05674SAndreas Gohr        if (null === $this->vendorDir) {
3946cb05674SAndreas Gohr            return;
3956cb05674SAndreas Gohr        }
3966cb05674SAndreas Gohr
3976cb05674SAndreas Gohr        if ($prepend) {
3986cb05674SAndreas Gohr            self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
3996cb05674SAndreas Gohr        } else {
4006cb05674SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
4016cb05674SAndreas Gohr            self::$registeredLoaders[$this->vendorDir] = $this;
4026cb05674SAndreas Gohr        }
403605f8e8dSAndreas Gohr    }
404605f8e8dSAndreas Gohr
405605f8e8dSAndreas Gohr    /**
406605f8e8dSAndreas Gohr     * Unregisters this instance as an autoloader.
407*d3233986SAndreas Gohr     *
408*d3233986SAndreas Gohr     * @return void
409605f8e8dSAndreas Gohr     */
410605f8e8dSAndreas Gohr    public function unregister()
411605f8e8dSAndreas Gohr    {
412605f8e8dSAndreas Gohr        spl_autoload_unregister(array($this, 'loadClass'));
4136cb05674SAndreas Gohr
4146cb05674SAndreas Gohr        if (null !== $this->vendorDir) {
4156cb05674SAndreas Gohr            unset(self::$registeredLoaders[$this->vendorDir]);
4166cb05674SAndreas Gohr        }
417605f8e8dSAndreas Gohr    }
418605f8e8dSAndreas Gohr
419605f8e8dSAndreas Gohr    /**
420605f8e8dSAndreas Gohr     * Loads the given class or interface.
421605f8e8dSAndreas Gohr     *
422605f8e8dSAndreas Gohr     * @param  string    $class The name of the class
423*d3233986SAndreas Gohr     * @return true|null True if loaded, null otherwise
424605f8e8dSAndreas Gohr     */
425605f8e8dSAndreas Gohr    public function loadClass($class)
426605f8e8dSAndreas Gohr    {
427605f8e8dSAndreas Gohr        if ($file = $this->findFile($class)) {
428605f8e8dSAndreas Gohr            includeFile($file);
429605f8e8dSAndreas Gohr
430605f8e8dSAndreas Gohr            return true;
431605f8e8dSAndreas Gohr        }
432*d3233986SAndreas Gohr
433*d3233986SAndreas Gohr        return null;
434605f8e8dSAndreas Gohr    }
435605f8e8dSAndreas Gohr
436605f8e8dSAndreas Gohr    /**
437605f8e8dSAndreas Gohr     * Finds the path to the file where the class is defined.
438605f8e8dSAndreas Gohr     *
439605f8e8dSAndreas Gohr     * @param string $class The name of the class
440605f8e8dSAndreas Gohr     *
441605f8e8dSAndreas Gohr     * @return string|false The path if found, false otherwise
442605f8e8dSAndreas Gohr     */
443605f8e8dSAndreas Gohr    public function findFile($class)
444605f8e8dSAndreas Gohr    {
445605f8e8dSAndreas Gohr        // class map lookup
446605f8e8dSAndreas Gohr        if (isset($this->classMap[$class])) {
447605f8e8dSAndreas Gohr            return $this->classMap[$class];
448605f8e8dSAndreas Gohr        }
4497a33d2f8SNiklas Keller        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
450605f8e8dSAndreas Gohr            return false;
451605f8e8dSAndreas Gohr        }
452e0dd796dSAndreas Gohr        if (null !== $this->apcuPrefix) {
453e0dd796dSAndreas Gohr            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
454e0dd796dSAndreas Gohr            if ($hit) {
455e0dd796dSAndreas Gohr                return $file;
456e0dd796dSAndreas Gohr            }
457e0dd796dSAndreas Gohr        }
458605f8e8dSAndreas Gohr
459605f8e8dSAndreas Gohr        $file = $this->findFileWithExtension($class, '.php');
460605f8e8dSAndreas Gohr
461605f8e8dSAndreas Gohr        // Search for Hack files if we are running on HHVM
4627a33d2f8SNiklas Keller        if (false === $file && defined('HHVM_VERSION')) {
463605f8e8dSAndreas Gohr            $file = $this->findFileWithExtension($class, '.hh');
464605f8e8dSAndreas Gohr        }
465605f8e8dSAndreas Gohr
466e0dd796dSAndreas Gohr        if (null !== $this->apcuPrefix) {
467e0dd796dSAndreas Gohr            apcu_add($this->apcuPrefix.$class, $file);
468e0dd796dSAndreas Gohr        }
469e0dd796dSAndreas Gohr
4707a33d2f8SNiklas Keller        if (false === $file) {
471605f8e8dSAndreas Gohr            // Remember that this class does not exist.
4727a33d2f8SNiklas Keller            $this->missingClasses[$class] = true;
473605f8e8dSAndreas Gohr        }
474605f8e8dSAndreas Gohr
475605f8e8dSAndreas Gohr        return $file;
476605f8e8dSAndreas Gohr    }
477605f8e8dSAndreas Gohr
4786cb05674SAndreas Gohr    /**
4796cb05674SAndreas Gohr     * Returns the currently registered loaders indexed by their corresponding vendor directories.
4806cb05674SAndreas Gohr     *
4816cb05674SAndreas Gohr     * @return self[]
4826cb05674SAndreas Gohr     */
4836cb05674SAndreas Gohr    public static function getRegisteredLoaders()
4846cb05674SAndreas Gohr    {
4856cb05674SAndreas Gohr        return self::$registeredLoaders;
4866cb05674SAndreas Gohr    }
4876cb05674SAndreas Gohr
488*d3233986SAndreas Gohr    /**
489*d3233986SAndreas Gohr     * @param  string       $class
490*d3233986SAndreas Gohr     * @param  string       $ext
491*d3233986SAndreas Gohr     * @return string|false
492*d3233986SAndreas Gohr     */
493605f8e8dSAndreas Gohr    private function findFileWithExtension($class, $ext)
494605f8e8dSAndreas Gohr    {
495605f8e8dSAndreas Gohr        // PSR-4 lookup
496605f8e8dSAndreas Gohr        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
497605f8e8dSAndreas Gohr
498605f8e8dSAndreas Gohr        $first = $class[0];
499605f8e8dSAndreas Gohr        if (isset($this->prefixLengthsPsr4[$first])) {
500e0dd796dSAndreas Gohr            $subPath = $class;
501e0dd796dSAndreas Gohr            while (false !== $lastPos = strrpos($subPath, '\\')) {
502e0dd796dSAndreas Gohr                $subPath = substr($subPath, 0, $lastPos);
503e0dd796dSAndreas Gohr                $search = $subPath . '\\';
504e0dd796dSAndreas Gohr                if (isset($this->prefixDirsPsr4[$search])) {
505e43cd7e1SAndreas Gohr                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
506e0dd796dSAndreas Gohr                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
507e43cd7e1SAndreas Gohr                        if (file_exists($file = $dir . $pathEnd)) {
508605f8e8dSAndreas Gohr                            return $file;
509605f8e8dSAndreas Gohr                        }
510605f8e8dSAndreas Gohr                    }
511605f8e8dSAndreas Gohr                }
512605f8e8dSAndreas Gohr            }
513605f8e8dSAndreas Gohr        }
514605f8e8dSAndreas Gohr
515605f8e8dSAndreas Gohr        // PSR-4 fallback dirs
516605f8e8dSAndreas Gohr        foreach ($this->fallbackDirsPsr4 as $dir) {
5177a33d2f8SNiklas Keller            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
518605f8e8dSAndreas Gohr                return $file;
519605f8e8dSAndreas Gohr            }
520605f8e8dSAndreas Gohr        }
521605f8e8dSAndreas Gohr
522605f8e8dSAndreas Gohr        // PSR-0 lookup
523605f8e8dSAndreas Gohr        if (false !== $pos = strrpos($class, '\\')) {
524605f8e8dSAndreas Gohr            // namespaced class name
525605f8e8dSAndreas Gohr            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
526605f8e8dSAndreas Gohr                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
527605f8e8dSAndreas Gohr        } else {
528605f8e8dSAndreas Gohr            // PEAR-like class name
529605f8e8dSAndreas Gohr            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
530605f8e8dSAndreas Gohr        }
531605f8e8dSAndreas Gohr
532605f8e8dSAndreas Gohr        if (isset($this->prefixesPsr0[$first])) {
533605f8e8dSAndreas Gohr            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
534605f8e8dSAndreas Gohr                if (0 === strpos($class, $prefix)) {
535605f8e8dSAndreas Gohr                    foreach ($dirs as $dir) {
5367a33d2f8SNiklas Keller                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
537605f8e8dSAndreas Gohr                            return $file;
538605f8e8dSAndreas Gohr                        }
539605f8e8dSAndreas Gohr                    }
540605f8e8dSAndreas Gohr                }
541605f8e8dSAndreas Gohr            }
542605f8e8dSAndreas Gohr        }
543605f8e8dSAndreas Gohr
544605f8e8dSAndreas Gohr        // PSR-0 fallback dirs
545605f8e8dSAndreas Gohr        foreach ($this->fallbackDirsPsr0 as $dir) {
5467a33d2f8SNiklas Keller            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
547605f8e8dSAndreas Gohr                return $file;
548605f8e8dSAndreas Gohr            }
549605f8e8dSAndreas Gohr        }
550605f8e8dSAndreas Gohr
551605f8e8dSAndreas Gohr        // PSR-0 include paths.
552605f8e8dSAndreas Gohr        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
553605f8e8dSAndreas Gohr            return $file;
554605f8e8dSAndreas Gohr        }
5557a33d2f8SNiklas Keller
5567a33d2f8SNiklas Keller        return false;
557605f8e8dSAndreas Gohr    }
558605f8e8dSAndreas Gohr}
559605f8e8dSAndreas Gohr
560605f8e8dSAndreas Gohr/**
561605f8e8dSAndreas Gohr * Scope isolated include.
562605f8e8dSAndreas Gohr *
563605f8e8dSAndreas Gohr * Prevents access to $this/self from included files.
564*d3233986SAndreas Gohr *
565*d3233986SAndreas Gohr * @param  string $file
566*d3233986SAndreas Gohr * @return void
567*d3233986SAndreas Gohr * @private
568605f8e8dSAndreas Gohr */
569605f8e8dSAndreas Gohrfunction includeFile($file)
570605f8e8dSAndreas Gohr{
571605f8e8dSAndreas Gohr    include $file;
572605f8e8dSAndreas Gohr}
573