ClassMapGenerator.php 4.5 KB

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