ClassLoader.php 5.44 KB
Newer Older
1 2 3
<?php

/*
4
 * This file is part of Composer.
5
 *
6 7
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
8 9 10 11 12
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

13
namespace Composer\Autoload;
14 15

/**
16
 * ClassLoader implements a PSR-0 class loader
17 18 19
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
 *
20
 *     $loader = new \Composer\Autoload\ClassLoader();
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component', __DIR__.'/component');
 *     $loader->add('Symfony',           __DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
38 39
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
40 41 42 43 44 45 46 47
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 */
class ClassLoader
{
    private $prefixes = array();
    private $fallbackDirs = array();
    private $useIncludePath = false;
48
    private $classMap = array();
49 50 51 52 53 54 55 56 57 58 59

    public function getPrefixes()
    {
        return $this->prefixes;
    }

    public function getFallbackDirs()
    {
        return $this->fallbackDirs;
    }

60 61 62 63 64 65 66 67 68
    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array $classMap Class to filename map
     */
    public function addClassMap(array $classMap)
69
    {
70 71 72 73
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
74 75 76 77 78 79
        }
    }

    /**
     * Registers a set of classes
     *
80 81
     * @param string       $prefix The classes prefix
     * @param array|string $paths  The location(s) of the classes
82
     */
83
    public function add($prefix, $paths)
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    {
        if (!$prefix) {
            foreach ((array) $paths as $path) {
                $this->fallbackDirs[] = $path;
            }

            return;
        }
        if (isset($this->prefixes[$prefix])) {
            $this->prefixes[$prefix] = array_merge(
                $this->prefixes[$prefix],
                (array) $paths
            );
        } else {
            $this->prefixes[$prefix] = (array) $paths;
        }
    }

    /**
103
     * Turns on searching the include path for class files.
104
     *
105
     * @param bool $useIncludePath
106 107 108 109 110 111 112 113 114 115
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to check
     * for classes.
     *
116
     * @return bool
117 118 119 120 121 122 123 124 125
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Registers this instance as an autoloader.
     *
126
     * @param bool $prepend Whether to prepend the autoloader or not
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

    /**
     * Unregisters this instance as an autoloader.
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));
    }

    /**
     * Loads the given class or interface.
     *
144 145
     * @param  string    $class The name of the class
     * @return bool|null True, if loaded
146 147 148 149
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
150
            include $file;
151 152 153 154 155 156 157 158 159 160 161 162 163 164

            return true;
        }
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|null The path, if found
     */
    public function findFile($class)
    {
165 166 167 168
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
        if ('\\' == $class[0]) {
            $class = substr($class, 1);
        }

        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR;
            $className = substr($class, $pos + 1);
        } else {
            // PEAR-like class name
            $classPath = null;
            $className = $class;
        }

        $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

        foreach ($this->prefixes as $prefix => $dirs) {
            if (0 === strpos($class, $prefix)) {
                foreach ($dirs as $dir) {
                    if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
                        return $dir . DIRECTORY_SEPARATOR . $classPath;
                    }
                }
            }
        }

        foreach ($this->fallbackDirs as $dir) {
            if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) {
                return $dir . DIRECTORY_SEPARATOR . $classPath;
            }
        }

        if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) {
            return $file;
        }
204 205

        $this->classMap[$class] = false;
206 207
    }
}