Total Complexity | 44 |
Total Lines | 423 |
Duplicated Lines | 0 % |
Changes | 16 | ||
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 |
||
37 | class AnnotationReader implements Reader, ReaderWithConstantsAnnotations |
||
|
|||
38 | { |
||
39 | /** |
||
40 | * Global map for imports. |
||
41 | * |
||
42 | * @var array |
||
43 | */ |
||
44 | private static $globalImports = [ |
||
45 | 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', |
||
46 | ]; |
||
47 | |||
48 | /** |
||
49 | * A list with annotations that are not causing exceptions when not resolved to an annotation class. |
||
50 | * |
||
51 | * The names are case sensitive. |
||
52 | * |
||
53 | * @var array |
||
54 | */ |
||
55 | private static $globalIgnoredNames = [ |
||
56 | // Annotation tags |
||
57 | 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, |
||
58 | /* Can we enable this? 'Enum' => true, */ |
||
59 | 'Required' => true, |
||
60 | 'Target' => true, |
||
61 | // Widely used tags (but not existent in phpdoc) |
||
62 | 'fix' => true , 'fixme' => true, |
||
63 | 'override' => true, |
||
64 | // PHPDocumentor 1 tags |
||
65 | 'abstract'=> true, 'access'=> true, |
||
66 | 'code' => true, |
||
67 | 'deprec'=> true, |
||
68 | 'endcode' => true, 'exception'=> true, |
||
69 | 'final'=> true, |
||
70 | 'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true, |
||
71 | 'magic' => true, |
||
72 | 'name'=> true, |
||
73 | 'toc' => true, 'tutorial'=> true, |
||
74 | 'private' => true, |
||
75 | 'static'=> true, 'staticvar'=> true, 'staticVar'=> true, |
||
76 | 'throw' => true, |
||
77 | // PHPDocumentor 2 tags. |
||
78 | 'api' => true, 'author'=> true, |
||
79 | 'category'=> true, 'copyright'=> true, |
||
80 | 'deprecated'=> true, |
||
81 | 'example'=> true, |
||
82 | 'filesource'=> true, |
||
83 | 'global'=> true, |
||
84 | 'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true, |
||
85 | 'license'=> true, 'link'=> true, |
||
86 | 'method' => true, |
||
87 | 'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true, |
||
88 | 'return'=> true, |
||
89 | 'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true, |
||
90 | 'throws'=> true, 'todo'=> true, 'TODO'=> true, |
||
91 | 'usedby'=> true, 'uses' => true, |
||
92 | 'var'=> true, 'version'=> true, |
||
93 | // PHPUnit tags |
||
94 | 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, |
||
95 | // PHPCheckStyle |
||
96 | 'SuppressWarnings' => true, |
||
97 | // PHPStorm |
||
98 | 'noinspection' => true, |
||
99 | // PEAR |
||
100 | 'package_version' => true, |
||
101 | // PlantUML |
||
102 | 'startuml' => true, 'enduml' => true, |
||
103 | // Symfony 3.3 Cache Adapter |
||
104 | 'experimental' => true, |
||
105 | // Slevomat Coding Standard |
||
106 | 'phpcsSuppress' => true, |
||
107 | // PHP CodeSniffer |
||
108 | 'codingStandardsIgnoreStart' => true, |
||
109 | 'codingStandardsIgnoreEnd' => true, |
||
110 | // PHPStan |
||
111 | 'template' => true, 'implements' => true, 'extends' => true, 'use' => true, |
||
112 | ]; |
||
113 | |||
114 | /** |
||
115 | * A list with annotations that are not causing exceptions when not resolved to an annotation class. |
||
116 | * |
||
117 | * The names are case sensitive. |
||
118 | * |
||
119 | * @var array |
||
120 | */ |
||
121 | private static $globalIgnoredNamespaces = []; |
||
122 | |||
123 | /** |
||
124 | * Add a new annotation to the globally ignored annotation names with regard to exception handling. |
||
125 | * |
||
126 | * @param string $name |
||
127 | */ |
||
128 | static public function addGlobalIgnoredName($name) |
||
129 | { |
||
130 | self::$globalIgnoredNames[$name] = true; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. |
||
135 | * |
||
136 | * @param string $namespace |
||
137 | */ |
||
138 | static public function addGlobalIgnoredNamespace($namespace) |
||
139 | { |
||
140 | self::$globalIgnoredNamespaces[$namespace] = true; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * Annotations parser. |
||
145 | * |
||
146 | * @var \Doctrine\Common\Annotations\DocParser |
||
147 | */ |
||
148 | private $parser; |
||
149 | |||
150 | /** |
||
151 | * Annotations parser used to collect parsing metadata. |
||
152 | * |
||
153 | * @var \Doctrine\Common\Annotations\DocParser |
||
154 | */ |
||
155 | private $preParser; |
||
156 | |||
157 | /** |
||
158 | * PHP parser used to collect imports. |
||
159 | * |
||
160 | * @var \Doctrine\Common\Annotations\PhpParser |
||
161 | */ |
||
162 | private $phpParser; |
||
163 | |||
164 | /** |
||
165 | * In-memory cache mechanism to store imported annotations per class. |
||
166 | * |
||
167 | * @var array |
||
168 | */ |
||
169 | private $imports = []; |
||
170 | |||
171 | /** |
||
172 | * In-memory cache mechanism to store ignored annotations per class. |
||
173 | * |
||
174 | * @var array |
||
175 | */ |
||
176 | private $ignoredAnnotationNames = []; |
||
177 | |||
178 | /** |
||
179 | * Constructor. |
||
180 | * |
||
181 | * Initializes a new AnnotationReader. |
||
182 | * |
||
183 | * @throws AnnotationException |
||
184 | */ |
||
185 | public function __construct(DocParser $parser = null) |
||
186 | { |
||
187 | if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { |
||
188 | throw AnnotationException::optimizerPlusSaveComments(); |
||
189 | } |
||
190 | |||
191 | if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) { |
||
192 | throw AnnotationException::optimizerPlusSaveComments(); |
||
193 | } |
||
194 | |||
195 | AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); |
||
196 | |||
197 | $this->parser = $parser ?: new DocParser(); |
||
198 | |||
199 | $this->preParser = new DocParser; |
||
200 | |||
201 | $this->preParser->setImports(self::$globalImports); |
||
202 | $this->preParser->setIgnoreNotImportedAnnotations(true); |
||
203 | $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames); |
||
204 | |||
205 | $this->phpParser = new PhpParser; |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * {@inheritDoc} |
||
210 | */ |
||
211 | public function getClassAnnotations(ReflectionClass $class) |
||
212 | { |
||
213 | $this->parser->setTarget(Target::TARGET_CLASS); |
||
214 | $this->parser->setImports($this->getClassImports($class)); |
||
215 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
216 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
217 | |||
218 | return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * {@inheritDoc} |
||
223 | */ |
||
224 | public function getClassAnnotation(ReflectionClass $class, $annotationName) |
||
225 | { |
||
226 | $annotations = $this->getClassAnnotations($class); |
||
227 | |||
228 | foreach ($annotations as $annotation) { |
||
229 | if ($annotation instanceof $annotationName) { |
||
230 | return $annotation; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | return null; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * {@inheritDoc} |
||
239 | */ |
||
240 | public function getPropertyAnnotations(ReflectionProperty $property) |
||
241 | { |
||
242 | $class = $property->getDeclaringClass(); |
||
243 | $context = 'property ' . $class->getName() . "::\$" . $property->getName(); |
||
244 | |||
245 | $this->parser->setTarget(Target::TARGET_PROPERTY); |
||
246 | $this->parser->setImports($this->getPropertyImports($property)); |
||
247 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
248 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
249 | |||
250 | return $this->parser->parse($property->getDocComment(), $context); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * {@inheritDoc} |
||
255 | */ |
||
256 | public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) |
||
257 | { |
||
258 | $annotations = $this->getPropertyAnnotations($property); |
||
259 | |||
260 | foreach ($annotations as $annotation) { |
||
261 | if ($annotation instanceof $annotationName) { |
||
262 | return $annotation; |
||
263 | } |
||
264 | } |
||
265 | |||
266 | return null; |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * {@inheritDoc} |
||
271 | */ |
||
272 | public function getMethodAnnotations(ReflectionMethod $method) |
||
273 | { |
||
274 | $class = $method->getDeclaringClass(); |
||
275 | $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; |
||
276 | |||
277 | $this->parser->setTarget(Target::TARGET_METHOD); |
||
278 | $this->parser->setImports($this->getMethodImports($method)); |
||
279 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
280 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
281 | |||
282 | return $this->parser->parse($method->getDocComment(), $context); |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * {@inheritDoc} |
||
287 | */ |
||
288 | public function getMethodAnnotation(ReflectionMethod $method, $annotationName) |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * {@inheritDoc} |
||
303 | */ |
||
304 | public function getConstantAnnotations(\ReflectionClassConstant $constant): array |
||
305 | { |
||
306 | $class = $constant->getDeclaringClass(); |
||
307 | $context = 'constant ' . $class->getName() . "::" . $constant->getName(); |
||
308 | |||
309 | $this->parser->setTarget(Target::TARGET_CONSTANT); |
||
310 | $this->parser->setImports($this->getConstantImports($constant)); |
||
311 | $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
||
312 | $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); |
||
313 | |||
314 | return $this->parser->parse($constant->getDocComment(), $context); |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * {@inheritDoc} |
||
319 | */ |
||
320 | public function getConstantAnnotation(\ReflectionClassConstant $constant, $annotationName) |
||
321 | { |
||
322 | $annotations = $this->getConstantAnnotations($constant); |
||
323 | |||
324 | foreach ($annotations as $annotation) { |
||
325 | if ($annotation instanceof $annotationName) { |
||
326 | return $annotation; |
||
327 | } |
||
328 | } |
||
329 | |||
330 | return null; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Returns the ignored annotations for the given class. |
||
335 | * |
||
336 | * @return array |
||
337 | */ |
||
338 | private function getIgnoredAnnotationNames(ReflectionClass $class) |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Retrieves imports. |
||
352 | * |
||
353 | * @return array |
||
354 | */ |
||
355 | private function getClassImports(ReflectionClass $class) |
||
356 | { |
||
357 | $name = $class->getName(); |
||
358 | if (isset($this->imports[$name])) { |
||
359 | return $this->imports[$name]; |
||
360 | } |
||
361 | |||
362 | $this->collectParsingMetadata($class); |
||
363 | |||
364 | return $this->imports[$name]; |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * Retrieves imports for methods. |
||
369 | * |
||
370 | * @return array |
||
371 | */ |
||
372 | private function getMethodImports(ReflectionMethod $method) |
||
373 | { |
||
374 | $class = $method->getDeclaringClass(); |
||
375 | $classImports = $this->getClassImports($class); |
||
376 | |||
377 | $traitImports = []; |
||
378 | |||
379 | foreach ($class->getTraits() as $trait) { |
||
380 | if ($trait->hasMethod($method->getName()) |
||
381 | && $trait->getFileName() === $method->getFileName() |
||
382 | ) { |
||
383 | $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); |
||
384 | } |
||
385 | } |
||
386 | |||
387 | return array_merge($classImports, $traitImports); |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Retrieves imports for properties. |
||
392 | * |
||
393 | * @return array |
||
394 | */ |
||
395 | private function getPropertyImports(ReflectionProperty $property) |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Retrieves imports for constants. |
||
413 | * |
||
414 | * @return object[] |
||
415 | */ |
||
416 | private function getConstantImports(\ReflectionClassConstant $constant): array |
||
417 | { |
||
418 | $class = $constant->getDeclaringClass(); |
||
419 | $classImports = $this->getClassImports($class); |
||
420 | if (!method_exists($class, 'getTraits')) { |
||
421 | return $classImports; |
||
422 | } |
||
423 | |||
424 | $traitImports = array(); |
||
425 | |||
426 | foreach ($class->getTraits() as $trait) { |
||
427 | if ($trait->hasConstant($constant->getName())) { |
||
428 | $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); |
||
429 | } |
||
430 | } |
||
431 | |||
432 | return array_merge($classImports, $traitImports); |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * Collects parsing metadata for a given class. |
||
437 | */ |
||
438 | private function collectParsingMetadata(ReflectionClass $class) |
||
460 | } |
||
461 | } |
||
462 |
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.