PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php

https://gitlab.com/Isaki/le331.fr
PHP | 370 lines | 231 code | 56 blank | 83 comment | 50 complexity | ebf053e23c7b26bd412c9976dffc3a20 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\ClassLoader;
  11. /**
  12. * ClassCollectionLoader.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. */
  16. class ClassCollectionLoader
  17. {
  18. private static $loaded;
  19. private static $seen;
  20. private static $useTokenizer = true;
  21. /**
  22. * Loads a list of classes and caches them in one big file.
  23. *
  24. * @param array $classes An array of classes to load
  25. * @param string $cacheDir A cache directory
  26. * @param string $name The cache name prefix
  27. * @param bool $autoReload Whether to flush the cache when the cache is stale or not
  28. * @param bool $adaptive Whether to remove already declared classes or not
  29. * @param string $extension File extension of the resulting file
  30. *
  31. * @throws \InvalidArgumentException When class can't be loaded
  32. */
  33. public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
  34. {
  35. // each $name can only be loaded once per PHP process
  36. if (isset(self::$loaded[$name])) {
  37. return;
  38. }
  39. self::$loaded[$name] = true;
  40. $declared = array_merge(get_declared_classes(), get_declared_interfaces());
  41. if (function_exists('get_declared_traits')) {
  42. $declared = array_merge($declared, get_declared_traits());
  43. }
  44. if ($adaptive) {
  45. // don't include already declared classes
  46. $classes = array_diff($classes, $declared);
  47. // the cache is different depending on which classes are already declared
  48. $name = $name.'-'.substr(hash('sha256', implode('|', $classes)), 0, 5);
  49. }
  50. $classes = array_unique($classes);
  51. $cache = $cacheDir.'/'.$name.$extension;
  52. // auto-reload
  53. $reload = false;
  54. if ($autoReload) {
  55. $metadata = $cache.'.meta';
  56. if (!is_file($metadata) || !is_file($cache)) {
  57. $reload = true;
  58. } else {
  59. $time = filemtime($cache);
  60. $meta = unserialize(file_get_contents($metadata));
  61. sort($meta[1]);
  62. sort($classes);
  63. if ($meta[1] != $classes) {
  64. $reload = true;
  65. } else {
  66. foreach ($meta[0] as $resource) {
  67. if (!is_file($resource) || filemtime($resource) > $time) {
  68. $reload = true;
  69. break;
  70. }
  71. }
  72. }
  73. }
  74. }
  75. if (!$reload && is_file($cache)) {
  76. require_once $cache;
  77. return;
  78. }
  79. $files = array();
  80. $content = '';
  81. foreach (self::getOrderedClasses($classes) as $class) {
  82. if (in_array($class->getName(), $declared)) {
  83. continue;
  84. }
  85. $files[] = $class->getFileName();
  86. $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName()));
  87. // fakes namespace declaration for global code
  88. if (!$class->inNamespace()) {
  89. $c = "\nnamespace\n{\n".$c."\n}\n";
  90. }
  91. $c = self::fixNamespaceDeclarations('<?php '.$c);
  92. $c = preg_replace('/^\s*<\?php/', '', $c);
  93. $content .= $c;
  94. }
  95. // cache the core classes
  96. if (!is_dir(dirname($cache))) {
  97. mkdir(dirname($cache), 0777, true);
  98. }
  99. self::writeCacheFile($cache, '<?php '.$content);
  100. if ($autoReload) {
  101. // save the resources
  102. self::writeCacheFile($metadata, serialize(array($files, $classes)));
  103. }
  104. }
  105. /**
  106. * Adds brackets around each namespace if it's not already the case.
  107. *
  108. * @param string $source Namespace string
  109. *
  110. * @return string Namespaces with brackets
  111. */
  112. public static function fixNamespaceDeclarations($source)
  113. {
  114. if (!function_exists('token_get_all') || !self::$useTokenizer) {
  115. if (preg_match('/namespace(.*?)\s*;/', $source)) {
  116. $source = preg_replace('/namespace(.*?)\s*;/', "namespace$1\n{", $source)."}\n";
  117. }
  118. return $source;
  119. }
  120. $rawChunk = '';
  121. $output = '';
  122. $inNamespace = false;
  123. $tokens = token_get_all($source);
  124. for (reset($tokens); false !== $token = current($tokens); next($tokens)) {
  125. if (is_string($token)) {
  126. $rawChunk .= $token;
  127. } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
  128. // strip comments
  129. continue;
  130. } elseif (T_NAMESPACE === $token[0]) {
  131. if ($inNamespace) {
  132. $rawChunk .= "}\n";
  133. }
  134. $rawChunk .= $token[1];
  135. // namespace name and whitespaces
  136. while (($t = next($tokens)) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
  137. $rawChunk .= $t[1];
  138. }
  139. if ('{' === $t) {
  140. $inNamespace = false;
  141. prev($tokens);
  142. } else {
  143. $rawChunk = rtrim($rawChunk)."\n{";
  144. $inNamespace = true;
  145. }
  146. } elseif (T_START_HEREDOC === $token[0]) {
  147. $output .= self::compressCode($rawChunk).$token[1];
  148. do {
  149. $token = next($tokens);
  150. $output .= is_string($token) ? $token : $token[1];
  151. } while ($token[0] !== T_END_HEREDOC);
  152. $output .= "\n";
  153. $rawChunk = '';
  154. } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
  155. $output .= self::compressCode($rawChunk).$token[1];
  156. $rawChunk = '';
  157. } else {
  158. $rawChunk .= $token[1];
  159. }
  160. }
  161. if ($inNamespace) {
  162. $rawChunk .= "}\n";
  163. }
  164. return $output.self::compressCode($rawChunk);
  165. }
  166. /**
  167. * This method is only useful for testing.
  168. */
  169. public static function enableTokenizer($bool)
  170. {
  171. self::$useTokenizer = (bool) $bool;
  172. }
  173. /**
  174. * Strips leading & trailing ws, multiple EOL, multiple ws.
  175. *
  176. * @param string $code Original PHP code
  177. *
  178. * @return string compressed code
  179. */
  180. private static function compressCode($code)
  181. {
  182. return preg_replace(
  183. array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
  184. array('', '', "\n", ' '),
  185. $code
  186. );
  187. }
  188. /**
  189. * Writes a cache file.
  190. *
  191. * @param string $file Filename
  192. * @param string $content Temporary file content
  193. *
  194. * @throws \RuntimeException when a cache file cannot be written
  195. */
  196. private static function writeCacheFile($file, $content)
  197. {
  198. $tmpFile = tempnam(dirname($file), basename($file));
  199. if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
  200. @chmod($file, 0666 & ~umask());
  201. return;
  202. }
  203. throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
  204. }
  205. /**
  206. * Gets an ordered array of passed classes including all their dependencies.
  207. *
  208. * @param array $classes
  209. *
  210. * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed)
  211. *
  212. * @throws \InvalidArgumentException When a class can't be loaded
  213. */
  214. private static function getOrderedClasses(array $classes)
  215. {
  216. $map = array();
  217. self::$seen = array();
  218. foreach ($classes as $class) {
  219. try {
  220. $reflectionClass = new \ReflectionClass($class);
  221. } catch (\ReflectionException $e) {
  222. throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
  223. }
  224. $map = array_merge($map, self::getClassHierarchy($reflectionClass));
  225. }
  226. return $map;
  227. }
  228. private static function getClassHierarchy(\ReflectionClass $class)
  229. {
  230. if (isset(self::$seen[$class->getName()])) {
  231. return array();
  232. }
  233. self::$seen[$class->getName()] = true;
  234. $classes = array($class);
  235. $parent = $class;
  236. while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
  237. self::$seen[$parent->getName()] = true;
  238. array_unshift($classes, $parent);
  239. }
  240. $traits = array();
  241. if (method_exists('ReflectionClass', 'getTraits')) {
  242. foreach ($classes as $c) {
  243. foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
  244. if ($trait !== $c) {
  245. $traits[] = $trait;
  246. }
  247. }
  248. }
  249. }
  250. return array_merge(self::getInterfaces($class), $traits, $classes);
  251. }
  252. private static function getInterfaces(\ReflectionClass $class)
  253. {
  254. $classes = array();
  255. foreach ($class->getInterfaces() as $interface) {
  256. $classes = array_merge($classes, self::getInterfaces($interface));
  257. }
  258. if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
  259. self::$seen[$class->getName()] = true;
  260. $classes[] = $class;
  261. }
  262. return $classes;
  263. }
  264. private static function computeTraitDeps(\ReflectionClass $class)
  265. {
  266. $traits = $class->getTraits();
  267. $deps = array($class->getName() => $traits);
  268. while ($trait = array_pop($traits)) {
  269. if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
  270. self::$seen[$trait->getName()] = true;
  271. $traitDeps = $trait->getTraits();
  272. $deps[$trait->getName()] = $traitDeps;
  273. $traits = array_merge($traits, $traitDeps);
  274. }
  275. }
  276. return $deps;
  277. }
  278. /**
  279. * Dependencies resolution.
  280. *
  281. * This function does not check for circular dependencies as it should never
  282. * occur with PHP traits.
  283. *
  284. * @param array $tree The dependency tree
  285. * @param \ReflectionClass $node The node
  286. * @param \ArrayObject $resolved An array of already resolved dependencies
  287. * @param \ArrayObject $unresolved An array of dependencies to be resolved
  288. *
  289. * @return \ArrayObject The dependencies for the given node
  290. *
  291. * @throws \RuntimeException if a circular dependency is detected
  292. */
  293. private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
  294. {
  295. if (null === $resolved) {
  296. $resolved = new \ArrayObject();
  297. }
  298. if (null === $unresolved) {
  299. $unresolved = new \ArrayObject();
  300. }
  301. $nodeName = $node->getName();
  302. if (isset($tree[$nodeName])) {
  303. $unresolved[$nodeName] = $node;
  304. foreach ($tree[$nodeName] as $dependency) {
  305. if (!$resolved->offsetExists($dependency->getName())) {
  306. self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
  307. }
  308. }
  309. $resolved[$nodeName] = $node;
  310. unset($unresolved[$nodeName]);
  311. }
  312. return $resolved;
  313. }
  314. }