├── .gitignore ├── ApcClassLoader.php ├── CHANGELOG.md ├── ClassCollectionLoader.php ├── ClassLoader.php ├── ClassMapGenerator.php ├── LICENSE ├── MapClassLoader.php ├── Psr4ClassLoader.php ├── README.md ├── Tests ├── ApcClassLoaderTest.php ├── ClassCollectionLoaderTest.php ├── ClassLoaderTest.php ├── ClassMapGeneratorTest.php ├── Fixtures │ ├── Apc │ │ ├── Namespaced │ │ │ ├── Bar.php │ │ │ ├── Baz.php │ │ │ ├── Foo.php │ │ │ └── FooBar.php │ │ ├── Pearlike │ │ │ ├── Bar.php │ │ │ ├── Baz.php │ │ │ └── Foo.php │ │ ├── alpha │ │ │ └── Apc │ │ │ │ ├── ApcPrefixCollision │ │ │ │ └── A │ │ │ │ │ ├── Bar.php │ │ │ │ │ └── Foo.php │ │ │ │ └── NamespaceCollision │ │ │ │ └── A │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ ├── beta │ │ │ └── Apc │ │ │ │ ├── ApcPrefixCollision │ │ │ │ └── A │ │ │ │ │ └── B │ │ │ │ │ ├── Bar.php │ │ │ │ │ └── Foo.php │ │ │ │ └── NamespaceCollision │ │ │ │ └── A │ │ │ │ └── B │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ └── fallback │ │ │ ├── Apc │ │ │ └── Pearlike │ │ │ │ └── FooBar.php │ │ │ └── Namespaced │ │ │ └── FooBar.php │ ├── ClassesWithParents │ │ ├── A.php │ │ ├── ATrait.php │ │ ├── B.php │ │ ├── BTrait.php │ │ ├── CInterface.php │ │ ├── CTrait.php │ │ ├── D.php │ │ ├── E.php │ │ ├── F.php │ │ ├── G.php │ │ └── GInterface.php │ ├── DeclaredClass.php │ ├── DeclaredInterface.php │ ├── Namespaced │ │ ├── Bar.php │ │ ├── Baz.php │ │ ├── Foo.php │ │ ├── WithComments.php │ │ ├── WithDirMagic.php │ │ ├── WithFileMagic.php │ │ ├── WithHaltCompiler.php │ │ └── WithStrictTypes.php │ ├── Namespaced2 │ │ ├── Bar.php │ │ ├── Baz.php │ │ └── Foo.php │ ├── Pearlike │ │ ├── Bar.php │ │ ├── Baz.php │ │ ├── Foo.php │ │ └── WithComments.php │ ├── Pearlike2 │ │ ├── Bar.php │ │ ├── Baz.php │ │ └── Foo.php │ ├── WarmedClass.php │ ├── WarmedInterface.php │ ├── alpha │ │ ├── NamespaceCollision │ │ │ ├── A │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ │ └── C │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ └── PrefixCollision │ │ │ ├── A │ │ │ ├── Bar.php │ │ │ └── Foo.php │ │ │ └── C │ │ │ ├── Bar.php │ │ │ └── Foo.php │ ├── beta │ │ ├── NamespaceCollision │ │ │ ├── A │ │ │ │ └── B │ │ │ │ │ ├── Bar.php │ │ │ │ │ └── Foo.php │ │ │ └── C │ │ │ │ └── B │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ └── PrefixCollision │ │ │ ├── A │ │ │ └── B │ │ │ │ ├── Bar.php │ │ │ │ └── Foo.php │ │ │ └── C │ │ │ └── B │ │ │ ├── Bar.php │ │ │ └── Foo.php │ ├── classmap │ │ ├── SomeClass.php │ │ ├── SomeInterface.php │ │ ├── SomeParent.php │ │ ├── multipleNs.php │ │ ├── notAClass.php │ │ ├── notPhpFile.md │ │ └── sameNsMultipleClasses.php │ ├── deps │ │ └── traits.php │ ├── fallback │ │ ├── Namespaced │ │ │ └── FooBar.php │ │ ├── Namespaced2 │ │ │ └── FooBar.php │ │ ├── Pearlike │ │ │ └── FooBar.php │ │ └── Pearlike2 │ │ │ └── FooBar.php │ ├── includepath │ │ └── Foo.php │ ├── php5.4 │ │ └── traits.php │ ├── php5.5 │ │ └── class_cons.php │ └── psr-4 │ │ ├── Class_With_Underscores.php │ │ ├── Foo.php │ │ └── Lets │ │ └── Go │ │ └── Deeper │ │ ├── Class_With_Underscores.php │ │ └── Foo.php └── Psr4ClassLoaderTest.php ├── WinCacheClassLoader.php ├── XcacheClassLoader.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | phpunit.xml 4 | -------------------------------------------------------------------------------- /ApcClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. 18 | * 19 | * It expects an object implementing a findFile method to find the file. This 20 | * allows using it as a wrapper around the other loaders of the component (the 21 | * ClassLoader for instance) but also around any other autoloaders following 22 | * this convention (the Composer one for instance). 23 | * 24 | * // with a Symfony autoloader 25 | * use Symfony\Component\ClassLoader\ClassLoader; 26 | * 27 | * $loader = new ClassLoader(); 28 | * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); 29 | * $loader->addPrefix('Symfony', __DIR__.'/framework'); 30 | * 31 | * // or with a Composer autoloader 32 | * use Composer\Autoload\ClassLoader; 33 | * 34 | * $loader = new ClassLoader(); 35 | * $loader->add('Symfony\Component', __DIR__.'/component'); 36 | * $loader->add('Symfony', __DIR__.'/framework'); 37 | * 38 | * $cachedLoader = new ApcClassLoader('my_prefix', $loader); 39 | * 40 | * // activate the cached autoloader 41 | * $cachedLoader->register(); 42 | * 43 | * // eventually deactivate the non-cached loader if it was registered previously 44 | * // to be sure to use the cached one. 45 | * $loader->unregister(); 46 | * 47 | * @author Fabien Potencier 48 | * @author Kris Wallsmith 49 | * 50 | * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. 51 | */ 52 | class ApcClassLoader 53 | { 54 | private $prefix; 55 | 56 | /** 57 | * A class loader object that implements the findFile() method. 58 | * 59 | * @var object 60 | */ 61 | protected $decorated; 62 | 63 | /** 64 | * @param string $prefix The APC namespace prefix to use 65 | * @param object $decorated A class loader object that implements the findFile() method 66 | * 67 | * @throws \RuntimeException 68 | * @throws \InvalidArgumentException 69 | */ 70 | public function __construct($prefix, $decorated) 71 | { 72 | if (!\function_exists('apcu_fetch')) { 73 | throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.'); 74 | } 75 | 76 | if (!method_exists($decorated, 'findFile')) { 77 | throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); 78 | } 79 | 80 | $this->prefix = $prefix; 81 | $this->decorated = $decorated; 82 | } 83 | 84 | /** 85 | * Registers this instance as an autoloader. 86 | * 87 | * @param bool $prepend Whether to prepend the autoloader or not 88 | */ 89 | public function register($prepend = false) 90 | { 91 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 92 | } 93 | 94 | /** 95 | * Unregisters this instance as an autoloader. 96 | */ 97 | public function unregister() 98 | { 99 | spl_autoload_unregister([$this, 'loadClass']); 100 | } 101 | 102 | /** 103 | * Loads the given class or interface. 104 | * 105 | * @param string $class The name of the class 106 | * 107 | * @return bool|null True, if loaded 108 | */ 109 | public function loadClass($class) 110 | { 111 | if ($file = $this->findFile($class)) { 112 | require $file; 113 | 114 | return true; 115 | } 116 | 117 | return null; 118 | } 119 | 120 | /** 121 | * Finds a file by class name while caching lookups to APC. 122 | * 123 | * @param string $class A class name to resolve to file 124 | * 125 | * @return string|null 126 | */ 127 | public function findFile($class) 128 | { 129 | $file = apcu_fetch($this->prefix.$class, $success); 130 | 131 | if (!$success) { 132 | apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); 133 | } 134 | 135 | return $file; 136 | } 137 | 138 | /** 139 | * Passes through all unknown calls onto the decorated object. 140 | */ 141 | public function __call($method, $args) 142 | { 143 | return \call_user_func_array([$this->decorated, $method], $args); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 3.3.0 5 | ----- 6 | 7 | * deprecated the component: use Composer instead 8 | 9 | 3.0.0 10 | ----- 11 | 12 | * The DebugClassLoader class has been removed 13 | * The DebugUniversalClassLoader class has been removed 14 | * The UniversalClassLoader class has been removed 15 | * The ApcUniversalClassLoader class has been removed 16 | 17 | 2.4.0 18 | ----- 19 | 20 | * deprecated the UniversalClassLoader in favor of the ClassLoader class instead 21 | * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead 22 | * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component 23 | * deprecated the DebugClassLoader as it has been moved to the Debug component instead 24 | 25 | 2.3.0 26 | ----- 27 | 28 | * added a WinCacheClassLoader for WinCache 29 | 30 | 2.1.0 31 | ----- 32 | 33 | * added a DebugClassLoader able to wrap any autoloader providing a findFile 34 | method 35 | * added a new ApcClassLoader and XcacheClassLoader using composition to wrap 36 | other loaders 37 | * added a new ClassLoader which does not distinguish between namespaced and 38 | pear-like classes (as the PEAR convention is a subset of PSR-0) and 39 | supports using Composer's namespace maps 40 | * added a class map generator 41 | * added support for loading globally-installed PEAR packages 42 | -------------------------------------------------------------------------------- /ClassCollectionLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | if (\PHP_VERSION_ID >= 70000) { 15 | @trigger_error('The '.__NAMESPACE__.'\ClassCollectionLoader class is deprecated since Symfony 3.3 and will be removed in 4.0.', \E_USER_DEPRECATED); 16 | } 17 | 18 | /** 19 | * ClassCollectionLoader. 20 | * 21 | * @author Fabien Potencier 22 | * 23 | * @deprecated since version 3.3, to be removed in 4.0. 24 | */ 25 | class ClassCollectionLoader 26 | { 27 | private static $loaded; 28 | private static $seen; 29 | private static $useTokenizer = true; 30 | 31 | /** 32 | * Loads a list of classes and caches them in one big file. 33 | * 34 | * @param array $classes An array of classes to load 35 | * @param string $cacheDir A cache directory 36 | * @param string $name The cache name prefix 37 | * @param bool $autoReload Whether to flush the cache when the cache is stale or not 38 | * @param bool $adaptive Whether to remove already declared classes or not 39 | * @param string $extension File extension of the resulting file 40 | * 41 | * @throws \InvalidArgumentException When class can't be loaded 42 | */ 43 | public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') 44 | { 45 | // each $name can only be loaded once per PHP process 46 | if (isset(self::$loaded[$name])) { 47 | return; 48 | } 49 | 50 | self::$loaded[$name] = true; 51 | 52 | if ($adaptive) { 53 | $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); 54 | 55 | // don't include already declared classes 56 | $classes = array_diff($classes, $declared); 57 | 58 | // the cache is different depending on which classes are already declared 59 | $name .= '-'.substr(hash('sha256', implode('|', $classes)), 0, 5); 60 | } 61 | 62 | $classes = array_unique($classes); 63 | 64 | // cache the core classes 65 | if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { 66 | throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s".', $cacheDir)); 67 | } 68 | $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.\DIRECTORY_SEPARATOR); 69 | $cache = $cacheDir.'/'.$name.$extension; 70 | 71 | // auto-reload 72 | $reload = false; 73 | if ($autoReload) { 74 | $metadata = $cache.'.meta'; 75 | if (!is_file($metadata) || !is_file($cache)) { 76 | $reload = true; 77 | } else { 78 | $time = filemtime($cache); 79 | $meta = unserialize(file_get_contents($metadata)); 80 | 81 | sort($meta[1]); 82 | sort($classes); 83 | 84 | if ($meta[1] != $classes) { 85 | $reload = true; 86 | } else { 87 | foreach ($meta[0] as $resource) { 88 | if (!is_file($resource) || filemtime($resource) > $time) { 89 | $reload = true; 90 | 91 | break; 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | if (!$reload && file_exists($cache)) { 99 | require_once $cache; 100 | 101 | return; 102 | } 103 | if (!$adaptive) { 104 | $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); 105 | } 106 | 107 | $files = self::inline($classes, $cache, $declared); 108 | 109 | if ($autoReload) { 110 | // save the resources 111 | self::writeCacheFile($metadata, serialize([array_values($files), $classes])); 112 | } 113 | } 114 | 115 | /** 116 | * Generates a file where classes and their parents are inlined. 117 | * 118 | * @param array $classes An array of classes to load 119 | * @param string $cache The file where classes are inlined 120 | * @param array $excluded An array of classes that won't be inlined 121 | * 122 | * @return array The source map of inlined classes, with classes as keys and files as values 123 | * 124 | * @throws \RuntimeException When class can't be loaded 125 | */ 126 | public static function inline($classes, $cache, array $excluded) 127 | { 128 | $declared = []; 129 | foreach (self::getOrderedClasses($excluded) as $class) { 130 | $declared[$class->getName()] = true; 131 | } 132 | 133 | // cache the core classes 134 | $cacheDir = \dirname($cache); 135 | if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { 136 | throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s".', $cacheDir)); 137 | } 138 | 139 | $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?getName()])) { 154 | continue; 155 | } 156 | $declared[$class->getName()] = true; 157 | 158 | $files[$class->getName()] = $file = $class->getFileName(); 159 | $c = file_get_contents($file); 160 | 161 | if (preg_match($dontInlineRegex, $c)) { 162 | $file = explode('/', str_replace(\DIRECTORY_SEPARATOR, '/', $file)); 163 | 164 | for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { 165 | if ($file[$i] !== $cacheDir[$i]) { 166 | break; 167 | } 168 | } 169 | if (1 >= $i) { 170 | $file = var_export(implode('/', $file), true); 171 | } else { 172 | $file = \array_slice($file, $i); 173 | $file = str_repeat('../', \count($cacheDir) - $i).implode('/', $file); 174 | $file = '__DIR__.'.var_export('/'.$file, true); 175 | } 176 | 177 | $c = "\nnamespace {require $file;}"; 178 | } else { 179 | $c = preg_replace(['/^\s*<\?php/', '/\?>\s*$/'], '', $c); 180 | 181 | // fakes namespace declaration for global code 182 | if (!$class->inNamespace()) { 183 | $c = "\nnamespace\n{\n".$c."\n}\n"; 184 | } 185 | 186 | $c = self::fixNamespaceDeclarations(' true, \T_NS_SEPARATOR => true, \T_STRING => true]; 220 | if (\defined('T_NAME_QUALIFIED')) { 221 | $nsTokens[T_NAME_QUALIFIED] = true; 222 | } 223 | 224 | for ($i = 0; isset($tokens[$i]); ++$i) { 225 | $token = $tokens[$i]; 226 | if (!isset($token[1]) || 'b"' === $token) { 227 | $rawChunk .= $token; 228 | } elseif (\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT])) { 229 | // strip comments 230 | continue; 231 | } elseif (\T_NAMESPACE === $token[0]) { 232 | if ($inNamespace) { 233 | $rawChunk .= "}\n"; 234 | } 235 | $rawChunk .= $token[1]; 236 | 237 | // namespace name and whitespaces 238 | while (isset($tokens[++$i][1], $nsTokens[$tokens[$i][0]])) { 239 | $rawChunk .= $tokens[$i][1]; 240 | } 241 | if ('{' === $tokens[$i]) { 242 | $inNamespace = false; 243 | --$i; 244 | } else { 245 | $rawChunk = rtrim($rawChunk)."\n{"; 246 | $inNamespace = true; 247 | } 248 | } elseif (\T_START_HEREDOC === $token[0]) { 249 | $output .= self::compressCode($rawChunk).$token[1]; 250 | do { 251 | $token = $tokens[++$i]; 252 | $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token; 253 | } while (\T_END_HEREDOC !== $token[0]); 254 | $output .= "\n"; 255 | $rawChunk = ''; 256 | } elseif (\T_CONSTANT_ENCAPSED_STRING === $token[0]) { 257 | $output .= self::compressCode($rawChunk).$token[1]; 258 | $rawChunk = ''; 259 | } else { 260 | $rawChunk .= $token[1]; 261 | } 262 | } 263 | 264 | if ($inNamespace) { 265 | $rawChunk .= "}\n"; 266 | } 267 | 268 | $output .= self::compressCode($rawChunk); 269 | 270 | if (\PHP_VERSION_ID >= 70000) { 271 | // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 272 | unset($tokens, $rawChunk); 273 | gc_mem_caches(); 274 | } 275 | 276 | return $output; 277 | } 278 | 279 | /** 280 | * This method is only useful for testing. 281 | */ 282 | public static function enableTokenizer($bool) 283 | { 284 | self::$useTokenizer = (bool) $bool; 285 | } 286 | 287 | /** 288 | * Strips leading & trailing ws, multiple EOL, multiple ws. 289 | * 290 | * @param string $code Original PHP code 291 | * 292 | * @return string compressed code 293 | */ 294 | private static function compressCode($code) 295 | { 296 | return preg_replace( 297 | ['/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'], 298 | ['', '', "\n", ' '], 299 | $code 300 | ); 301 | } 302 | 303 | /** 304 | * Writes a cache file. 305 | * 306 | * @param string $file Filename 307 | * @param string $content Temporary file content 308 | * 309 | * @throws \RuntimeException when a cache file cannot be written 310 | */ 311 | private static function writeCacheFile($file, $content) 312 | { 313 | $dir = \dirname($file); 314 | if (!is_writable($dir)) { 315 | throw new \RuntimeException(sprintf('Cache directory "%s" is not writable.', $dir)); 316 | } 317 | 318 | $tmpFile = tempnam($dir, basename($file)); 319 | 320 | if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { 321 | @chmod($file, 0666 & ~umask()); 322 | 323 | return; 324 | } 325 | 326 | throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); 327 | } 328 | 329 | /** 330 | * Gets an ordered array of passed classes including all their dependencies. 331 | * 332 | * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed) 333 | * 334 | * @throws \InvalidArgumentException When a class can't be loaded 335 | */ 336 | private static function getOrderedClasses(array $classes) 337 | { 338 | $map = []; 339 | self::$seen = []; 340 | foreach ($classes as $class) { 341 | try { 342 | $reflectionClass = new \ReflectionClass($class); 343 | } catch (\ReflectionException $e) { 344 | throw new \InvalidArgumentException(sprintf('Unable to load class "%s".', $class)); 345 | } 346 | 347 | $map = array_merge($map, self::getClassHierarchy($reflectionClass)); 348 | } 349 | 350 | return $map; 351 | } 352 | 353 | private static function getClassHierarchy(\ReflectionClass $class) 354 | { 355 | if (isset(self::$seen[$class->getName()])) { 356 | return []; 357 | } 358 | 359 | self::$seen[$class->getName()] = true; 360 | 361 | $classes = [$class]; 362 | $parent = $class; 363 | while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { 364 | self::$seen[$parent->getName()] = true; 365 | 366 | array_unshift($classes, $parent); 367 | } 368 | 369 | $traits = []; 370 | 371 | foreach ($classes as $c) { 372 | foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { 373 | if ($trait !== $c) { 374 | $traits[] = $trait; 375 | } 376 | } 377 | } 378 | 379 | return array_merge(self::getInterfaces($class), $traits, $classes); 380 | } 381 | 382 | private static function getInterfaces(\ReflectionClass $class) 383 | { 384 | $classes = []; 385 | 386 | foreach ($class->getInterfaces() as $interface) { 387 | $classes = array_merge($classes, self::getInterfaces($interface)); 388 | } 389 | 390 | if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { 391 | self::$seen[$class->getName()] = true; 392 | 393 | $classes[] = $class; 394 | } 395 | 396 | return $classes; 397 | } 398 | 399 | private static function computeTraitDeps(\ReflectionClass $class) 400 | { 401 | $traits = $class->getTraits(); 402 | $deps = [$class->getName() => $traits]; 403 | while ($trait = array_pop($traits)) { 404 | if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { 405 | self::$seen[$trait->getName()] = true; 406 | $traitDeps = $trait->getTraits(); 407 | $deps[$trait->getName()] = $traitDeps; 408 | $traits = array_merge($traits, $traitDeps); 409 | } 410 | } 411 | 412 | return $deps; 413 | } 414 | 415 | /** 416 | * Dependencies resolution. 417 | * 418 | * This function does not check for circular dependencies as it should never 419 | * occur with PHP traits. 420 | * 421 | * @param array $tree The dependency tree 422 | * @param \ReflectionClass $node The node 423 | * @param \ArrayObject $resolved An array of already resolved dependencies 424 | * @param \ArrayObject $unresolved An array of dependencies to be resolved 425 | * 426 | * @return \ArrayObject The dependencies for the given node 427 | * 428 | * @throws \RuntimeException if a circular dependency is detected 429 | */ 430 | private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) 431 | { 432 | if (null === $resolved) { 433 | $resolved = new \ArrayObject(); 434 | } 435 | if (null === $unresolved) { 436 | $unresolved = new \ArrayObject(); 437 | } 438 | $nodeName = $node->getName(); 439 | 440 | if (isset($tree[$nodeName])) { 441 | $unresolved[$nodeName] = $node; 442 | foreach ($tree[$nodeName] as $dependency) { 443 | if (!$resolved->offsetExists($dependency->getName())) { 444 | self::resolveDependencies($tree, $dependency, $resolved, $unresolved); 445 | } 446 | } 447 | $resolved[$nodeName] = $node; 448 | unset($unresolved[$nodeName]); 449 | } 450 | 451 | return $resolved; 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * ClassLoader implements an PSR-0 class loader. 18 | * 19 | * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 20 | * 21 | * $loader = new ClassLoader(); 22 | * 23 | * // register classes with namespaces 24 | * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); 25 | * $loader->addPrefix('Symfony', __DIR__.'/framework'); 26 | * 27 | * // activate the autoloader 28 | * $loader->register(); 29 | * 30 | * // to enable searching the include path (e.g. for PEAR packages) 31 | * $loader->setUseIncludePath(true); 32 | * 33 | * In this example, if you try to use a class in the Symfony\Component 34 | * namespace or one of its children (Symfony\Component\Console for instance), 35 | * the autoloader will first look for the class under the component/ 36 | * directory, and it will then fallback to the framework/ directory if not 37 | * found before giving up. 38 | * 39 | * @author Fabien Potencier 40 | * @author Jordi Boggiano 41 | * 42 | * @deprecated since version 3.3, to be removed in 4.0. 43 | */ 44 | class ClassLoader 45 | { 46 | private $prefixes = []; 47 | private $fallbackDirs = []; 48 | private $useIncludePath = false; 49 | 50 | /** 51 | * Returns prefixes. 52 | * 53 | * @return array 54 | */ 55 | public function getPrefixes() 56 | { 57 | return $this->prefixes; 58 | } 59 | 60 | /** 61 | * Returns fallback directories. 62 | * 63 | * @return array 64 | */ 65 | public function getFallbackDirs() 66 | { 67 | return $this->fallbackDirs; 68 | } 69 | 70 | /** 71 | * Adds prefixes. 72 | * 73 | * @param array $prefixes Prefixes to add 74 | */ 75 | public function addPrefixes(array $prefixes) 76 | { 77 | foreach ($prefixes as $prefix => $path) { 78 | $this->addPrefix($prefix, $path); 79 | } 80 | } 81 | 82 | /** 83 | * Registers a set of classes. 84 | * 85 | * @param string $prefix The classes prefix 86 | * @param array|string $paths The location(s) of the classes 87 | */ 88 | public function addPrefix($prefix, $paths) 89 | { 90 | if (!$prefix) { 91 | foreach ((array) $paths as $path) { 92 | $this->fallbackDirs[] = $path; 93 | } 94 | 95 | return; 96 | } 97 | if (isset($this->prefixes[$prefix])) { 98 | if (\is_array($paths)) { 99 | $this->prefixes[$prefix] = array_unique(array_merge( 100 | $this->prefixes[$prefix], 101 | $paths 102 | )); 103 | } elseif (!\in_array($paths, $this->prefixes[$prefix])) { 104 | $this->prefixes[$prefix][] = $paths; 105 | } 106 | } else { 107 | $this->prefixes[$prefix] = array_unique((array) $paths); 108 | } 109 | } 110 | 111 | /** 112 | * Turns on searching the include for class files. 113 | * 114 | * @param bool $useIncludePath 115 | */ 116 | public function setUseIncludePath($useIncludePath) 117 | { 118 | $this->useIncludePath = (bool) $useIncludePath; 119 | } 120 | 121 | /** 122 | * Can be used to check if the autoloader uses the include path to check 123 | * for classes. 124 | * 125 | * @return bool 126 | */ 127 | public function getUseIncludePath() 128 | { 129 | return $this->useIncludePath; 130 | } 131 | 132 | /** 133 | * Registers this instance as an autoloader. 134 | * 135 | * @param bool $prepend Whether to prepend the autoloader or not 136 | */ 137 | public function register($prepend = false) 138 | { 139 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 140 | } 141 | 142 | /** 143 | * Unregisters this instance as an autoloader. 144 | */ 145 | public function unregister() 146 | { 147 | spl_autoload_unregister([$this, 'loadClass']); 148 | } 149 | 150 | /** 151 | * Loads the given class or interface. 152 | * 153 | * @param string $class The name of the class 154 | * 155 | * @return bool|null True, if loaded 156 | */ 157 | public function loadClass($class) 158 | { 159 | if ($file = $this->findFile($class)) { 160 | require $file; 161 | 162 | return true; 163 | } 164 | 165 | return null; 166 | } 167 | 168 | /** 169 | * Finds the path to the file where the class is defined. 170 | * 171 | * @param string $class The name of the class 172 | * 173 | * @return string|null The path, if found 174 | */ 175 | public function findFile($class) 176 | { 177 | if (false !== $pos = strrpos($class, '\\')) { 178 | // namespaced class name 179 | $classPath = str_replace('\\', \DIRECTORY_SEPARATOR, substr($class, 0, $pos)).\DIRECTORY_SEPARATOR; 180 | $className = substr($class, $pos + 1); 181 | } else { 182 | // PEAR-like class name 183 | $classPath = null; 184 | $className = $class; 185 | } 186 | 187 | $classPath .= str_replace('_', \DIRECTORY_SEPARATOR, $className).'.php'; 188 | 189 | foreach ($this->prefixes as $prefix => $dirs) { 190 | if ($class === strstr($class, $prefix)) { 191 | foreach ($dirs as $dir) { 192 | if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { 193 | return $dir.\DIRECTORY_SEPARATOR.$classPath; 194 | } 195 | } 196 | } 197 | } 198 | 199 | foreach ($this->fallbackDirs as $dir) { 200 | if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { 201 | return $dir.\DIRECTORY_SEPARATOR.$classPath; 202 | } 203 | } 204 | 205 | if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { 206 | return $file; 207 | } 208 | 209 | return null; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /ClassMapGenerator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\ClassMapGenerator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * ClassMapGenerator. 18 | * 19 | * @author Gyula Sallai 20 | * 21 | * @deprecated since version 3.3, to be removed in 4.0. 22 | */ 23 | class ClassMapGenerator 24 | { 25 | /** 26 | * Generate a class map file. 27 | * 28 | * @param array|string $dirs Directories or a single path to search in 29 | * @param string $file The name of the class map file 30 | */ 31 | public static function dump($dirs, $file) 32 | { 33 | $dirs = (array) $dirs; 34 | $maps = []; 35 | 36 | foreach ($dirs as $dir) { 37 | $maps = array_merge($maps, static::createMap($dir)); 38 | } 39 | 40 | file_put_contents($file, sprintf('isFile()) { 60 | continue; 61 | } 62 | 63 | $path = $file->getRealPath() ?: $file->getPathname(); 64 | 65 | if ('php' !== pathinfo($path, \PATHINFO_EXTENSION)) { 66 | continue; 67 | } 68 | 69 | $classes = self::findClasses($path); 70 | 71 | if (\PHP_VERSION_ID >= 70000) { 72 | // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 73 | gc_mem_caches(); 74 | } 75 | 76 | foreach ($classes as $class) { 77 | $map[$class] = $path; 78 | } 79 | } 80 | 81 | return $map; 82 | } 83 | 84 | /** 85 | * Extract the classes in the given file. 86 | * 87 | * @param string $path The file to check 88 | * 89 | * @return array The found classes 90 | */ 91 | private static function findClasses($path) 92 | { 93 | $contents = file_get_contents($path); 94 | $tokens = token_get_all($contents); 95 | 96 | $nsTokens = [\T_STRING => true, \T_NS_SEPARATOR => true]; 97 | if (\defined('T_NAME_QUALIFIED')) { 98 | $nsTokens[T_NAME_QUALIFIED] = true; 99 | } 100 | 101 | $classes = []; 102 | 103 | $namespace = ''; 104 | for ($i = 0; isset($tokens[$i]); ++$i) { 105 | $token = $tokens[$i]; 106 | 107 | if (!isset($token[1])) { 108 | continue; 109 | } 110 | 111 | $class = ''; 112 | 113 | switch ($token[0]) { 114 | case \T_NAMESPACE: 115 | $namespace = ''; 116 | // If there is a namespace, extract it 117 | while (isset($tokens[++$i][1])) { 118 | if (isset($nsTokens[$tokens[$i][0]])) { 119 | $namespace .= $tokens[$i][1]; 120 | } 121 | } 122 | $namespace .= '\\'; 123 | break; 124 | case \T_CLASS: 125 | case \T_INTERFACE: 126 | case \T_TRAIT: 127 | // Skip usage of ::class constant 128 | $isClassConstant = false; 129 | for ($j = $i - 1; $j > 0; --$j) { 130 | if (!isset($tokens[$j][1])) { 131 | break; 132 | } 133 | 134 | if (\T_DOUBLE_COLON === $tokens[$j][0]) { 135 | $isClassConstant = true; 136 | break; 137 | } elseif (!\in_array($tokens[$j][0], [\T_WHITESPACE, \T_DOC_COMMENT, \T_COMMENT])) { 138 | break; 139 | } 140 | } 141 | 142 | if ($isClassConstant) { 143 | break; 144 | } 145 | 146 | // Find the classname 147 | while (isset($tokens[++$i][1])) { 148 | $t = $tokens[$i]; 149 | if (\T_STRING === $t[0]) { 150 | $class .= $t[1]; 151 | } elseif ('' !== $class && \T_WHITESPACE === $t[0]) { 152 | break; 153 | } 154 | } 155 | 156 | $classes[] = ltrim($namespace.$class, '\\'); 157 | break; 158 | default: 159 | break; 160 | } 161 | } 162 | 163 | return $classes; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2020 Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MapClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\MapClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * A class loader that uses a mapping file to look up paths. 18 | * 19 | * @author Fabien Potencier 20 | * 21 | * @deprecated since version 3.3, to be removed in 4.0. 22 | */ 23 | class MapClassLoader 24 | { 25 | private $map = []; 26 | 27 | /** 28 | * @param array $map A map where keys are classes and values the absolute file path 29 | */ 30 | public function __construct(array $map) 31 | { 32 | $this->map = $map; 33 | } 34 | 35 | /** 36 | * Registers this instance as an autoloader. 37 | * 38 | * @param bool $prepend Whether to prepend the autoloader or not 39 | */ 40 | public function register($prepend = false) 41 | { 42 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 43 | } 44 | 45 | /** 46 | * Loads the given class or interface. 47 | * 48 | * @param string $class The name of the class 49 | */ 50 | public function loadClass($class) 51 | { 52 | if (isset($this->map[$class])) { 53 | require $this->map[$class]; 54 | } 55 | } 56 | 57 | /** 58 | * Finds the path to the file where the class is defined. 59 | * 60 | * @param string $class The name of the class 61 | * 62 | * @return string|null The path, if found 63 | */ 64 | public function findFile($class) 65 | { 66 | return isset($this->map[$class]) ? $this->map[$class] : null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Psr4ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\Psr4ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * A PSR-4 compatible class loader. 18 | * 19 | * See http://www.php-fig.org/psr/psr-4/ 20 | * 21 | * @author Alexander M. Turek 22 | * 23 | * @deprecated since version 3.3, to be removed in 4.0. 24 | */ 25 | class Psr4ClassLoader 26 | { 27 | private $prefixes = []; 28 | 29 | /** 30 | * @param string $prefix 31 | * @param string $baseDir 32 | */ 33 | public function addPrefix($prefix, $baseDir) 34 | { 35 | $prefix = trim($prefix, '\\').'\\'; 36 | $baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR; 37 | $this->prefixes[] = [$prefix, $baseDir]; 38 | } 39 | 40 | /** 41 | * @param string $class 42 | * 43 | * @return string|null 44 | */ 45 | public function findFile($class) 46 | { 47 | $class = ltrim($class, '\\'); 48 | 49 | foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { 50 | if (0 === strpos($class, $currentPrefix)) { 51 | $classWithoutPrefix = substr($class, \strlen($currentPrefix)); 52 | $file = $currentBaseDir.str_replace('\\', \DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; 53 | if (file_exists($file)) { 54 | return $file; 55 | } 56 | } 57 | } 58 | 59 | return null; 60 | } 61 | 62 | /** 63 | * @param string $class 64 | * 65 | * @return bool 66 | */ 67 | public function loadClass($class) 68 | { 69 | $file = $this->findFile($class); 70 | if (null !== $file) { 71 | require $file; 72 | 73 | return true; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | /** 80 | * Registers this instance as an autoloader. 81 | * 82 | * @param bool $prepend 83 | */ 84 | public function register($prepend = false) 85 | { 86 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 87 | } 88 | 89 | /** 90 | * Removes this instance from the registered autoloaders. 91 | */ 92 | public function unregister() 93 | { 94 | spl_autoload_unregister([$this, 'loadClass']); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ClassLoader Component 2 | ===================== 3 | 4 | The ClassLoader component provides tools to autoload your classes and cache 5 | their locations for performance. 6 | 7 | Resources 8 | --------- 9 | 10 | * [Documentation](https://symfony.com/doc/current/components/class_loader.html) 11 | * [Contributing](https://symfony.com/doc/current/contributing/index.html) 12 | * [Report issues](https://github.com/symfony/symfony/issues) and 13 | [send Pull Requests](https://github.com/symfony/symfony/pulls) 14 | in the [main Symfony repository](https://github.com/symfony/symfony) 15 | -------------------------------------------------------------------------------- /Tests/ApcClassLoaderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use Symfony\Component\ClassLoader\ApcClassLoader; 16 | use Symfony\Component\ClassLoader\ClassLoader; 17 | 18 | /** 19 | * @group legacy 20 | */ 21 | class ApcClassLoaderTest extends TestCase 22 | { 23 | protected function setUp() 24 | { 25 | if (!(filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) { 26 | $this->markTestSkipped('The apc extension is not enabled.'); 27 | } else { 28 | apcu_clear_cache(); 29 | } 30 | } 31 | 32 | protected function tearDown() 33 | { 34 | if (filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { 35 | apcu_clear_cache(); 36 | } 37 | } 38 | 39 | public function testConstructor() 40 | { 41 | $loader = new ClassLoader(); 42 | $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 43 | 44 | $loader = new ApcClassLoader('test.prefix.', $loader); 45 | 46 | $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); 47 | } 48 | 49 | /** 50 | * @dataProvider getLoadClassTests 51 | */ 52 | public function testLoadClass($className, $testClassName, $message) 53 | { 54 | $loader = new ClassLoader(); 55 | $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 56 | $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 57 | 58 | $loader = new ApcClassLoader('test.prefix.', $loader); 59 | $loader->loadClass($testClassName); 60 | $this->assertTrue(class_exists($className), $message); 61 | } 62 | 63 | public function getLoadClassTests() 64 | { 65 | return [ 66 | ['\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'], 67 | ['Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'], 68 | ]; 69 | } 70 | 71 | /** 72 | * @dataProvider getLoadClassFromFallbackTests 73 | */ 74 | public function testLoadClassFromFallback($className, $testClassName, $message) 75 | { 76 | $loader = new ClassLoader(); 77 | $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 78 | $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 79 | $loader->addPrefix('', [__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback']); 80 | 81 | $loader = new ApcClassLoader('test.prefix.fallback', $loader); 82 | $loader->loadClass($testClassName); 83 | 84 | $this->assertTrue(class_exists($className), $message); 85 | } 86 | 87 | public function getLoadClassFromFallbackTests() 88 | { 89 | return [ 90 | ['\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'], 91 | ['Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'], 92 | ['\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'], 93 | ['Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'], 94 | ]; 95 | } 96 | 97 | /** 98 | * @dataProvider getLoadClassNamespaceCollisionTests 99 | */ 100 | public function testLoadClassNamespaceCollision($namespaces, $className, $message) 101 | { 102 | $loader = new ClassLoader(); 103 | $loader->addPrefixes($namespaces); 104 | 105 | $loader = new ApcClassLoader('test.prefix.collision.', $loader); 106 | $loader->loadClass($className); 107 | 108 | $this->assertTrue(class_exists($className), $message); 109 | } 110 | 111 | public function getLoadClassNamespaceCollisionTests() 112 | { 113 | return [ 114 | [ 115 | [ 116 | 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', 117 | 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', 118 | ], 119 | 'Apc\NamespaceCollision\A\Foo', 120 | '->loadClass() loads NamespaceCollision\A\Foo from alpha.', 121 | ], 122 | [ 123 | [ 124 | 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', 125 | 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', 126 | ], 127 | 'Apc\NamespaceCollision\A\Bar', 128 | '->loadClass() loads NamespaceCollision\A\Bar from alpha.', 129 | ], 130 | [ 131 | [ 132 | 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', 133 | 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', 134 | ], 135 | 'Apc\NamespaceCollision\A\B\Foo', 136 | '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', 137 | ], 138 | [ 139 | [ 140 | 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', 141 | 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', 142 | ], 143 | 'Apc\NamespaceCollision\A\B\Bar', 144 | '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', 145 | ], 146 | ]; 147 | } 148 | 149 | /** 150 | * @dataProvider getLoadClassPrefixCollisionTests 151 | */ 152 | public function testLoadClassPrefixCollision($prefixes, $className, $message) 153 | { 154 | $loader = new ClassLoader(); 155 | $loader->addPrefixes($prefixes); 156 | 157 | $loader = new ApcClassLoader('test.prefix.collision.', $loader); 158 | $loader->loadClass($className); 159 | 160 | $this->assertTrue(class_exists($className), $message); 161 | } 162 | 163 | public function getLoadClassPrefixCollisionTests() 164 | { 165 | return [ 166 | [ 167 | [ 168 | 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', 169 | 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', 170 | ], 171 | 'ApcPrefixCollision_A_Foo', 172 | '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', 173 | ], 174 | [ 175 | [ 176 | 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', 177 | 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', 178 | ], 179 | 'ApcPrefixCollision_A_Bar', 180 | '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', 181 | ], 182 | [ 183 | [ 184 | 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', 185 | 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', 186 | ], 187 | 'ApcPrefixCollision_A_B_Foo', 188 | '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', 189 | ], 190 | [ 191 | [ 192 | 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', 193 | 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', 194 | ], 195 | 'ApcPrefixCollision_A_B_Bar', 196 | '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', 197 | ], 198 | ]; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Tests/ClassCollectionLoaderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use Symfony\Component\ClassLoader\ClassCollectionLoader; 16 | use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass; 17 | use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass; 18 | 19 | require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; 20 | require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; 21 | require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; 22 | require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; 23 | 24 | /** 25 | * @group legacy 26 | */ 27 | class ClassCollectionLoaderTest extends TestCase 28 | { 29 | public function testTraitDependencies() 30 | { 31 | require_once __DIR__.'/Fixtures/deps/traits.php'; 32 | 33 | $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); 34 | $m = $r->getMethod('getOrderedClasses'); 35 | $m->setAccessible(true); 36 | 37 | $ordered = $m->invoke(null, ['CTFoo']); 38 | 39 | $this->assertEquals( 40 | ['TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'], 41 | array_map(function ($class) { return $class->getName(); }, $ordered) 42 | ); 43 | 44 | $ordered = $m->invoke(null, ['CTBar']); 45 | 46 | $this->assertEquals( 47 | ['TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'], 48 | array_map(function ($class) { return $class->getName(); }, $ordered) 49 | ); 50 | } 51 | 52 | /** 53 | * @dataProvider getDifferentOrders 54 | */ 55 | public function testClassReordering(array $classes) 56 | { 57 | $expected = [ 58 | 'ClassesWithParents\\GInterface', 59 | 'ClassesWithParents\\CInterface', 60 | 'ClassesWithParents\\B', 61 | 'ClassesWithParents\\A', 62 | ]; 63 | 64 | $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); 65 | $m = $r->getMethod('getOrderedClasses'); 66 | $m->setAccessible(true); 67 | 68 | $ordered = $m->invoke(null, $classes); 69 | 70 | $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); 71 | } 72 | 73 | public function getDifferentOrders() 74 | { 75 | return [ 76 | [[ 77 | 'ClassesWithParents\\A', 78 | 'ClassesWithParents\\CInterface', 79 | 'ClassesWithParents\\GInterface', 80 | 'ClassesWithParents\\B', 81 | ]], 82 | [[ 83 | 'ClassesWithParents\\B', 84 | 'ClassesWithParents\\A', 85 | 'ClassesWithParents\\CInterface', 86 | ]], 87 | [[ 88 | 'ClassesWithParents\\CInterface', 89 | 'ClassesWithParents\\B', 90 | 'ClassesWithParents\\A', 91 | ]], 92 | [[ 93 | 'ClassesWithParents\\A', 94 | ]], 95 | ]; 96 | } 97 | 98 | /** 99 | * @dataProvider getDifferentOrdersForTraits 100 | */ 101 | public function testClassWithTraitsReordering(array $classes) 102 | { 103 | require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; 104 | require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; 105 | require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; 106 | require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; 107 | require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; 108 | 109 | $expected = [ 110 | 'ClassesWithParents\\GInterface', 111 | 'ClassesWithParents\\CInterface', 112 | 'ClassesWithParents\\ATrait', 113 | 'ClassesWithParents\\BTrait', 114 | 'ClassesWithParents\\CTrait', 115 | 'ClassesWithParents\\B', 116 | 'ClassesWithParents\\A', 117 | 'ClassesWithParents\\D', 118 | 'ClassesWithParents\\E', 119 | ]; 120 | 121 | $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); 122 | $m = $r->getMethod('getOrderedClasses'); 123 | $m->setAccessible(true); 124 | 125 | $ordered = $m->invoke(null, $classes); 126 | 127 | $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); 128 | } 129 | 130 | public function getDifferentOrdersForTraits() 131 | { 132 | return [ 133 | [[ 134 | 'ClassesWithParents\\E', 135 | 'ClassesWithParents\\ATrait', 136 | ]], 137 | [[ 138 | 'ClassesWithParents\\E', 139 | ]], 140 | ]; 141 | } 142 | 143 | public function testFixClassWithTraitsOrdering() 144 | { 145 | require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; 146 | require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; 147 | require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; 148 | 149 | $classes = [ 150 | 'ClassesWithParents\\F', 151 | 'ClassesWithParents\\G', 152 | ]; 153 | 154 | $expected = [ 155 | 'ClassesWithParents\\CTrait', 156 | 'ClassesWithParents\\F', 157 | 'ClassesWithParents\\G', 158 | ]; 159 | 160 | $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); 161 | $m = $r->getMethod('getOrderedClasses'); 162 | $m->setAccessible(true); 163 | 164 | $ordered = $m->invoke(null, $classes); 165 | 166 | $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); 167 | } 168 | 169 | /** 170 | * @dataProvider getFixNamespaceDeclarationsData 171 | */ 172 | public function testFixNamespaceDeclarations($source, $expected) 173 | { 174 | $this->assertEquals('assertEquals('expectException('InvalidArgumentException'); 214 | if (is_file($file = sys_get_temp_dir().'/foo.php')) { 215 | unlink($file); 216 | } 217 | 218 | ClassCollectionLoader::load(['SomeNotExistingClass'], sys_get_temp_dir(), 'foo', false); 219 | } 220 | 221 | public function testCommentStripping() 222 | { 223 | if (is_file($file = __DIR__.'/bar.php')) { 224 | unlink($file); 225 | } 226 | spl_autoload_register($r = function ($class) { 227 | if (0 === strpos($class, 'Namespaced') || 0 === strpos($class, 'Pearlike_')) { 228 | @require_once __DIR__.'/Fixtures/'.str_replace(['\\', '_'], '/', $class).'.php'; 229 | } 230 | }); 231 | 232 | $strictTypes = \defined('HHVM_VERSION') ? '' : "\nnamespace {require __DIR__.'/Fixtures/Namespaced/WithStrictTypes.php';}"; 233 | 234 | ClassCollectionLoader::load( 235 | ['Namespaced\\WithComments', 'Pearlike_WithComments', 'Namespaced\\WithDirMagic', 'Namespaced\\WithFileMagic', 'Namespaced\\WithHaltCompiler', $strictTypes ? 'Namespaced\\WithStrictTypes' : 'Namespaced\\WithComments'], 236 | __DIR__, 237 | 'bar', 238 | false 239 | ); 240 | 241 | spl_autoload_unregister($r); 242 | 243 | $this->assertEquals(<<<'EOF' 244 | namespace Namespaced 245 | { 246 | class WithComments 247 | { 248 | public static $loaded = true; 249 | } 250 | $string ='string should not be modified {$string}'; 251 | $heredoc = (<<assertTrue(class_exists(WarmedClass::class, true)); 289 | 290 | @unlink($cache = sys_get_temp_dir().'/inline.php'); 291 | 292 | $classes = [WarmedClass::class]; 293 | $excluded = [DeclaredClass::class]; 294 | 295 | ClassCollectionLoader::inline($classes, $cache, $excluded); 296 | 297 | $this->assertSame(<<<'EOTXT' 298 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use Symfony\Component\ClassLoader\ClassLoader; 16 | 17 | /** 18 | * @group legacy 19 | */ 20 | class ClassLoaderTest extends TestCase 21 | { 22 | public function testGetPrefixes() 23 | { 24 | $loader = new ClassLoader(); 25 | $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 26 | $loader->addPrefix('Bar', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 27 | $loader->addPrefix('Bas', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 28 | $prefixes = $loader->getPrefixes(); 29 | $this->assertArrayHasKey('Foo', $prefixes); 30 | $this->assertArrayNotHasKey('Foo1', $prefixes); 31 | $this->assertArrayHasKey('Bar', $prefixes); 32 | $this->assertArrayHasKey('Bas', $prefixes); 33 | } 34 | 35 | public function testGetFallbackDirs() 36 | { 37 | $loader = new ClassLoader(); 38 | $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 39 | $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 40 | $fallback_dirs = $loader->getFallbackDirs(); 41 | $this->assertCount(2, $fallback_dirs); 42 | } 43 | 44 | /** 45 | * @dataProvider getLoadClassTests 46 | */ 47 | public function testLoadClass($className, $testClassName, $message) 48 | { 49 | $loader = new ClassLoader(); 50 | $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 51 | $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 52 | $loader->loadClass($testClassName); 53 | $this->assertTrue(class_exists($className), $message); 54 | } 55 | 56 | public function getLoadClassTests() 57 | { 58 | return [ 59 | ['\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'], 60 | ['\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'], 61 | ]; 62 | } 63 | 64 | /** 65 | * @dataProvider getLoadNonexistentClassTests 66 | */ 67 | public function testLoadNonexistentClass($className, $testClassName, $message) 68 | { 69 | $loader = new ClassLoader(); 70 | $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 71 | $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 72 | $loader->loadClass($testClassName); 73 | $this->assertFalse(class_exists($className), $message); 74 | } 75 | 76 | public function getLoadNonexistentClassTests() 77 | { 78 | return [ 79 | ['\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'], 80 | ]; 81 | } 82 | 83 | public function testAddPrefixSingle() 84 | { 85 | $loader = new ClassLoader(); 86 | $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 87 | $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 88 | $prefixes = $loader->getPrefixes(); 89 | $this->assertArrayHasKey('Foo', $prefixes); 90 | $this->assertCount(1, $prefixes['Foo']); 91 | } 92 | 93 | public function testAddPrefixesSingle() 94 | { 95 | $loader = new ClassLoader(); 96 | $loader->addPrefixes(['Foo' => ['foo', 'foo']]); 97 | $loader->addPrefixes(['Foo' => ['foo']]); 98 | $prefixes = $loader->getPrefixes(); 99 | $this->assertArrayHasKey('Foo', $prefixes); 100 | $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); 101 | } 102 | 103 | public function testAddPrefixMulti() 104 | { 105 | $loader = new ClassLoader(); 106 | $loader->addPrefix('Foo', 'foo'); 107 | $loader->addPrefix('Foo', 'bar'); 108 | $prefixes = $loader->getPrefixes(); 109 | $this->assertArrayHasKey('Foo', $prefixes); 110 | $this->assertCount(2, $prefixes['Foo']); 111 | $this->assertContains('foo', $prefixes['Foo']); 112 | $this->assertContains('bar', $prefixes['Foo']); 113 | } 114 | 115 | public function testUseIncludePath() 116 | { 117 | $loader = new ClassLoader(); 118 | $this->assertFalse($loader->getUseIncludePath()); 119 | 120 | $this->assertNull($loader->findFile('Foo')); 121 | 122 | $includePath = get_include_path(); 123 | 124 | $loader->setUseIncludePath(true); 125 | $this->assertTrue($loader->getUseIncludePath()); 126 | 127 | set_include_path(__DIR__.'/Fixtures/includepath'.\PATH_SEPARATOR.$includePath); 128 | 129 | $this->assertEquals(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'includepath'.\DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); 130 | 131 | set_include_path($includePath); 132 | } 133 | 134 | /** 135 | * @dataProvider getLoadClassFromFallbackTests 136 | */ 137 | public function testLoadClassFromFallback($className, $testClassName, $message) 138 | { 139 | $loader = new ClassLoader(); 140 | $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 141 | $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); 142 | $loader->addPrefix('', [__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/fallback']); 143 | $loader->loadClass($testClassName); 144 | $this->assertTrue(class_exists($className), $message); 145 | } 146 | 147 | public function getLoadClassFromFallbackTests() 148 | { 149 | return [ 150 | ['\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'], 151 | ['\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'], 152 | ['\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'], 153 | ['\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'], 154 | ]; 155 | } 156 | 157 | /** 158 | * @dataProvider getLoadClassNamespaceCollisionTests 159 | */ 160 | public function testLoadClassNamespaceCollision($namespaces, $className, $message) 161 | { 162 | $loader = new ClassLoader(); 163 | $loader->addPrefixes($namespaces); 164 | 165 | $loader->loadClass($className); 166 | $this->assertTrue(class_exists($className), $message); 167 | } 168 | 169 | public function getLoadClassNamespaceCollisionTests() 170 | { 171 | return [ 172 | [ 173 | [ 174 | 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 175 | 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 176 | ], 177 | 'NamespaceCollision\C\Foo', 178 | '->loadClass() loads NamespaceCollision\C\Foo from alpha.', 179 | ], 180 | [ 181 | [ 182 | 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 183 | 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 184 | ], 185 | 'NamespaceCollision\C\Bar', 186 | '->loadClass() loads NamespaceCollision\C\Bar from alpha.', 187 | ], 188 | [ 189 | [ 190 | 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 191 | 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 192 | ], 193 | 'NamespaceCollision\C\B\Foo', 194 | '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', 195 | ], 196 | [ 197 | [ 198 | 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 199 | 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 200 | ], 201 | 'NamespaceCollision\C\B\Bar', 202 | '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', 203 | ], 204 | [ 205 | [ 206 | 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 207 | 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 208 | ], 209 | 'PrefixCollision_C_Foo', 210 | '->loadClass() loads PrefixCollision_C_Foo from alpha.', 211 | ], 212 | [ 213 | [ 214 | 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 215 | 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 216 | ], 217 | 'PrefixCollision_C_Bar', 218 | '->loadClass() loads PrefixCollision_C_Bar from alpha.', 219 | ], 220 | [ 221 | [ 222 | 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 223 | 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 224 | ], 225 | 'PrefixCollision_C_B_Foo', 226 | '->loadClass() loads PrefixCollision_C_B_Foo from beta.', 227 | ], 228 | [ 229 | [ 230 | 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', 231 | 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', 232 | ], 233 | 'PrefixCollision_C_B_Bar', 234 | '->loadClass() loads PrefixCollision_C_B_Bar from beta.', 235 | ], 236 | ]; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Tests/ClassMapGeneratorTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use Symfony\Component\ClassLoader\ClassMapGenerator; 16 | 17 | /** 18 | * @group legacy 19 | */ 20 | class ClassMapGeneratorTest extends TestCase 21 | { 22 | /** 23 | * @var string|null 24 | */ 25 | private $workspace = null; 26 | 27 | public function prepare_workspace() 28 | { 29 | $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); 30 | mkdir($this->workspace, 0777, true); 31 | $this->workspace = realpath($this->workspace); 32 | } 33 | 34 | /** 35 | * @param string $file 36 | */ 37 | private function clean($file) 38 | { 39 | if (is_dir($file) && !is_link($file)) { 40 | $dir = new \FilesystemIterator($file); 41 | foreach ($dir as $childFile) { 42 | $this->clean($childFile); 43 | } 44 | 45 | rmdir($file); 46 | } else { 47 | unlink($file); 48 | } 49 | } 50 | 51 | /** 52 | * @dataProvider getTestCreateMapTests 53 | */ 54 | public function testDump($directory) 55 | { 56 | $this->prepare_workspace(); 57 | 58 | $file = $this->workspace.'/file'; 59 | 60 | $generator = new ClassMapGenerator(); 61 | $generator->dump($directory, $file); 62 | $this->assertFileExists($file); 63 | 64 | $this->clean($this->workspace); 65 | } 66 | 67 | /** 68 | * @dataProvider getTestCreateMapTests 69 | */ 70 | public function testCreateMap($directory, $expected) 71 | { 72 | $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); 73 | } 74 | 75 | public function getTestCreateMapTests() 76 | { 77 | $data = [ 78 | [__DIR__.'/Fixtures/Namespaced', [ 79 | 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', 80 | 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', 81 | 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', 82 | 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', 83 | 'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php', 84 | 'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php', 85 | 'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php', 86 | 'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php', 87 | ]], 88 | [__DIR__.'/Fixtures/beta/NamespaceCollision', [ 89 | 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', 90 | 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', 91 | 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', 92 | 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', 93 | ]], 94 | [__DIR__.'/Fixtures/Pearlike', [ 95 | 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', 96 | 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', 97 | 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', 98 | 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', 99 | ]], 100 | [__DIR__.'/Fixtures/classmap', [ 101 | 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', 102 | 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', 103 | 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', 104 | 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', 105 | 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', 106 | 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', 107 | 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', 108 | 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', 109 | 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', 110 | 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', 111 | ]], 112 | [__DIR__.'/Fixtures/php5.4', [ 113 | 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', 114 | 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', 115 | 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', 116 | 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', 117 | 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', 118 | 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', 119 | ]], 120 | [__DIR__.'/Fixtures/php5.5', [ 121 | 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', 122 | ]], 123 | ]; 124 | 125 | return $data; 126 | } 127 | 128 | public function testCreateMapFinderSupport() 129 | { 130 | $finder = new \Symfony\Component\Finder\Finder(); 131 | $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); 132 | 133 | $this->assertEqualsNormalized([ 134 | 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', 135 | 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', 136 | 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', 137 | 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', 138 | ], ClassMapGenerator::createMap($finder)); 139 | } 140 | 141 | protected function assertEqualsNormalized($expected, $actual, $message = '') 142 | { 143 | foreach ($expected as $ns => $path) { 144 | $expected[$ns] = str_replace('\\', '/', $path); 145 | } 146 | foreach ($actual as $ns => $path) { 147 | $actual[$ns] = str_replace('\\', '/', $path); 148 | } 149 | $this->assertEquals($expected, $actual, $message); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/Namespaced/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\Namespaced; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/Namespaced/Baz.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\Namespaced; 13 | 14 | class Baz 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/Namespaced/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\Namespaced; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/Namespaced/FooBar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\Namespaced; 13 | 14 | class FooBar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/Pearlike/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\NamespaceCollision\A; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\NamespaceCollision\A; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\NamespaceCollision\A\B; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\NamespaceCollision\A\B; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Apc\Namespaced; 13 | 14 | class FooBar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/ClassesWithParents/A.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Namespaced; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Namespaced/Baz.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Namespaced; 13 | 14 | class Baz 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Namespaced/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Namespaced; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/Namespaced/WithComments.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Namespaced; 13 | 14 | class WithComments 15 | { 16 | /** @Boolean */ 17 | public static $loaded = true; 18 | } 19 | 20 | $string = 'string should not be modified {$string}'; 21 | 22 | $heredoc = (<< 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | class Pearlike_WithComments 13 | { 14 | /** @Boolean */ 15 | public static $loaded = true; 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Fixtures/Pearlike2/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace NamespaceCollision\A; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace NamespaceCollision\A; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace NamespaceCollision\A\B; 13 | 14 | class Bar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace NamespaceCollision\A\B; 13 | 14 | class Foo 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ClassMap; 13 | 14 | class SomeClass extends SomeParent implements SomeInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Fixtures/classmap/SomeInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ClassMap; 13 | 14 | interface SomeInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Fixtures/classmap/SomeParent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace ClassMap; 13 | 14 | abstract class SomeParent 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Fixtures/classmap/multipleNs.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Foo\Bar; 13 | 14 | class A 15 | { 16 | } 17 | class B 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Fixtures/deps/traits.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Namespaced; 13 | 14 | class FooBar 15 | { 16 | public static $loaded = true; 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Fixtures/fallback/Namespaced2/FooBar.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader\Tests; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | use Symfony\Component\ClassLoader\Psr4ClassLoader; 16 | 17 | /** 18 | * @group legacy 19 | */ 20 | class Psr4ClassLoaderTest extends TestCase 21 | { 22 | /** 23 | * @param string $className 24 | * @dataProvider getLoadClassTests 25 | */ 26 | public function testLoadClass($className) 27 | { 28 | $loader = new Psr4ClassLoader(); 29 | $loader->addPrefix( 30 | 'Acme\\DemoLib', 31 | __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' 32 | ); 33 | $loader->loadClass($className); 34 | $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); 35 | } 36 | 37 | /** 38 | * @return array 39 | */ 40 | public function getLoadClassTests() 41 | { 42 | return [ 43 | ['Acme\\DemoLib\\Foo'], 44 | ['Acme\\DemoLib\\Class_With_Underscores'], 45 | ['Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'], 46 | ['Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'], 47 | ]; 48 | } 49 | 50 | /** 51 | * @param string $className 52 | * @dataProvider getLoadNonexistentClassTests 53 | */ 54 | public function testLoadNonexistentClass($className) 55 | { 56 | $loader = new Psr4ClassLoader(); 57 | $loader->addPrefix( 58 | 'Acme\\DemoLib', 59 | __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' 60 | ); 61 | $loader->loadClass($className); 62 | $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); 63 | } 64 | 65 | /** 66 | * @return array 67 | */ 68 | public function getLoadNonexistentClassTests() 69 | { 70 | return [ 71 | ['Acme\\DemoLib\\I_Do_Not_Exist'], 72 | ['UnknownVendor\\SomeLib\\I_Do_Not_Exist'], 73 | ]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /WinCacheClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. 18 | * 19 | * It expects an object implementing a findFile method to find the file. This 20 | * allow using it as a wrapper around the other loaders of the component (the 21 | * ClassLoader for instance) but also around any other autoloaders following 22 | * this convention (the Composer one for instance). 23 | * 24 | * // with a Symfony autoloader 25 | * $loader = new ClassLoader(); 26 | * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); 27 | * $loader->addPrefix('Symfony', __DIR__.'/framework'); 28 | * 29 | * // or with a Composer autoloader 30 | * use Composer\Autoload\ClassLoader; 31 | * 32 | * $loader = new ClassLoader(); 33 | * $loader->add('Symfony\Component', __DIR__.'/component'); 34 | * $loader->add('Symfony', __DIR__.'/framework'); 35 | * 36 | * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); 37 | * 38 | * // activate the cached autoloader 39 | * $cachedLoader->register(); 40 | * 41 | * // eventually deactivate the non-cached loader if it was registered previously 42 | * // to be sure to use the cached one. 43 | * $loader->unregister(); 44 | * 45 | * @author Fabien Potencier 46 | * @author Kris Wallsmith 47 | * @author Artem Ryzhkov 48 | * 49 | * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. 50 | */ 51 | class WinCacheClassLoader 52 | { 53 | private $prefix; 54 | 55 | /** 56 | * A class loader object that implements the findFile() method. 57 | * 58 | * @var object 59 | */ 60 | protected $decorated; 61 | 62 | /** 63 | * @param string $prefix The WinCache namespace prefix to use 64 | * @param object $decorated A class loader object that implements the findFile() method 65 | * 66 | * @throws \RuntimeException 67 | * @throws \InvalidArgumentException 68 | */ 69 | public function __construct($prefix, $decorated) 70 | { 71 | if (!\extension_loaded('wincache')) { 72 | throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); 73 | } 74 | 75 | if (!method_exists($decorated, 'findFile')) { 76 | throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); 77 | } 78 | 79 | $this->prefix = $prefix; 80 | $this->decorated = $decorated; 81 | } 82 | 83 | /** 84 | * Registers this instance as an autoloader. 85 | * 86 | * @param bool $prepend Whether to prepend the autoloader or not 87 | */ 88 | public function register($prepend = false) 89 | { 90 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 91 | } 92 | 93 | /** 94 | * Unregisters this instance as an autoloader. 95 | */ 96 | public function unregister() 97 | { 98 | spl_autoload_unregister([$this, 'loadClass']); 99 | } 100 | 101 | /** 102 | * Loads the given class or interface. 103 | * 104 | * @param string $class The name of the class 105 | * 106 | * @return bool|null True, if loaded 107 | */ 108 | public function loadClass($class) 109 | { 110 | if ($file = $this->findFile($class)) { 111 | require $file; 112 | 113 | return true; 114 | } 115 | 116 | return null; 117 | } 118 | 119 | /** 120 | * Finds a file by class name while caching lookups to WinCache. 121 | * 122 | * @param string $class A class name to resolve to file 123 | * 124 | * @return string|null 125 | */ 126 | public function findFile($class) 127 | { 128 | $file = wincache_ucache_get($this->prefix.$class, $success); 129 | 130 | if (!$success) { 131 | wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); 132 | } 133 | 134 | return $file; 135 | } 136 | 137 | /** 138 | * Passes through all unknown calls onto the decorated object. 139 | */ 140 | public function __call($method, $args) 141 | { 142 | return \call_user_func_array([$this->decorated, $method], $args); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /XcacheClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\ClassLoader; 13 | 14 | @trigger_error('The '.__NAMESPACE__.'\XcacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', \E_USER_DEPRECATED); 15 | 16 | /** 17 | * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. 18 | * 19 | * It expects an object implementing a findFile method to find the file. This 20 | * allows using it as a wrapper around the other loaders of the component (the 21 | * ClassLoader for instance) but also around any other autoloaders following 22 | * this convention (the Composer one for instance). 23 | * 24 | * // with a Symfony autoloader 25 | * $loader = new ClassLoader(); 26 | * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); 27 | * $loader->addPrefix('Symfony', __DIR__.'/framework'); 28 | * 29 | * // or with a Composer autoloader 30 | * use Composer\Autoload\ClassLoader; 31 | * 32 | * $loader = new ClassLoader(); 33 | * $loader->add('Symfony\Component', __DIR__.'/component'); 34 | * $loader->add('Symfony', __DIR__.'/framework'); 35 | * 36 | * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); 37 | * 38 | * // activate the cached autoloader 39 | * $cachedLoader->register(); 40 | * 41 | * // eventually deactivate the non-cached loader if it was registered previously 42 | * // to be sure to use the cached one. 43 | * $loader->unregister(); 44 | * 45 | * @author Fabien Potencier 46 | * @author Kris Wallsmith 47 | * @author Kim Hemsø Rasmussen 48 | * 49 | * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. 50 | */ 51 | class XcacheClassLoader 52 | { 53 | private $prefix; 54 | private $decorated; 55 | 56 | /** 57 | * @param string $prefix The XCache namespace prefix to use 58 | * @param object $decorated A class loader object that implements the findFile() method 59 | * 60 | * @throws \RuntimeException 61 | * @throws \InvalidArgumentException 62 | */ 63 | public function __construct($prefix, $decorated) 64 | { 65 | if (!\extension_loaded('xcache')) { 66 | throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); 67 | } 68 | 69 | if (!method_exists($decorated, 'findFile')) { 70 | throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); 71 | } 72 | 73 | $this->prefix = $prefix; 74 | $this->decorated = $decorated; 75 | } 76 | 77 | /** 78 | * Registers this instance as an autoloader. 79 | * 80 | * @param bool $prepend Whether to prepend the autoloader or not 81 | */ 82 | public function register($prepend = false) 83 | { 84 | spl_autoload_register([$this, 'loadClass'], true, $prepend); 85 | } 86 | 87 | /** 88 | * Unregisters this instance as an autoloader. 89 | */ 90 | public function unregister() 91 | { 92 | spl_autoload_unregister([$this, 'loadClass']); 93 | } 94 | 95 | /** 96 | * Loads the given class or interface. 97 | * 98 | * @param string $class The name of the class 99 | * 100 | * @return bool|null True, if loaded 101 | */ 102 | public function loadClass($class) 103 | { 104 | if ($file = $this->findFile($class)) { 105 | require $file; 106 | 107 | return true; 108 | } 109 | 110 | return null; 111 | } 112 | 113 | /** 114 | * Finds a file by class name while caching lookups to Xcache. 115 | * 116 | * @param string $class A class name to resolve to file 117 | * 118 | * @return string|null 119 | */ 120 | public function findFile($class) 121 | { 122 | if (xcache_isset($this->prefix.$class)) { 123 | $file = xcache_get($this->prefix.$class); 124 | } else { 125 | $file = $this->decorated->findFile($class) ?: null; 126 | xcache_set($this->prefix.$class, $file); 127 | } 128 | 129 | return $file; 130 | } 131 | 132 | /** 133 | * Passes through all unknown calls onto the decorated object. 134 | */ 135 | public function __call($method, $args) 136 | { 137 | return \call_user_func_array([$this->decorated, $method], $args); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/class-loader", 3 | "type": "library", 4 | "description": "Symfony ClassLoader Component", 5 | "keywords": [], 6 | "homepage": "https://symfony.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Fabien Potencier", 11 | "email": "fabien@symfony.com" 12 | }, 13 | { 14 | "name": "Symfony Community", 15 | "homepage": "https://symfony.com/contributors" 16 | } 17 | ], 18 | "minimum-stability": "dev", 19 | "require": { 20 | "php": "^5.5.9|>=7.0.8" 21 | }, 22 | "require-dev": { 23 | "symfony/finder": "~2.8|~3.0|~4.0", 24 | "symfony/polyfill-apcu": "~1.1" 25 | }, 26 | "suggest": { 27 | "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" 28 | }, 29 | "autoload": { 30 | "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, 31 | "exclude-from-classmap": [ 32 | "/Tests/" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ./Tests/ 18 | 19 | 20 | 21 | 22 | 23 | ./ 24 | 25 | ./Resources 26 | ./Tests 27 | ./vendor 28 | 29 | 30 | 31 | 32 | --------------------------------------------------------------------------------