TokenParser   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Test Coverage

Coverage 96.77%

Importance

Changes 0
Metric Value
wmc 29
eloc 60
dl 0
loc 165
ccs 60
cts 62
cp 0.9677
rs 10
c 0
b 0
f 0

6 Methods

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