d3965d753a52ca0dc651d6be12f3eea165eff7b3
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 namespace PhpDocReader\PhpParser;
4
5 /**
6 * Parses a file for namespaces/use/class declarations.
7 *
8 * Class taken and adapted from doctrine/annotations to avoid pulling the whole package.
9 *
10 * @author Fabien Potencier <fabien@symfony.com>
11 * @author Christian Kaps <christian.kaps@mohiva.com>
12 */
13 class TokenParser
14 {
15 /**
16 * The token list.
17 *
18 * @var array
19 */
20 private $tokens;
21
22 /**
23 * The number of tokens.
24 *
25 * @var int
26 */
27 private $numTokens;
28
29 /**
30 * The current array pointer.
31 *
32 * @var int
33 */
34 private $pointer = 0;
35
36 /**
37 * @param string $contents
38 */
39 public function __construct($contents)
40 {
41 $this->tokens = token_get_all($contents);
42
43 // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it
44 // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored
45 // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a
46 // docblock. If the first thing in the file is a class without a doc block this would cause calls to
47 // getDocBlock() on said class to return our long lost doc_comment. Argh.
48 // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least
49 // it's harmless to us.
50 token_get_all("<?php\n/**\n *\n */");
51
52 $this->numTokens = count($this->tokens);
53 }
54
55 /**
56 * Gets all use statements.
57 *
58 * @param string $namespaceName The namespace name of the reflected class.
59 *
60 * @return array A list with all found use statements.
61 */
62 public function parseUseStatements($namespaceName)
63 {
64 $statements = array();
65 while (($token = $this->next())) {
66 if ($token[0] === T_USE) {
67 $statements = array_merge($statements, $this->parseUseStatement());
68 continue;
69 }
70 if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
71 continue;
72 }
73
74 // Get fresh array for new namespace. This is to prevent the parser to collect the use statements
75 // for a previous namespace with the same name. This is the case if a namespace is defined twice
76 // or if a namespace with the same name is commented out.
77 $statements = array();
78 }
79
80 return $statements;
81 }
82
83 /**
84 * Gets the next non whitespace and non comment token.
85 *
86 * @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
87 * If FALSE then only whitespace and normal comments are skipped.
88 *
89 * @return array|null The token if exists, null otherwise.
90 */
91 private function next($docCommentIsComment = true)
92 {
93 for ($i = $this->pointer; $i < $this->numTokens; $i++) {
94 $this->pointer++;
95 if ($this->tokens[$i][0] === T_WHITESPACE ||
96 $this->tokens[$i][0] === T_COMMENT ||
97 ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
98
99 continue;
100 }
101
102 return $this->tokens[$i];
103 }
104
105 return null;
106 }
107
108 /**
109 * Parses a single use statement.
110 *
111 * @return array A list with all found class names for a use statement.
112 */
113 private function parseUseStatement()
114 {
115 $class = '';
116 $alias = '';
117 $statements = array();
118 $explicitAlias = false;
119 while (($token = $this->next())) {
120 $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
121 if (!$explicitAlias && $isNameToken) {
122 $class .= $token[1];
123 $alias = $token[1];
124 } elseif ($explicitAlias && $isNameToken) {
125 $alias .= $token[1];
126 } elseif ($token[0] === T_AS) {
127 $explicitAlias = true;
128 $alias = '';
129 } elseif ($token === ',') {
130 $statements[strtolower($alias)] = $class;
131 $class = '';
132 $alias = '';
133 $explicitAlias = false;
134 } elseif ($token === ';') {
135 $statements[strtolower($alias)] = $class;
136 break;
137 } else {
138 break;
139 }
140 }
141
142 return $statements;
143 }
144
145 /**
146 * Gets the namespace.
147 *
148 * @return string The found namespace.
149 */
150 private function parseNamespace()
151 {
152 $name = '';
153 while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
154 $name .= $token[1];
155 }
156
157 return $name;
158 }
159 }