ClassMapGenerator.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. * ClassMapGenerator.
  13. *
  14. * @author Gyula Sallai <salla016@gmail.com>
  15. */
  16. class ClassMapGenerator
  17. {
  18. /**
  19. * Generate a class map file.
  20. *
  21. * @param array|string $dirs Directories or a single path to search in
  22. * @param string $file The name of the class map file
  23. */
  24. public static function dump($dirs, $file)
  25. {
  26. $dirs = (array) $dirs;
  27. $maps = array();
  28. foreach ($dirs as $dir) {
  29. $maps = array_merge($maps, static::createMap($dir));
  30. }
  31. file_put_contents($file, sprintf('<?php return %s;', var_export($maps, true)));
  32. }
  33. /**
  34. * Iterate over all files in the given directory searching for classes.
  35. *
  36. * @param \Iterator|string $dir The directory to search in or an iterator
  37. *
  38. * @return array A class map array
  39. */
  40. public static function createMap($dir)
  41. {
  42. if (is_string($dir)) {
  43. $dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
  44. }
  45. $map = array();
  46. foreach ($dir as $file) {
  47. if (!$file->isFile()) {
  48. continue;
  49. }
  50. $path = $file->getRealPath() ?: $file->getPathname();
  51. if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
  52. continue;
  53. }
  54. $classes = self::findClasses($path);
  55. if (\PHP_VERSION_ID >= 70000) {
  56. // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
  57. gc_mem_caches();
  58. }
  59. foreach ($classes as $class) {
  60. $map[$class] = $path;
  61. }
  62. }
  63. return $map;
  64. }
  65. /**
  66. * Extract the classes in the given file.
  67. *
  68. * @param string $path The file to check
  69. *
  70. * @return array The found classes
  71. */
  72. private static function findClasses($path)
  73. {
  74. $contents = file_get_contents($path);
  75. $tokens = token_get_all($contents);
  76. $classes = array();
  77. $namespace = '';
  78. for ($i = 0; isset($tokens[$i]); ++$i) {
  79. $token = $tokens[$i];
  80. if (!isset($token[1])) {
  81. continue;
  82. }
  83. $class = '';
  84. switch ($token[0]) {
  85. case T_NAMESPACE:
  86. $namespace = '';
  87. // If there is a namespace, extract it
  88. while (isset($tokens[++$i][1])) {
  89. if (in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) {
  90. $namespace .= $tokens[$i][1];
  91. }
  92. }
  93. $namespace .= '\\';
  94. break;
  95. case T_CLASS:
  96. case T_INTERFACE:
  97. case T_TRAIT:
  98. // Skip usage of ::class constant
  99. $isClassConstant = false;
  100. for ($j = $i - 1; $j > 0; --$j) {
  101. if (!isset($tokens[$j][1])) {
  102. break;
  103. }
  104. if (T_DOUBLE_COLON === $tokens[$j][0]) {
  105. $isClassConstant = true;
  106. break;
  107. } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) {
  108. break;
  109. }
  110. }
  111. if ($isClassConstant) {
  112. break;
  113. }
  114. // Find the classname
  115. while (isset($tokens[++$i][1])) {
  116. $t = $tokens[$i];
  117. if (T_STRING === $t[0]) {
  118. $class .= $t[1];
  119. } elseif ('' !== $class && T_WHITESPACE === $t[0]) {
  120. break;
  121. }
  122. }
  123. $classes[] = ltrim($namespace.$class, '\\');
  124. break;
  125. default:
  126. break;
  127. }
  128. }
  129. return $classes;
  130. }
  131. }