PageRenderTime 78ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony/class-loader/ClassCollectionLoader.php

https://gitlab.com/reasonat/test8
PHP | 379 lines | 237 code | 58 blank | 84 comment | 54 complexity | 0f73d3c4616d3ee268ea91280b760edd 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($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
  97. throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir));
  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('/(^|\s)namespace(.*?)\s*;/', $source)) {
  116. $source = preg_replace('/(^|\s)namespace(.*?)\s*;/', "$1namespace$2\n{", $source)."}\n";
  117. }
  118. return $source;
  119. }
  120. $rawChunk = '';
  121. $output = '';
  122. $inNamespace = false;
  123. $tokens = token_get_all($source);
  124. for ($i = 0; isset($tokens[$i]); ++$i) {
  125. $token = $tokens[$i];
  126. if (!isset($token[1]) || 'b"' === $token) {
  127. $rawChunk .= $token;
  128. } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
  129. // strip comments
  130. continue;
  131. } elseif (T_NAMESPACE === $token[0]) {
  132. if ($inNamespace) {
  133. $rawChunk .= "}\n";
  134. }
  135. $rawChunk .= $token[1];
  136. // namespace name and whitespaces
  137. while (isset($tokens[++$i][1]) && in_array($tokens[$i][0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
  138. $rawChunk .= $tokens[$i][1];
  139. }
  140. if ('{' === $tokens[$i]) {
  141. $inNamespace = false;
  142. --$i;
  143. } else {
  144. $rawChunk = rtrim($rawChunk)."\n{";
  145. $inNamespace = true;
  146. }
  147. } elseif (T_START_HEREDOC === $token[0]) {
  148. $output .= self::compressCode($rawChunk).$token[1];
  149. do {
  150. $token = $tokens[++$i];
  151. $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
  152. } while ($token[0] !== T_END_HEREDOC);
  153. $output .= "\n";
  154. $rawChunk = '';
  155. } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
  156. $output .= self::compressCode($rawChunk).$token[1];
  157. $rawChunk = '';
  158. } else {
  159. $rawChunk .= $token[1];
  160. }
  161. }
  162. if ($inNamespace) {
  163. $rawChunk .= "}\n";
  164. }
  165. $output .= self::compressCode($rawChunk);
  166. if (PHP_VERSION_ID >= 70000) {
  167. // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
  168. unset($tokens, $rawChunk);
  169. gc_mem_caches();
  170. }
  171. return $output;
  172. }
  173. /**
  174. * This method is only useful for testing.
  175. */
  176. public static function enableTokenizer($bool)
  177. {
  178. self::$useTokenizer = (bool) $bool;
  179. }
  180. /**
  181. * Strips leading & trailing ws, multiple EOL, multiple ws.
  182. *
  183. * @param string $code Original PHP code
  184. *
  185. * @return string compressed code
  186. */
  187. private static function compressCode($code)
  188. {
  189. return preg_replace(
  190. array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
  191. array('', '', "\n", ' '),
  192. $code
  193. );
  194. }
  195. /**
  196. * Writes a cache file.
  197. *
  198. * @param string $file Filename
  199. * @param string $content Temporary file content
  200. *
  201. * @throws \RuntimeException when a cache file cannot be written
  202. */
  203. private static function writeCacheFile($file, $content)
  204. {
  205. $tmpFile = tempnam(dirname($file), basename($file));
  206. if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
  207. @chmod($file, 0666 & ~umask());
  208. return;
  209. }
  210. throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
  211. }
  212. /**
  213. * Gets an ordered array of passed classes including all their dependencies.
  214. *
  215. * @param array $classes
  216. *
  217. * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed)
  218. *
  219. * @throws \InvalidArgumentException When a class can't be loaded
  220. */
  221. private static function getOrderedClasses(array $classes)
  222. {
  223. $map = array();
  224. self::$seen = array();
  225. foreach ($classes as $class) {
  226. try {
  227. $reflectionClass = new \ReflectionClass($class);
  228. } catch (\ReflectionException $e) {
  229. throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
  230. }
  231. $map = array_merge($map, self::getClassHierarchy($reflectionClass));
  232. }
  233. return $map;
  234. }
  235. private static function getClassHierarchy(\ReflectionClass $class)
  236. {
  237. if (isset(self::$seen[$class->getName()])) {
  238. return array();
  239. }
  240. self::$seen[$class->getName()] = true;
  241. $classes = array($class);
  242. $parent = $class;
  243. while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
  244. self::$seen[$parent->getName()] = true;
  245. array_unshift($classes, $parent);
  246. }
  247. $traits = array();
  248. if (method_exists('ReflectionClass', 'getTraits')) {
  249. foreach ($classes as $c) {
  250. foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
  251. if ($trait !== $c) {
  252. $traits[] = $trait;
  253. }
  254. }
  255. }
  256. }
  257. return array_merge(self::getInterfaces($class), $traits, $classes);
  258. }
  259. private static function getInterfaces(\ReflectionClass $class)
  260. {
  261. $classes = array();
  262. foreach ($class->getInterfaces() as $interface) {
  263. $classes = array_merge($classes, self::getInterfaces($interface));
  264. }
  265. if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
  266. self::$seen[$class->getName()] = true;
  267. $classes[] = $class;
  268. }
  269. return $classes;
  270. }
  271. private static function computeTraitDeps(\ReflectionClass $class)
  272. {
  273. $traits = $class->getTraits();
  274. $deps = array($class->getName() => $traits);
  275. while ($trait = array_pop($traits)) {
  276. if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
  277. self::$seen[$trait->getName()] = true;
  278. $traitDeps = $trait->getTraits();
  279. $deps[$trait->getName()] = $traitDeps;
  280. $traits = array_merge($traits, $traitDeps);
  281. }
  282. }
  283. return $deps;
  284. }
  285. /**
  286. * Dependencies resolution.
  287. *
  288. * This function does not check for circular dependencies as it should never
  289. * occur with PHP traits.
  290. *
  291. * @param array $tree The dependency tree
  292. * @param \ReflectionClass $node The node
  293. * @param \ArrayObject $resolved An array of already resolved dependencies
  294. * @param \ArrayObject $unresolved An array of dependencies to be resolved
  295. *
  296. * @return \ArrayObject The dependencies for the given node
  297. *
  298. * @throws \RuntimeException if a circular dependency is detected
  299. */
  300. private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
  301. {
  302. if (null === $resolved) {
  303. $resolved = new \ArrayObject();
  304. }
  305. if (null === $unresolved) {
  306. $unresolved = new \ArrayObject();
  307. }
  308. $nodeName = $node->getName();
  309. if (isset($tree[$nodeName])) {
  310. $unresolved[$nodeName] = $node;
  311. foreach ($tree[$nodeName] as $dependency) {
  312. if (!$resolved->offsetExists($dependency->getName())) {
  313. self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
  314. }
  315. }
  316. $resolved[$nodeName] = $node;
  317. unset($unresolved[$nodeName]);
  318. }
  319. return $resolved;
  320. }
  321. }