Completed
Push — master ( 382c63...5355c1 )
by rugk
02:29
created

ClassLoader::getUseIncludePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of Composer.
5
 *
6
 * (c) Nils Adermann <[email protected]>
7
 *     Jordi Boggiano <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Composer\Autoload;
14
15
/**
16
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
 *
18
 *     $loader = new \Composer\Autoload\ClassLoader();
19
 *
20
 *     // register classes with namespaces
21
 *     $loader->add('Symfony\Component', __DIR__.'/component');
22
 *     $loader->add('Symfony',           __DIR__.'/framework');
23
 *
24
 *     // activate the autoloader
25
 *     $loader->register();
26
 *
27
 *     // to enable searching the include path (eg. for PEAR packages)
28
 *     $loader->setUseIncludePath(true);
29
 *
30
 * In this example, if you try to use a class in the Symfony\Component
31
 * namespace or one of its children (Symfony\Component\Console for instance),
32
 * the autoloader will first look for the class under the component/
33
 * directory, and it will then fallback to the framework/ directory if not
34
 * found before giving up.
35
 *
36
 * This class is loosely based on the Symfony UniversalClassLoader.
37
 *
38
 * @author Fabien Potencier <[email protected]>
39
 * @author Jordi Boggiano <[email protected]>
40
 * @see    http://www.php-fig.org/psr/psr-0/
41
 * @see    http://www.php-fig.org/psr/psr-4/
42
 */
43
class ClassLoader
44
{
45
    // PSR-4
46
    private $prefixLengthsPsr4 = array();
47
    private $prefixDirsPsr4 = array();
48
    private $fallbackDirsPsr4 = array();
49
50
    // PSR-0
51
    private $prefixesPsr0 = array();
52
    private $fallbackDirsPsr0 = array();
53
54
    private $useIncludePath = false;
55
    private $classMap = array();
56
57
    private $classMapAuthoritative = false;
58
59
    public function getPrefixes()
60
    {
61
        if (!empty($this->prefixesPsr0)) {
62
            return call_user_func_array('array_merge', $this->prefixesPsr0);
63
        }
64
65
        return array();
66
    }
67
68
    public function getPrefixesPsr4()
69
    {
70
        return $this->prefixDirsPsr4;
71
    }
72
73
    public function getFallbackDirs()
74
    {
75
        return $this->fallbackDirsPsr0;
76
    }
77
78
    public function getFallbackDirsPsr4()
79
    {
80
        return $this->fallbackDirsPsr4;
81
    }
82
83
    public function getClassMap()
84
    {
85
        return $this->classMap;
86
    }
87
88
    /**
89
     * @param array $classMap Class to filename map
90
     */
91
    public function addClassMap(array $classMap)
92
    {
93
        if ($this->classMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->classMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
94
            $this->classMap = array_merge($this->classMap, $classMap);
95
        } else {
96
            $this->classMap = $classMap;
97
        }
98
    }
99
100
    /**
101
     * Registers a set of PSR-0 directories for a given prefix, either
102
     * appending or prepending to the ones previously set for this prefix.
103
     *
104
     * @param string       $prefix  The prefix
105
     * @param array|string $paths   The PSR-0 root directories
106
     * @param bool         $prepend Whether to prepend the directories
107
     */
108
    public function add($prefix, $paths, $prepend = false)
109
    {
110 View Code Duplication
        if (!$prefix) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
            if ($prepend) {
112
                $this->fallbackDirsPsr0 = array_merge(
113
                    (array) $paths,
114
                    $this->fallbackDirsPsr0
115
                );
116
            } else {
117
                $this->fallbackDirsPsr0 = array_merge(
118
                    $this->fallbackDirsPsr0,
119
                    (array) $paths
120
                );
121
            }
122
123
            return;
124
        }
125
126
        $first = $prefix[0];
127 View Code Duplication
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
129
130
            return;
131
        }
132
        if ($prepend) {
133
            $this->prefixesPsr0[$first][$prefix] = array_merge(
134
                (array) $paths,
135
                $this->prefixesPsr0[$first][$prefix]
136
            );
137 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
            $this->prefixesPsr0[$first][$prefix] = array_merge(
139
                $this->prefixesPsr0[$first][$prefix],
140
                (array) $paths
141
            );
142
        }
143
    }
144
145
    /**
146
     * Registers a set of PSR-4 directories for a given namespace, either
147
     * appending or prepending to the ones previously set for this namespace.
148
     *
149
     * @param string       $prefix  The prefix/namespace, with trailing '\\'
150
     * @param array|string $paths   The PSR-4 base directories
151
     * @param bool         $prepend Whether to prepend the directories
152
     *
153
     * @throws \InvalidArgumentException
154
     */
155
    public function addPsr4($prefix, $paths, $prepend = false)
156
    {
157
        if (!$prefix) {
158
            // Register directories for the root namespace.
159 View Code Duplication
            if ($prepend) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
                $this->fallbackDirsPsr4 = array_merge(
161
                    (array) $paths,
162
                    $this->fallbackDirsPsr4
163
                );
164
            } else {
165
                $this->fallbackDirsPsr4 = array_merge(
166
                    $this->fallbackDirsPsr4,
167
                    (array) $paths
168
                );
169
            }
170 View Code Duplication
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
171
            // Register directories for a new namespace.
172
            $length = strlen($prefix);
173
            if ('\\' !== $prefix[$length - 1]) {
174
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
175
            }
176
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
177
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
178
        } elseif ($prepend) {
179
            // Prepend directories for an already registered namespace.
180
            $this->prefixDirsPsr4[$prefix] = array_merge(
181
                (array) $paths,
182
                $this->prefixDirsPsr4[$prefix]
183
            );
184
        } else {
185
            // Append directories for an already registered namespace.
186
            $this->prefixDirsPsr4[$prefix] = array_merge(
187
                $this->prefixDirsPsr4[$prefix],
188
                (array) $paths
189
            );
190
        }
191
    }
192
193
    /**
194
     * Registers a set of PSR-0 directories for a given prefix,
195
     * replacing any others previously set for this prefix.
196
     *
197
     * @param string       $prefix The prefix
198
     * @param array|string $paths  The PSR-0 base directories
199
     */
200
    public function set($prefix, $paths)
201
    {
202
        if (!$prefix) {
203
            $this->fallbackDirsPsr0 = (array) $paths;
204
        } else {
205
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
206
        }
207
    }
208
209
    /**
210
     * Registers a set of PSR-4 directories for a given namespace,
211
     * replacing any others previously set for this namespace.
212
     *
213
     * @param string       $prefix The prefix/namespace, with trailing '\\'
214
     * @param array|string $paths  The PSR-4 base directories
215
     *
216
     * @throws \InvalidArgumentException
217
     */
218
    public function setPsr4($prefix, $paths)
219
    {
220 View Code Duplication
        if (!$prefix) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
            $this->fallbackDirsPsr4 = (array) $paths;
222
        } else {
223
            $length = strlen($prefix);
224
            if ('\\' !== $prefix[$length - 1]) {
225
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
226
            }
227
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
228
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
229
        }
230
    }
231
232
    /**
233
     * Turns on searching the include path for class files.
234
     *
235
     * @param bool $useIncludePath
236
     */
237
    public function setUseIncludePath($useIncludePath)
238
    {
239
        $this->useIncludePath = $useIncludePath;
240
    }
241
242
    /**
243
     * Can be used to check if the autoloader uses the include path to check
244
     * for classes.
245
     *
246
     * @return bool
247
     */
248
    public function getUseIncludePath()
249
    {
250
        return $this->useIncludePath;
251
    }
252
253
    /**
254
     * Turns off searching the prefix and fallback directories for classes
255
     * that have not been registered with the class map.
256
     *
257
     * @param bool $classMapAuthoritative
258
     */
259
    public function setClassMapAuthoritative($classMapAuthoritative)
260
    {
261
        $this->classMapAuthoritative = $classMapAuthoritative;
262
    }
263
264
    /**
265
     * Should class lookup fail if not found in the current class map?
266
     *
267
     * @return bool
268
     */
269
    public function isClassMapAuthoritative()
270
    {
271
        return $this->classMapAuthoritative;
272
    }
273
274
    /**
275
     * Registers this instance as an autoloader.
276
     *
277
     * @param bool $prepend Whether to prepend the autoloader or not
278
     */
279
    public function register($prepend = false)
280
    {
281
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
282
    }
283
284
    /**
285
     * Unregisters this instance as an autoloader.
286
     */
287
    public function unregister()
288
    {
289
        spl_autoload_unregister(array($this, 'loadClass'));
290
    }
291
292
    /**
293
     * Loads the given class or interface.
294
     *
295
     * @param  string    $class The name of the class
296
     * @return bool|null True if loaded, null otherwise
297
     */
298
    public function loadClass($class)
299
    {
300
        if ($file = $this->findFile($class)) {
301
            includeFile($file);
302
303
            return true;
304
        }
305
    }
306
307
    /**
308
     * Finds the path to the file where the class is defined.
309
     *
310
     * @param string $class The name of the class
311
     *
312
     * @return string|false The path if found, false otherwise
313
     */
314
    public function findFile($class)
315
    {
316
        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
317
        if ('\\' == $class[0]) {
318
            $class = substr($class, 1);
319
        }
320
321
        // class map lookup
322
        if (isset($this->classMap[$class])) {
323
            return $this->classMap[$class];
324
        }
325
        if ($this->classMapAuthoritative) {
326
            return false;
327
        }
328
329
        $file = $this->findFileWithExtension($class, '.php');
330
331
        // Search for Hack files if we are running on HHVM
332
        if ($file === null && defined('HHVM_VERSION')) {
333
            $file = $this->findFileWithExtension($class, '.hh');
334
        }
335
336
        if ($file === null) {
337
            // Remember that this class does not exist.
338
            return $this->classMap[$class] = false;
339
        }
340
341
        return $file;
342
    }
343
344
    private function findFileWithExtension($class, $ext)
345
    {
346
        // PSR-4 lookup
347
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
348
349
        $first = $class[0];
350 View Code Duplication
        if (isset($this->prefixLengthsPsr4[$first])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
352
                if (0 === strpos($class, $prefix)) {
353
                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
354
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
355
                            return $file;
356
                        }
357
                    }
358
                }
359
            }
360
        }
361
362
        // PSR-4 fallback dirs
363
        foreach ($this->fallbackDirsPsr4 as $dir) {
364
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
365
                return $file;
366
            }
367
        }
368
369
        // PSR-0 lookup
370
        if (false !== $pos = strrpos($class, '\\')) {
371
            // namespaced class name
372
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
373
                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
374
        } else {
375
            // PEAR-like class name
376
            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
377
        }
378
379 View Code Duplication
        if (isset($this->prefixesPsr0[$first])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
380
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
381
                if (0 === strpos($class, $prefix)) {
382
                    foreach ($dirs as $dir) {
383
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
384
                            return $file;
385
                        }
386
                    }
387
                }
388
            }
389
        }
390
391
        // PSR-0 fallback dirs
392
        foreach ($this->fallbackDirsPsr0 as $dir) {
393
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
394
                return $file;
395
            }
396
        }
397
398
        // PSR-0 include paths.
399
        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
400
            return $file;
401
        }
402
    }
403
}
404
405
/**
406
 * Scope isolated include.
407
 *
408
 * Prevents access to $this/self from included files.
409
 */
410
function includeFile($file)
411
{
412
    include $file;
413
}
414