Total Complexity | 43 |
Total Lines | 415 |
Duplicated Lines | 0 % |
Changes | 14 | ||
Bugs | 1 | Features | 1 |
Complex classes like AnnotationReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AnnotationReader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class AnnotationReader implements Reader |
||
39 | { |
||
40 | /** |
||
41 | * Global map for imports. |
||
42 | * |
||
43 | * @var array |
||
44 | */ |
||
45 | private static $globalImports = [ |
||
46 | 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', |
||
47 | ]; |
||
48 | |||
49 | /** |
||
50 | * A list with annotations that are not causing exceptions when not resolved to an annotation class. |
||
51 | * |
||
52 | * The names are case sensitive. |
||
53 | * |
||
54 | * @var array |
||
55 | */ |
||
56 | private static $globalIgnoredNames = [ |
||
57 | // Annotation tags |
||
58 | 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, |
||
59 | /* Can we enable this? 'Enum' => true, */ |
||
60 | 'Required' => true, |
||
61 | 'Target' => true, |
||
62 | // Widely used tags (but not existent in phpdoc) |
||
63 | 'fix' => true , 'fixme' => true, |
||
64 | 'override' => true, |
||
65 | // PHPDocumentor 1 tags |
||
66 | 'abstract'=> true, 'access'=> true, |
||
67 | 'code' => true, |
||
68 | 'deprec'=> true, |
||
69 | 'endcode' => true, 'exception'=> true, |
||
70 | 'final'=> true, |
||
71 | 'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true, |
||
72 | 'magic' => true, |
||
73 | 'name'=> true, |
||
74 | 'toc' => true, 'tutorial'=> true, |
||
75 | 'private' => true, |
||
76 | 'static'=> true, 'staticvar'=> true, 'staticVar'=> true, |
||
77 | 'throw' => true, |
||
78 | // PHPDocumentor 2 tags. |
||
79 | 'api' => true, 'author'=> true, |
||
80 | 'category'=> true, 'copyright'=> true, |
||
81 | 'deprecated'=> true, |
||
82 | 'example'=> true, |
||
83 | 'filesource'=> true, |
||
84 | 'global'=> true, |
||
85 | 'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true, |
||
86 | 'license'=> true, 'link'=> true, |
||
87 | 'method' => true, |
||
88 | 'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true, |
||
89 | 'return'=> true, |
||
90 | 'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true, |
||
91 | 'throws'=> true, 'todo'=> true, 'TODO'=> true, |
||
92 | 'usedby'=> true, 'uses' => true, |
||
93 | 'var'=> true, 'version'=> true, |
||
94 | // PHPUnit tags |
||
95 | 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, |
||
96 | // PHPCheckStyle |
||
97 | 'SuppressWarnings' => true, |
||
98 | // PHPStorm |
||
99 | 'noinspection' => true, |
||
100 | // PEAR |
||
101 | 'package_version' => true, |
||
102 | // PlantUML |
||
103 | 'startuml' => true, 'enduml' => true, |
||
104 | // Symfony 3.3 Cache Adapter |
||
105 | 'experimental' => true, |
||
106 | // Slevomat Coding Standard |
||
107 | 'phpcsSuppress' => true, |
||
108 | // PHP CodeSniffer |
||
109 | 'codingStandardsIgnoreStart' => true, |
||
110 | 'codingStandardsIgnoreEnd' => true, |
||
111 | // PHPStan |
||
112 | 'template' => true, 'implements' => true, 'extends' => true, 'use' => true, |
||
113 | ]; |
||
114 | |||
115 | /** |
||
116 | * A list with annotations that are not causing exceptions when not resolved to an annotation class. |
||
117 | * |
||
118 | * The names are case sensitive. |
||
119 | * |
||
120 | * @var array |
||
121 | */ |
||
122 | private static $globalIgnoredNamespaces = []; |
||
123 | |||
124 | /** |
||
125 | * Add a new annotation to the globally ignored annotation names with regard to exception handling. |
||
126 | * |
||
127 | * @param string $name |
||
128 | */ |
||
129 | static public function addGlobalIgnoredName($name) |
||
130 | { |
||
131 | self::$globalIgnoredNames[$name] = true; |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. |
||
136 | * |
||
137 | * @param string $namespace |
||
138 | */ |
||
139 | static public function addGlobalIgnoredNamespace($namespace) |
||
140 | { |
||
141 | self::$globalIgnoredNamespaces[$namespace] = true; |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * Annotations parser. |
||
146 | * |
||
147 | * @var \Doctrine\Common\Annotations\DocParser |
||
148 | */ |
||
149 | private $parser; |
||
150 | |||
151 | /** |
||
152 | * Annotations parser used to collect parsing metadata. |
||
153 | * |
||
154 | * @var \Doctrine\Common\Annotations\DocParser |
||
155 | */ |
||
156 | private $preParser; |
||
157 | |||
158 | /** |
||
159 | * PHP parser used to collect imports. |
||
160 | * |
||
161 | * @var \Doctrine\Common\Annotations\PhpParser |
||
162 | */ |
||
163 | private $phpParser; |
||
164 | |||
165 | /** |
||
166 | * In-memory cache mechanism to store imported annotations per class. |
||
167 | * |
||
168 | * @var array |
||
169 | */ |
||
170 | private $imports = []; |
||
171 | |||
172 | /** |
||
173 | * In-memory cache mechanism to store ignored annotations per class. |
||
174 | * |
||
175 | * @var array |
||
176 | */ |
||
177 | private $ignoredAnnotationNames = []; |
||
178 | |||
179 | /** |
||
180 | * Constructor. |
||
181 | * |
||
182 | * Initializes a new AnnotationReader. |
||
183 | * |
||
184 | * @param DocParser $parser |
||
185 | * |
||
186 | * @throws AnnotationException |
||
187 | */ |
||
188 | public function __construct(DocParser $parser = null) |
||
189 | { |
||
190 | if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { |
||
191 | throw AnnotationException::optimizerPlusSaveComments(); |
||
192 | } |
||
193 | |||
194 | if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) { |
||
195 | throw AnnotationException::optimizerPlusSaveComments(); |
||
196 | } |
||
197 | |||
198 | AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); |
||
|
|||
199 | |||
200 | $this->parser = $parser ?: new DocParser(); |
||
201 | |||
202 | $this->preParser = new DocParser; |
||
203 | |||
204 | $this->preParser->setImports(self::$globalImports); |
||
205 | $this->preParser->setIgnoreNotImportedAnnotations(true); |
||
206 | $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames); |
||
207 | |||
208 | $this->phpParser = new PhpParser; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * {@inheritDoc} |
||
213 | */ |
||
214 | public function getClassAnnotations(ReflectionClass $class) |
||
215 | { |
||
216 | $this->parser->setTarget(Target::TARGET_CLASS); |
||
217 | $this->parser->setImports($this->getImports($class)); |
||
218 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
219 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
220 | |||
221 | return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * {@inheritDoc} |
||
226 | */ |
||
227 | public function getClassAnnotation(ReflectionClass $class, $annotationName) |
||
228 | { |
||
229 | $annotations = $this->getClassAnnotations($class); |
||
230 | |||
231 | foreach ($annotations as $annotation) { |
||
232 | if ($annotation instanceof $annotationName) { |
||
233 | return $annotation; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | return null; |
||
238 | } |
||
239 | |||
240 | /** |
||
241 | * {@inheritDoc} |
||
242 | */ |
||
243 | public function getPropertyAnnotations(ReflectionProperty $property) |
||
244 | { |
||
245 | $class = $property->getDeclaringClass(); |
||
246 | $context = 'property ' . $class->getName() . "::\$" . $property->getName(); |
||
247 | |||
248 | $this->parser->setTarget(Target::TARGET_PROPERTY); |
||
249 | $this->parser->setImports($this->getPropertyImports($property)); |
||
250 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
251 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
252 | |||
253 | return $this->parser->parse($property->getDocComment(), $context); |
||
254 | } |
||
255 | |||
256 | /** |
||
257 | * {@inheritDoc} |
||
258 | */ |
||
259 | public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) |
||
260 | { |
||
261 | $annotations = $this->getPropertyAnnotations($property); |
||
262 | |||
263 | foreach ($annotations as $annotation) { |
||
264 | if ($annotation instanceof $annotationName) { |
||
265 | return $annotation; |
||
266 | } |
||
267 | } |
||
268 | |||
269 | return null; |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * {@inheritDoc} |
||
274 | */ |
||
275 | public function getMethodAnnotations(ReflectionMethod $method) |
||
276 | { |
||
277 | $class = $method->getDeclaringClass(); |
||
278 | $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; |
||
279 | |||
280 | $this->parser->setTarget(Target::TARGET_METHOD); |
||
281 | $this->parser->setImports($this->getMethodImports($method)); |
||
282 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
283 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
284 | |||
285 | return $this->parser->parse($method->getDocComment(), $context); |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * {@inheritDoc} |
||
290 | */ |
||
291 | public function getMethodAnnotation(ReflectionMethod $method, $annotationName) |
||
292 | { |
||
293 | $annotations = $this->getMethodAnnotations($method); |
||
294 | |||
295 | foreach ($annotations as $annotation) { |
||
296 | if ($annotation instanceof $annotationName) { |
||
297 | return $annotation; |
||
298 | } |
||
299 | } |
||
300 | |||
301 | return null; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Gets the annotations applied to a function. |
||
306 | * |
||
307 | * @return array An array of Annotations. |
||
308 | */ |
||
309 | public function getFunctionAnnotations(ReflectionFunction $function) : array |
||
310 | { |
||
311 | $context = 'function ' . $function->getName(); |
||
312 | |||
313 | $this->parser->setTarget(Target::TARGET_FUNCTION); |
||
314 | $this->parser->setImports($this->getImports($function)); |
||
315 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function)); |
||
316 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
317 | |||
318 | return $this->parser->parse($function->getDocComment(), $context); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Gets a function annotation. |
||
323 | * |
||
324 | * @return object|null The Annotation or NULL, if the requested annotation does not exist. |
||
325 | */ |
||
326 | public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName) |
||
327 | { |
||
328 | $annotations = $this->getFunctionAnnotations($function); |
||
329 | |||
330 | foreach ($annotations as $annotation) { |
||
331 | if ($annotation instanceof $annotationName) { |
||
332 | return $annotation; |
||
333 | } |
||
334 | } |
||
335 | |||
336 | return null; |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Returns the ignored annotations for the given class or function. |
||
341 | * |
||
342 | * @param ReflectionClass|ReflectionFunction $reflection |
||
343 | */ |
||
344 | private function getIgnoredAnnotationNames($reflection) : array |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * Retrieves imports for a class or a function. |
||
360 | * |
||
361 | * @param ReflectionClass|ReflectionFunction $class |
||
362 | */ |
||
363 | private function getImports($reflection) : array |
||
364 | { |
||
365 | $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; |
||
366 | $name = $reflection->getName(); |
||
367 | |||
368 | if (isset($this->imports[$type][$name])) { |
||
369 | return $this->imports[$type][$name]; |
||
370 | } |
||
371 | |||
372 | $this->collectParsingMetadata($reflection); |
||
373 | |||
374 | return $this->imports[$type][$name]; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Retrieves imports for methods. |
||
379 | * |
||
380 | * @param \ReflectionMethod $method |
||
381 | * |
||
382 | * @return array |
||
383 | */ |
||
384 | private function getMethodImports(ReflectionMethod $method) |
||
385 | { |
||
386 | $class = $method->getDeclaringClass(); |
||
387 | $classImports = $this->getImports($class); |
||
388 | |||
389 | $traitImports = []; |
||
390 | |||
391 | foreach ($class->getTraits() as $trait) { |
||
392 | if ($trait->hasMethod($method->getName()) |
||
393 | && $trait->getFileName() === $method->getFileName() |
||
394 | ) { |
||
395 | $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); |
||
396 | } |
||
397 | } |
||
398 | |||
399 | return array_merge($classImports, $traitImports); |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * Retrieves imports for properties. |
||
404 | * |
||
405 | * @param \ReflectionProperty $property |
||
406 | * |
||
407 | * @return array |
||
408 | */ |
||
409 | private function getPropertyImports(ReflectionProperty $property) |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Collects parsing metadata for a given class or function. |
||
427 | * |
||
428 | * @param ReflectionClass|ReflectionFunction $class |
||
429 | */ |
||
430 | private function collectParsingMetadata($reflection) : void |
||
453 | } |
||
454 | } |
||
455 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.