PhpParser.php 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\Common\Annotations;
  20. /**
  21. * Parses a file for namespaces/use/class declarations.
  22. *
  23. * @author Fabien Potencier <fabien@symfony.com>
  24. */
  25. final class PhpParser
  26. {
  27. private $tokens;
  28. /**
  29. * Parses a class.
  30. *
  31. * @param \ReflectionClass $class
  32. */
  33. public function parseClass(\ReflectionClass $class)
  34. {
  35. if (false === $filename = $class->getFilename()) {
  36. return array();
  37. }
  38. $src = file_get_contents($filename);
  39. $name = $class->getName();
  40. // This is a short-cut for code that follows some conventions:
  41. // - namespaced
  42. // - one class per file
  43. if (preg_match_all('#\bnamespace\s+'.str_replace('\\', '\\\\', $class->getNamespaceName()).'\s*;.*?\b(?:class|interface)\s+'.$class->getShortName().'\b#s', $src, $matches)) {
  44. foreach ($matches[0] as $match) {
  45. $classes = $this->parse('<?php '.$match, $name);
  46. if (isset($classes[$name])) {
  47. return $classes[$name];
  48. }
  49. }
  50. }
  51. $classes = $this->parse($src, $name);
  52. return $classes[$name];
  53. }
  54. private function parse($src, $interestedClass = null)
  55. {
  56. $this->tokens = token_get_all($src);
  57. $classes = $uses = array();
  58. $namespace = '';
  59. while ($token = $this->next()) {
  60. if (T_NAMESPACE === $token[0]) {
  61. $namespace = $this->parseNamespace();
  62. $uses = array();
  63. } elseif (T_CLASS === $token[0] || T_INTERFACE === $token[0]) {
  64. if ('' !== $namespace) {
  65. $class = $namespace.'\\'.$this->nextValue();
  66. } else {
  67. $class = $this->nextValue();
  68. }
  69. $classes[$class] = $uses;
  70. if (null !== $interestedClass && $interestedClass === $class) {
  71. return $classes;
  72. }
  73. } elseif (T_USE === $token[0]) {
  74. foreach ($this->parseUseStatement() as $useStatement) {
  75. list($alias, $class) = $useStatement;
  76. $uses[strtolower($alias)] = $class;
  77. }
  78. }
  79. }
  80. return $classes;
  81. }
  82. private function parseNamespace()
  83. {
  84. $namespace = '';
  85. while ($token = $this->next()) {
  86. if (T_NS_SEPARATOR === $token[0] || T_STRING === $token[0]) {
  87. $namespace .= $token[1];
  88. } elseif (is_string($token) && in_array($token, array(';', '{'))) {
  89. return $namespace;
  90. }
  91. }
  92. }
  93. private function parseUseStatement()
  94. {
  95. $statements = $class = array();
  96. $alias = '';
  97. while ($token = $this->next()) {
  98. if (T_NS_SEPARATOR === $token[0] || T_STRING === $token[0]) {
  99. $class[] = $token[1];
  100. } else if (T_AS === $token[0]) {
  101. $alias = $this->nextValue();
  102. } else if (is_string($token)) {
  103. if (',' === $token || ';' === $token) {
  104. $statements[] = array(
  105. $alias ? $alias : $class[count($class) - 1],
  106. implode('', $class)
  107. );
  108. }
  109. if (';' === $token) {
  110. return $statements;
  111. }
  112. if (',' === $token) {
  113. $class = array();
  114. $alias = '';
  115. continue;
  116. }
  117. }
  118. }
  119. }
  120. private function next()
  121. {
  122. while ($token = array_shift($this->tokens)) {
  123. if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
  124. continue;
  125. }
  126. return $token;
  127. }
  128. }
  129. private function nextValue()
  130. {
  131. $token = $this->next();
  132. return is_array($token) ? $token[1] : $token;
  133. }
  134. }