PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/AdriVanHoudt/school
PHP | 365 lines | 228 code | 54 blank | 83 comment | 49 complexity | 3931d3149cc1f98588e1b53137536813 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 Boolean $autoReload Whether to flush the cache when the cache is stale or not
  28. * @param Boolean $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(md5(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 = $cacheDir.'/'.$name.$extension.'.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 .= $token[1];
  151. } while ($token[0] !== T_END_HEREDOC);
  152. $rawChunk = '';
  153. } elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
  154. $output .= self::compressCode($rawChunk) . $token[1];
  155. $rawChunk = '';
  156. } else {
  157. $rawChunk .= $token[1];
  158. }
  159. }
  160. if ($inNamespace) {
  161. $rawChunk .= "}\n";
  162. }
  163. return $output . self::compressCode($rawChunk);
  164. }
  165. /**
  166. * This method is only useful for testing.
  167. */
  168. public static function enableTokenizer($bool)
  169. {
  170. self::$useTokenizer = (Boolean) $bool;
  171. }
  172. /**
  173. * Strips leading & trailing ws, multiple EOL, multiple ws.
  174. *
  175. * @param string $code Original PHP code
  176. *
  177. * @return string compressed code
  178. */
  179. private static function compressCode($code)
  180. {
  181. return preg_replace(
  182. array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'),
  183. array('', '', "\n", ' '),
  184. $code
  185. );
  186. }
  187. /**
  188. * Writes a cache file.
  189. *
  190. * @param string $file Filename
  191. * @param string $content Temporary file content
  192. *
  193. * @throws \RuntimeException when a cache file cannot be written
  194. */
  195. private static function writeCacheFile($file, $content)
  196. {
  197. $tmpFile = tempnam(dirname($file), basename($file));
  198. if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
  199. @chmod($file, 0666 & ~umask());
  200. return;
  201. }
  202. throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
  203. }
  204. /**
  205. * Gets an ordered array of passed classes including all their dependencies.
  206. *
  207. * @param array $classes
  208. *
  209. * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed)
  210. *
  211. * @throws \InvalidArgumentException When a class can't be loaded
  212. */
  213. private static function getOrderedClasses(array $classes)
  214. {
  215. $map = array();
  216. self::$seen = array();
  217. foreach ($classes as $class) {
  218. try {
  219. $reflectionClass = new \ReflectionClass($class);
  220. } catch (\ReflectionException $e) {
  221. throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
  222. }
  223. $map = array_merge($map, self::getClassHierarchy($reflectionClass));
  224. }
  225. return $map;
  226. }
  227. private static function getClassHierarchy(\ReflectionClass $class)
  228. {
  229. if (isset(self::$seen[$class->getName()])) {
  230. return array();
  231. }
  232. self::$seen[$class->getName()] = true;
  233. $classes = array($class);
  234. $parent = $class;
  235. while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
  236. self::$seen[$parent->getName()] = true;
  237. array_unshift($classes, $parent);
  238. }
  239. $traits = array();
  240. if (function_exists('get_declared_traits')) {
  241. foreach ($classes as $c) {
  242. foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) {
  243. if ($trait !== $c) {
  244. $traits[] = $trait;
  245. }
  246. }
  247. }
  248. }
  249. return array_merge(self::getInterfaces($class), $traits, $classes);
  250. }
  251. private static function getInterfaces(\ReflectionClass $class)
  252. {
  253. $classes = array();
  254. foreach ($class->getInterfaces() as $interface) {
  255. $classes = array_merge($classes, self::getInterfaces($interface));
  256. }
  257. if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
  258. self::$seen[$class->getName()] = true;
  259. $classes[] = $class;
  260. }
  261. return $classes;
  262. }
  263. private static function computeTraitDeps(\ReflectionClass $class)
  264. {
  265. $traits = $class->getTraits();
  266. $deps = array($class->getName() => $traits);
  267. while ($trait = array_pop($traits)) {
  268. if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
  269. self::$seen[$trait->getName()] = true;
  270. $traitDeps = $trait->getTraits();
  271. $deps[$trait->getName()] = $traitDeps;
  272. $traits = array_merge($traits, $traitDeps);
  273. }
  274. }
  275. return $deps;
  276. }
  277. /**
  278. * Dependencies resolution.
  279. *
  280. * This function does not check for circular dependencies as it should never
  281. * occur with PHP traits.
  282. *
  283. * @param array $tree The dependency tree
  284. * @param \ReflectionClass $node The node
  285. * @param \ArrayObject $resolved An array of already resolved dependencies
  286. * @param \ArrayObject $unresolved An array of dependencies to be resolved
  287. *
  288. * @return \ArrayObject The dependencies for the given node
  289. *
  290. * @throws \RuntimeException if a circular dependency is detected
  291. */
  292. private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null)
  293. {
  294. if (null === $resolved) {
  295. $resolved = new \ArrayObject();
  296. }
  297. if (null === $unresolved) {
  298. $unresolved = new \ArrayObject();
  299. }
  300. $nodeName = $node->getName();
  301. $unresolved[$nodeName] = $node;
  302. foreach ($tree[$nodeName] as $dependency) {
  303. if (!$resolved->offsetExists($dependency->getName())) {
  304. self::resolveDependencies($tree, $dependency, $resolved, $unresolved);
  305. }
  306. }
  307. $resolved[$nodeName] = $node;
  308. unset($unresolved[$nodeName]);
  309. return $resolved;
  310. }
  311. }