ReacherCD/Batcave
0
1<?php2 3/*4 * This file is part of Composer.5 *6 * (c) Nils Adermann <naderman@naderman.de>7 * Jordi Boggiano <j.boggiano@seld.be>8 *9 * For the full copyright and license information, please view the LICENSE10 * file that was distributed with this source code.11 */12 13namespace 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 namespaces21 * $loader->add('Symfony\Component', __DIR__.'/component');22 * $loader->add('Symfony', __DIR__.'/framework');23 *24 * // activate the autoloader25 * $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\Component31 * 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 not34 * found before giving up.35 *36 * This class is loosely based on the Symfony UniversalClassLoader.37 *38 * @author Fabien Potencier <fabien@symfony.com>39 * @author Jordi Boggiano <j.boggiano@seld.be>40 * @see http://www.php-fig.org/psr/psr-0/41 * @see http://www.php-fig.org/psr/psr-4/42 */43class ClassLoader44{45 // PSR-446 private $prefixLengthsPsr4 = array();47 private $prefixDirsPsr4 = array();48 private $fallbackDirsPsr4 = array();49 50 // PSR-051 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 map90 */91 public function addClassMap(array $classMap)92 {93 if ($this->classMap) {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, either102 * appending or prepending to the ones previously set for this prefix.103 *104 * @param string $prefix The prefix105 * @param array|string $paths The PSR-0 root directories106 * @param bool $prepend Whether to prepend the directories107 */108 public function add($prefix, $paths, $prepend = false)109 {110 if (!$prefix) {111 if ($prepend) {112 $this->fallbackDirsPsr0 = array_merge(113 (array) $paths,114 $this->fallbackDirsPsr0115 );116 } else {117 $this->fallbackDirsPsr0 = array_merge(118 $this->fallbackDirsPsr0,119 (array) $paths120 );121 }122 123 return;124 }125 126 $first = $prefix[0];127 if (!isset($this->prefixesPsr0[$first][$prefix])) {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 } else {138 $this->prefixesPsr0[$first][$prefix] = array_merge(139 $this->prefixesPsr0[$first][$prefix],140 (array) $paths141 );142 }143 }144 145 /**146 * Registers a set of PSR-4 directories for a given namespace, either147 * 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 directories151 * @param bool $prepend Whether to prepend the directories152 *153 * @throws \InvalidArgumentException154 */155 public function addPsr4($prefix, $paths, $prepend = false)156 {157 if (!$prefix) {158 // Register directories for the root namespace.159 if ($prepend) {160 $this->fallbackDirsPsr4 = array_merge(161 (array) $paths,162 $this->fallbackDirsPsr4163 );164 } else {165 $this->fallbackDirsPsr4 = array_merge(166 $this->fallbackDirsPsr4,167 (array) $paths168 );169 }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.");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) $paths189 );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 prefix198 * @param array|string $paths The PSR-0 base directories199 */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 directories215 *216 * @throws \InvalidArgumentException217 */218 public function setPsr4($prefix, $paths)219 {220 if (!$prefix) {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 $useIncludePath236 */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 check244 * for classes.245 *246 * @return bool247 */248 public function getUseIncludePath()249 {250 return $this->useIncludePath;251 }252 253 /**254 * Turns off searching the prefix and fallback directories for classes255 * that have not been registered with the class map.256 *257 * @param bool $classMapAuthoritative258 */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 bool268 */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 not278 */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 class296 * @return bool|null True if loaded, null otherwise297 */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 class311 *312 * @return string|false The path if found, false otherwise313 */314 public function findFile($class)315 {316 // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731317 if ('\\' == $class[0]) {318 $class = substr($class, 1);319 }320 321 // class map lookup322 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 HHVM332 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 lookup347 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;348 349 $first = $class[0];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))) {355 return $file;356 }357 }358 }359 }360 }361 362 // PSR-4 fallback dirs363 foreach ($this->fallbackDirsPsr4 as $dir) {364 if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {365 return $file;366 }367 }368 369 // PSR-0 lookup370 if (false !== $pos = strrpos($class, '\\')) {371 // namespaced class name372 $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)373 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);374 } else {375 // PEAR-like class name376 $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;377 }378 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)) {384 return $file;385 }386 }387 }388 }389 }390 391 // PSR-0 fallback dirs392 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 */410function includeFile($file)411{412 include $file;413}414 