4
* This file is part of Composer.
6
* (c) Nils Adermann <naderman@naderman.de>
7
* Jordi Boggiano <j.boggiano@seld.be>
9
* For the full copyright and license information, please view the LICENSE
10
* file that was distributed with this source code.
13
namespace Composer\Autoload;
16
* ClassLoader implements a PSR-0 class loader
18
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
20
* $loader = new \Composer\Autoload\ClassLoader();
22
* // register classes with namespaces
23
* $loader->add('Symfony\Component', __DIR__.'/component');
24
* $loader->add('Symfony', __DIR__.'/framework');
26
* // activate the autoloader
27
* $loader->register();
29
* // to enable searching the include path (eg. for PEAR packages)
30
* $loader->setUseIncludePath(true);
32
* In this example, if you try to use a class in the Symfony\Component
33
* namespace or one of its children (Symfony\Component\Console for instance),
34
* the autoloader will first look for the class under the component/
35
* directory, and it will then fallback to the framework/ directory if not
36
* found before giving up.
38
* This class is loosely based on the Symfony UniversalClassLoader.
40
* @author Fabien Potencier <fabien@symfony.com>
41
* @author Jordi Boggiano <j.boggiano@seld.be>
46
private $prefixLengthsPsr4 = array();
47
private $prefixDirsPsr4 = array();
48
private $fallbackDirsPsr4 = array();
51
private $prefixesPsr0 = array();
52
private $fallbackDirsPsr0 = array();
54
private $useIncludePath = false;
55
private $classMap = array();
57
private $classMapAuthoritative = false;
59
public function getPrefixes()
61
if (!empty($this->prefixesPsr0)) {
62
return call_user_func_array('array_merge', $this->prefixesPsr0);
68
public function getPrefixesPsr4()
70
return $this->prefixDirsPsr4;
73
public function getFallbackDirs()
75
return $this->fallbackDirsPsr0;
78
public function getFallbackDirsPsr4()
80
return $this->fallbackDirsPsr4;
83
public function getClassMap()
85
return $this->classMap;
89
* @param array $classMap Class to filename map
91
public function addClassMap(array $classMap)
93
if ($this->classMap) {
94
$this->classMap = array_merge($this->classMap, $classMap);
96
$this->classMap = $classMap;
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.
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
108
public function add($prefix, $paths, $prepend = false)
112
$this->fallbackDirsPsr0 = array_merge(
114
$this->fallbackDirsPsr0
117
$this->fallbackDirsPsr0 = array_merge(
118
$this->fallbackDirsPsr0,
127
if (!isset($this->prefixesPsr0[$first][$prefix])) {
128
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
133
$this->prefixesPsr0[$first][$prefix] = array_merge(
135
$this->prefixesPsr0[$first][$prefix]
138
$this->prefixesPsr0[$first][$prefix] = array_merge(
139
$this->prefixesPsr0[$first][$prefix],
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.
149
* @param string $prefix The prefix/namespace, with trailing '\\'
150
* @param array|string $paths The PSR-0 base directories
151
* @param bool $prepend Whether to prepend the directories
153
* @throws \InvalidArgumentException
155
public function addPsr4($prefix, $paths, $prepend = false)
158
// Register directories for the root namespace.
160
$this->fallbackDirsPsr4 = array_merge(
162
$this->fallbackDirsPsr4
165
$this->fallbackDirsPsr4 = array_merge(
166
$this->fallbackDirsPsr4,
170
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
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.");
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(
182
$this->prefixDirsPsr4[$prefix]
185
// Append directories for an already registered namespace.
186
$this->prefixDirsPsr4[$prefix] = array_merge(
187
$this->prefixDirsPsr4[$prefix],
194
* Registers a set of PSR-0 directories for a given prefix,
195
* replacing any others previously set for this prefix.
197
* @param string $prefix The prefix
198
* @param array|string $paths The PSR-0 base directories
200
public function set($prefix, $paths)
203
$this->fallbackDirsPsr0 = (array) $paths;
205
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
210
* Registers a set of PSR-4 directories for a given namespace,
211
* replacing any others previously set for this namespace.
213
* @param string $prefix The prefix/namespace, with trailing '\\'
214
* @param array|string $paths The PSR-4 base directories
216
* @throws \InvalidArgumentException
218
public function setPsr4($prefix, $paths)
221
$this->fallbackDirsPsr4 = (array) $paths;
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.");
227
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
228
$this->prefixDirsPsr4[$prefix] = (array) $paths;
233
* Turns on searching the include path for class files.
235
* @param bool $useIncludePath
237
public function setUseIncludePath($useIncludePath)
239
$this->useIncludePath = $useIncludePath;
243
* Can be used to check if the autoloader uses the include path to check
248
public function getUseIncludePath()
250
return $this->useIncludePath;
254
* Turns off searching the prefix and fallback directories for classes
255
* that have not been registered with the class map.
257
* @param bool $classMapAuthoritative
259
public function setClassMapAuthoritative($classMapAuthoritative)
261
$this->classMapAuthoritative = $classMapAuthoritative;
265
* Should class lookup fail if not found in the current class map?
269
public function isClassMapAuthoritative()
271
return $this->classMapAuthoritative;
275
* Registers this instance as an autoloader.
277
* @param bool $prepend Whether to prepend the autoloader or not
279
public function register($prepend = false)
281
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
285
* Unregisters this instance as an autoloader.
287
public function unregister()
289
spl_autoload_unregister(array($this, 'loadClass'));
293
* Loads the given class or interface.
295
* @param string $class The name of the class
296
* @return bool|null True if loaded, null otherwise
298
public function loadClass($class)
300
if ($file = $this->findFile($class)) {
308
* Finds the path to the file where the class is defined.
310
* @param string $class The name of the class
312
* @return string|false The path if found, false otherwise
314
public function findFile($class)
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);
322
if (isset($this->classMap[$class])) {
323
return $this->classMap[$class];
325
if ($this->classMapAuthoritative) {
329
$file = $this->findFileWithExtension($class, '.php');
331
// Search for Hack files if we are running on HHVM
332
if ($file === null && defined('HHVM_VERSION')) {
333
$file = $this->findFileWithExtension($class, '.hh');
336
if ($file === null) {
337
// Remember that this class does not exist.
338
return $this->classMap[$class] = false;
344
private function findFileWithExtension($class, $ext)
347
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
350
if (isset($this->prefixLengthsPsr4[$first])) {
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))) {
362
// PSR-4 fallback dirs
363
foreach ($this->fallbackDirsPsr4 as $dir) {
364
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
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);
375
// PEAR-like class name
376
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
379
if (isset($this->prefixesPsr0[$first])) {
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)) {
391
// PSR-0 fallback dirs
392
foreach ($this->fallbackDirsPsr0 as $dir) {
393
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
398
// PSR-0 include paths.
399
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
406
* Scope isolated include.
408
* Prevents access to $this/self from included files.
410
function includeFile($file)