StaticReflectionParser::parse()   F
last analyzed

Complexity

Conditions 26
Paths 160

Size

Total Lines 90
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 60
CRAP Score 26.165

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 26
eloc 65
c 3
b 0
f 0
nc 160
nop 0
dl 0
loc 90
rs 3.6666
ccs 60
cts 64
cp 0.9375
crap 26.165

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Doctrine\Common\Reflection;
4
5
use Doctrine\Common\Annotations\TokenParser;
6
use ReflectionException;
7
use const T_CLASS;
8
use const T_DOC_COMMENT;
9
use const T_EXTENDS;
10
use const T_FUNCTION;
11
use const T_NEW;
12
use const T_PAAMAYIM_NEKUDOTAYIM;
13
use const T_PRIVATE;
14
use const T_PROTECTED;
15
use const T_PUBLIC;
16
use const T_STRING;
17
use const T_USE;
18
use const T_VAR;
19
use const T_VARIABLE;
20
use function array_merge;
21
use function file_get_contents;
22
use function ltrim;
23
use function preg_match;
24
use function sprintf;
25
use function strpos;
26
use function strrpos;
27
use function strtolower;
28
use function substr;
29
30
/**
31
 * Parses a file for namespaces/use/class declarations.
32
 */
33
class StaticReflectionParser implements ReflectionProviderInterface
34
{
35
    /**
36
     * The fully qualified class name.
37
     *
38
     * @var string
39
     */
40
    protected $className;
41
42
    /**
43
     * The short class name.
44
     *
45
     * @var string
46
     */
47
    protected $shortClassName;
48
49
    /**
50
     * Whether the caller only wants class annotations.
51
     *
52
     * @var bool
53
     */
54
    protected $classAnnotationOptimize;
55
56
    /**
57
     * A ClassFinder object which finds the class.
58
     *
59
     * @var ClassFinderInterface
60
     */
61
    protected $finder;
62
63
    /**
64
     * Whether the parser has run.
65
     *
66
     * @var bool
67
     */
68
    protected $parsed = false;
69
70
    /**
71
     * The namespace of the class.
72
     *
73
     * @var string
74
     */
75
    protected $namespace = '';
76
77
    /**
78
     * The use statements of the class.
79
     *
80
     * @var string[]
81
     */
82
    protected $useStatements = [];
83
84
    /**
85
     * The docComment of the class.
86
     *
87
     * @var mixed[]
88
     */
89
    protected $docComment = [
90
        'class' => '',
91
        'property' => [],
92
        'method' => [],
93
    ];
94
95
    /**
96
     * The name of the class this class extends, if any.
97
     *
98
     * @var string
99
     */
100
    protected $parentClassName = '';
101
102
    /**
103
     * The parent PSR-0 Parser.
104
     *
105
     * @var \Doctrine\Common\Reflection\StaticReflectionParser
106
     */
107
    protected $parentStaticReflectionParser;
108
109
    /**
110
     * Parses a class residing in a PSR-0 hierarchy.
111
     *
112
     * @param string               $className               The full, namespaced class name.
113
     * @param ClassFinderInterface $finder                  A ClassFinder object which finds the class.
114
     * @param bool                 $classAnnotationOptimize Only retrieve the class docComment.
115
     *                                                         Presumes there is only one statement per line.
116
     */
117 16
    public function __construct($className, $finder, $classAnnotationOptimize = false)
118
    {
119 16
        $this->className = ltrim($className, '\\');
120 16
        $lastNsPos       = strrpos($this->className, '\\');
121
122 16
        if ($lastNsPos !== false) {
123 16
            $this->namespace      = substr($this->className, 0, $lastNsPos);
124 16
            $this->shortClassName = substr($this->className, $lastNsPos + 1);
125
        } else {
126
            $this->shortClassName = $this->className;
127
        }
128
129 16
        $this->finder                  = $finder;
130 16
        $this->classAnnotationOptimize = $classAnnotationOptimize;
131 16
    }
132
133
    /**
134
     * @return void
135
     */
136 16
    protected function parse()
137
    {
138 16
        $fileName = $this->finder->findFile($this->className);
139
140 16
        if ($this->parsed || ! $fileName) {
141
            return;
142
        }
143 16
        $this->parsed = true;
144 16
        $contents     = file_get_contents($fileName);
145 16
        if ($this->classAnnotationOptimize) {
146 8
            $regex = sprintf('/\A.*^\s*((abstract|final)\s+)?class\s+%s\s+/sm', $this->shortClassName);
147
148 8
            if (preg_match($regex, $contents, $matches)) {
149 8
                $contents = $matches[0];
150
            }
151
        }
152 16
        $tokenParser = new TokenParser($contents);
153 16
        $docComment  = '';
154 16
        $last_token  = false;
155
156 16
        while ($token = $tokenParser->next(false)) {
157 16
            switch ($token[0]) {
158 16
                case T_USE:
159 8
                    $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement());
160 8
                    break;
161 16
                case T_DOC_COMMENT:
162 11
                    $docComment = $token[1];
163 11
                    break;
164 16
                case T_CLASS:
165 16
                    if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM && $last_token !== T_NEW) {
166 16
                        $this->docComment['class'] = $docComment;
167 16
                        $docComment                = '';
168
                    }
169 16
                    break;
170 16
                case T_VAR:
171 16
                case T_PRIVATE:
172 16
                case T_PROTECTED:
173 16
                case T_PUBLIC:
174 7
                    $token = $tokenParser->next();
175 7
                    if ($token[0] === T_VARIABLE) {
176 5
                        $propertyName                                = substr($token[1], 1);
177 5
                        $this->docComment['property'][$propertyName] = $docComment;
178 5
                        continue 2;
179
                    }
180 2
                    if ($token[0] !== T_FUNCTION) {
181
                        // For example, it can be T_FINAL.
182 1
                        continue 2;
183
                    }
184
                    // No break.
185 16
                case T_FUNCTION:
186
                    // The next string after function is the name, but
187
                    // there can be & before the function name so find the
188
                    // string.
189 1
                    while (($token = $tokenParser->next()) && $token[0] !== T_STRING) {
190
                        continue;
191
                    }
192 1
                    $methodName                              = $token[1];
193 1
                    $this->docComment['method'][$methodName] = $docComment;
194 1
                    $docComment                              = '';
195 1
                    break;
196 16
                case T_EXTENDS:
197 4
                    $this->parentClassName = $tokenParser->parseClass();
198 4
                    $nsPos                 = strpos($this->parentClassName, '\\');
199 4
                    $fullySpecified        = false;
200 4
                    if ($nsPos === 0) {
201
                        $fullySpecified = true;
202
                    } else {
203 4
                        if ($nsPos) {
204 1
                            $prefix  = strtolower(substr($this->parentClassName, 0, $nsPos));
205 1
                            $postfix = substr($this->parentClassName, $nsPos);
206
                        } else {
207 3
                            $prefix  = strtolower($this->parentClassName);
208 3
                            $postfix = '';
209
                        }
210 4
                        foreach ($this->useStatements as $alias => $use) {
211 1
                            if ($alias !== $prefix) {
212
                                continue;
213
                            }
214
215 1
                            $this->parentClassName = '\\' . $use . $postfix;
216 1
                            $fullySpecified        = true;
217
                        }
218
                    }
219 4
                    if (! $fullySpecified) {
220 3
                        $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName;
221
                    }
222 4
                    break;
223
            }
224
225 16
            $last_token = $token[0];
226
        }
227 16
    }
228
229
    /**
230
     * @return StaticReflectionParser
231
     */
232 4
    protected function getParentStaticReflectionParser()
233
    {
234 4
        if (empty($this->parentStaticReflectionParser)) {
235 4
            $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder);
236
        }
237
238 4
        return $this->parentStaticReflectionParser;
239
    }
240
241
    /**
242
     * @return string
243
     */
244 5
    public function getClassName()
245
    {
246 5
        return $this->className;
247
    }
248
249
    /**
250
     * @return string
251
     */
252
    public function getNamespaceName()
253
    {
254
        return $this->namespace;
255
    }
256
257
    /**
258
     * {@inheritDoc}
259
     */
260
    public function getReflectionClass()
261
    {
262
        return new StaticReflectionClass($this);
263
    }
264
265
    /**
266
     * {@inheritDoc}
267
     */
268
    public function getReflectionMethod($methodName)
269
    {
270
        return new StaticReflectionMethod($this, $methodName);
271
    }
272
273
    /**
274
     * {@inheritDoc}
275
     */
276
    public function getReflectionProperty($propertyName)
277
    {
278
        return new StaticReflectionProperty($this, $propertyName);
279
    }
280
281
    /**
282
     * Gets the use statements from this file.
283
     *
284
     * @return string[]
285
     */
286
    public function getUseStatements()
287
    {
288
        $this->parse();
289
290
        return $this->useStatements;
291
    }
292
293
    /**
294
     * Gets the doc comment.
295
     *
296
     * @param string $type The type: 'class', 'property' or 'method'.
297
     * @param string $name The name of the property or method, not needed for 'class'.
298
     *
299
     * @return string The doc comment, empty string if none.
300
     */
301 6
    public function getDocComment($type = 'class', $name = '')
302
    {
303 6
        $this->parse();
304
305 6
        return $name ? $this->docComment[$type][$name] : $this->docComment[$type];
306
    }
307
308
    /**
309
     * Gets the PSR-0 parser for the declaring class.
310
     *
311
     * @param string $type The type: 'property' or 'method'.
312
     * @param string $name The name of the property or method.
313
     *
314
     * @return StaticReflectionParser A static reflection parser for the declaring class.
315
     *
316
     * @throws ReflectionException
317
     */
318 10
    public function getStaticReflectionParserForDeclaringClass($type, $name)
319
    {
320 10
        $this->parse();
321 10
        if (isset($this->docComment[$type][$name])) {
322 5
            return $this;
323
        }
324 9
        if (! empty($this->parentClassName)) {
325 4
            return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name);
326
        }
327 5
        throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"');
328
    }
329
}
330