Complex classes like ReflectionClassLikeTrait 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 ReflectionClassLikeTrait, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | trait ReflectionClassLikeTrait |
||
31 | { |
||
32 | use InitializationTrait; |
||
33 | |||
34 | /** |
||
35 | * @var ClassLike |
||
36 | */ |
||
37 | protected $classLikeNode; |
||
38 | |||
39 | /** |
||
40 | * Short name of the class, without namespace |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $className; |
||
45 | |||
46 | /** |
||
47 | * List of all constants from the class |
||
48 | * |
||
49 | * @var array |
||
50 | */ |
||
51 | protected $constants; |
||
52 | |||
53 | /** |
||
54 | * Interfaces, empty array or null if not initialized yet |
||
55 | * |
||
56 | * @var \ReflectionClass[]|array|null |
||
57 | */ |
||
58 | protected $interfaceClasses; |
||
59 | |||
60 | /** |
||
61 | * List of traits, empty array or null if not initialized yet |
||
62 | * |
||
63 | * @var \ReflectionClass[]|array|null |
||
64 | */ |
||
65 | protected $traits; |
||
66 | |||
67 | /** |
||
68 | * Additional list of trait adaptations |
||
69 | * |
||
70 | * @var TraitUseAdaptation[]|array |
||
71 | */ |
||
72 | protected $traitAdaptations; |
||
73 | |||
74 | /** |
||
75 | * @var array|ReflectionMethod[] |
||
76 | */ |
||
77 | protected $methods; |
||
78 | |||
79 | /** |
||
80 | * Namespace name |
||
81 | * |
||
82 | * @var string |
||
83 | */ |
||
84 | protected $namespaceName = ''; |
||
85 | |||
86 | /** |
||
87 | * Parent class, or false if not present, null if uninitialized yet |
||
88 | * |
||
89 | * @var \ReflectionClass|false|null |
||
90 | */ |
||
91 | protected $parentClass; |
||
92 | |||
93 | /** |
||
94 | * @var array|ReflectionProperty[] |
||
95 | */ |
||
96 | protected $properties; |
||
97 | |||
98 | /** |
||
99 | * @var array|ReflectionClassConstant[] |
||
100 | */ |
||
101 | protected $classConstants; |
||
102 | |||
103 | /** |
||
104 | * Returns the string representation of the ReflectionClass object. |
||
105 | * |
||
106 | * @return string |
||
107 | */ |
||
108 | public function __toString() |
||
197 | |||
198 | |||
199 | /** |
||
200 | * {@inheritDoc} |
||
201 | */ |
||
202 | 10 | public function getConstant($name) |
|
210 | |||
211 | /** |
||
212 | * {@inheritDoc} |
||
213 | */ |
||
214 | 38 | public function getConstants() |
|
225 | |||
226 | /** |
||
227 | * {@inheritDoc} |
||
228 | */ |
||
229 | 19 | public function getConstructor() |
|
238 | |||
239 | /** |
||
240 | * Gets default properties from a class (including inherited properties). |
||
241 | * |
||
242 | * @link http://php.net/manual/en/reflectionclass.getdefaultproperties.php |
||
243 | * |
||
244 | * @return array An array of default properties, with the key being the name of the property and the value being |
||
245 | * the default value of the property or NULL if the property doesn't have a default value |
||
246 | */ |
||
247 | 29 | public function getDefaultProperties() |
|
273 | |||
274 | /** |
||
275 | * {@inheritDoc} |
||
276 | */ |
||
277 | 29 | public function getDocComment() |
|
283 | |||
284 | 29 | public function getEndLine() |
|
288 | |||
289 | 29 | public function getExtension() |
|
293 | |||
294 | 29 | public function getExtensionName() |
|
298 | |||
299 | 21 | public function getFileName() |
|
303 | |||
304 | /** |
||
305 | * {@inheritDoc} |
||
306 | */ |
||
307 | 30 | public function getInterfaceNames() |
|
311 | |||
312 | /** |
||
313 | * {@inheritDoc} |
||
314 | */ |
||
315 | 59 | public function getInterfaces() |
|
328 | |||
329 | /** |
||
330 | * {@inheritdoc} |
||
331 | * @param string $name |
||
332 | */ |
||
333 | 2047 | public function getMethod($name) |
|
344 | |||
345 | /** |
||
346 | * Returns list of reflection methods |
||
347 | * |
||
348 | * @param null|integer $filter Optional filter |
||
349 | * |
||
350 | * @return array|\ReflectionMethod[] |
||
351 | */ |
||
352 | 2077 | public function getMethods($filter = null) |
|
383 | |||
384 | /** |
||
385 | * Returns a bitfield of the access modifiers for this class. |
||
386 | * |
||
387 | * @link http://php.net/manual/en/reflectionclass.getmodifiers.php |
||
388 | * |
||
389 | * NB: this method is not fully compatible with original value because of hidden internal constants |
||
390 | * |
||
391 | * @return int |
||
392 | */ |
||
393 | 4 | public function getModifiers() |
|
420 | |||
421 | /** |
||
422 | * {@inheritDoc} |
||
423 | */ |
||
424 | 3004 | public function getName() |
|
430 | |||
431 | /** |
||
432 | * {@inheritDoc} |
||
433 | */ |
||
434 | 50 | public function getNamespaceName() |
|
438 | |||
439 | /** |
||
440 | * {@inheritDoc} |
||
441 | */ |
||
442 | 238 | public function getParentClass() |
|
459 | |||
460 | /** |
||
461 | * Retrieves reflected properties. |
||
462 | * |
||
463 | * @param int $filter The optional filter, for filtering desired property types. |
||
464 | * It's configured using the ReflectionProperty constants, and defaults to all property types. |
||
465 | * |
||
466 | * @return array|\Go\ParserReflection\ReflectionProperty[] |
||
467 | */ |
||
468 | 318 | public function getProperties($filter = null) |
|
501 | |||
502 | /** |
||
503 | * {@inheritdoc} |
||
504 | */ |
||
505 | 255 | public function getProperty($name) |
|
516 | |||
517 | /** |
||
518 | * @inheritDoc |
||
519 | */ |
||
520 | 2 | public function getReflectionConstant($name) |
|
521 | { |
||
522 | 2 | $classConstants = $this->getReflectionConstants(); |
|
523 | 2 | foreach ($classConstants as $classConstant) { |
|
524 | 2 | if ($classConstant->getName() == $name) { |
|
525 | 2 | return $classConstant; |
|
526 | } |
||
527 | } |
||
528 | |||
529 | 1 | return false; |
|
530 | } |
||
531 | |||
532 | /** |
||
533 | * @inheritDoc |
||
534 | */ |
||
535 | 7 | public function getReflectionConstants() |
|
536 | { |
||
537 | 7 | if (!isset($this->classConstants)) { |
|
538 | 7 | $directClassConstants = ReflectionClassConstant::collectFromClassNode($this->classLikeNode, $this->getName()); |
|
539 | 7 | $parentClassConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) { |
|
540 | 2 | $reflectionClassConstants = []; |
|
541 | 2 | foreach ($instance->getReflectionConstants() as $reflectionClassConstant) { |
|
|
|||
542 | 2 | if (!$isParent || !$reflectionClassConstant->isPrivate()) { |
|
543 | 2 | $reflectionClassConstants[$reflectionClassConstant->name] = $reflectionClassConstant; |
|
544 | } |
||
545 | } |
||
546 | 2 | $result += $reflectionClassConstants; |
|
547 | 7 | }); |
|
548 | 7 | $classConstants = $directClassConstants + $parentClassConstants; |
|
549 | |||
550 | 7 | $this->classConstants = $classConstants; |
|
551 | } |
||
552 | |||
553 | 7 | return array_values($this->classConstants); |
|
554 | } |
||
555 | |||
556 | /** |
||
557 | * {@inheritDoc} |
||
558 | */ |
||
559 | 3004 | public function getShortName() |
|
563 | |||
564 | 29 | public function getStartLine() |
|
568 | |||
569 | /** |
||
570 | * Returns an array of trait aliases |
||
571 | * |
||
572 | * @link http://php.net/manual/en/reflectionclass.gettraitaliases.php |
||
573 | * |
||
574 | * @return array|null an array with new method names in keys and original names (in the format "TraitName::original") in |
||
575 | * values. |
||
576 | */ |
||
577 | 33 | public function getTraitAliases() |
|
597 | |||
598 | /** |
||
599 | * Returns an array of names of traits used by this class |
||
600 | * |
||
601 | * @link http://php.net/manual/en/reflectionclass.gettraitnames.php |
||
602 | * |
||
603 | * @return array |
||
604 | */ |
||
605 | 29 | public function getTraitNames() |
|
609 | |||
610 | /** |
||
611 | * Returns an array of traits used by this class |
||
612 | * |
||
613 | * @link http://php.net/manual/en/reflectionclass.gettraits.php |
||
614 | * |
||
615 | * @return array|\ReflectionClass[] |
||
616 | */ |
||
617 | 233 | public function getTraits() |
|
627 | |||
628 | /** |
||
629 | * {@inheritDoc} |
||
630 | */ |
||
631 | 11 | public function hasConstant($name) |
|
638 | |||
639 | /** |
||
640 | * {@inheritdoc} |
||
641 | * @param string $name |
||
642 | */ |
||
643 | 20 | public function hasMethod($name) |
|
654 | |||
655 | /** |
||
656 | * {@inheritdoc} |
||
657 | */ |
||
658 | public function hasProperty($name) |
||
669 | |||
670 | /** |
||
671 | * {@inheritDoc} |
||
672 | * @param string $interfaceName |
||
673 | */ |
||
674 | 29 | public function implementsInterface($interfaceName) |
|
680 | |||
681 | /** |
||
682 | * {@inheritDoc} |
||
683 | */ |
||
684 | 29 | public function inNamespace() |
|
688 | |||
689 | /** |
||
690 | * {@inheritDoc} |
||
691 | */ |
||
692 | 75 | public function isAbstract() |
|
704 | |||
705 | /** |
||
706 | * Currently, anonymous classes aren't supported for parsed reflection |
||
707 | */ |
||
708 | public function isAnonymous() |
||
712 | |||
713 | /** |
||
714 | * {@inheritDoc} |
||
715 | */ |
||
716 | 29 | public function isCloneable() |
|
728 | |||
729 | /** |
||
730 | * {@inheritDoc} |
||
731 | */ |
||
732 | 33 | public function isFinal() |
|
738 | |||
739 | /** |
||
740 | * {@inheritDoc} |
||
741 | */ |
||
742 | public function isInstance($object) |
||
751 | |||
752 | /** |
||
753 | * {@inheritDoc} |
||
754 | */ |
||
755 | 29 | public function isInstantiable() |
|
767 | |||
768 | /** |
||
769 | * {@inheritDoc} |
||
770 | */ |
||
771 | 246 | public function isInterface() |
|
775 | |||
776 | /** |
||
777 | * {@inheritDoc} |
||
778 | */ |
||
779 | 29 | public function isInternal() |
|
784 | |||
785 | /** |
||
786 | * {@inheritDoc} |
||
787 | */ |
||
788 | 29 | public function isIterateable() |
|
792 | |||
793 | /** |
||
794 | * {@inheritDoc} |
||
795 | */ |
||
796 | public function isSubclassOf($class) |
||
819 | |||
820 | /** |
||
821 | * {@inheritDoc} |
||
822 | */ |
||
823 | 106 | public function isTrait() |
|
827 | |||
828 | /** |
||
829 | * {@inheritDoc} |
||
830 | */ |
||
831 | 29 | public function isUserDefined() |
|
836 | |||
837 | /** |
||
838 | * Gets static properties |
||
839 | * |
||
840 | * @link http://php.net/manual/en/reflectionclass.getstaticproperties.php |
||
841 | * |
||
842 | * @return array |
||
843 | */ |
||
844 | 30 | public function getStaticProperties() |
|
865 | |||
866 | /** |
||
867 | * Gets static property value |
||
868 | * |
||
869 | * @param string $name The name of the static property for which to return a value. |
||
870 | * @param mixed $default A default value to return in case the class does not declare |
||
871 | * a static property with the given name |
||
872 | * |
||
873 | * @return mixed |
||
874 | * @throws ReflectionException If there is no such property and no default value was given |
||
875 | */ |
||
876 | 1 | public function getStaticPropertyValue($name, $default = null) |
|
887 | |||
888 | |||
889 | /** |
||
890 | * Creates a new class instance from given arguments. |
||
891 | * |
||
892 | * @link http://php.net/manual/en/reflectionclass.newinstance.php |
||
893 | * |
||
894 | * Signature was hacked to support both 5.6, 7.1.x and 7.2.0 versions |
||
895 | * @see https://3v4l.org/hW9O9 |
||
896 | * @see https://3v4l.org/sWT3j |
||
897 | * @see https://3v4l.org/eeVf8 |
||
898 | * |
||
899 | * @param mixed $arg First argument |
||
900 | * @param mixed $args Accepts a variable number of arguments which are passed to the class constructor |
||
901 | * |
||
902 | * @return object |
||
903 | */ |
||
904 | 1 | public function newInstance($arg = null, ...$args) |
|
911 | |||
912 | /** |
||
913 | * Creates a new class instance from given arguments. |
||
914 | * |
||
915 | * @link http://php.net/manual/en/reflectionclass.newinstanceargs.php |
||
916 | * |
||
917 | * @param array $args The parameters to be passed to the class constructor as an array. |
||
918 | * |
||
919 | * @return object |
||
920 | */ |
||
921 | 1 | public function newInstanceArgs(array $args = []) |
|
928 | |||
929 | /** |
||
930 | * Creates a new class instance without invoking the constructor. |
||
931 | * |
||
932 | * @link http://php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php |
||
933 | * |
||
934 | * @return object |
||
935 | */ |
||
936 | 1 | public function newInstanceWithoutConstructor($args = null) |
|
943 | |||
944 | /** |
||
945 | * Sets static property value |
||
946 | * |
||
947 | * @link http://php.net/manual/en/reflectionclass.setstaticpropertyvalue.php |
||
948 | * |
||
949 | * @param string $name Property name |
||
950 | * @param mixed $value New property value |
||
951 | */ |
||
952 | 1 | public function setStaticPropertyValue($name, $value) |
|
958 | |||
959 | 175 | private function recursiveCollect(\Closure $collector) |
|
981 | |||
982 | /** |
||
983 | * Collects list of constants from the class itself |
||
984 | */ |
||
985 | 38 | private function collectSelfConstants() |
|
1004 | |||
1005 | /** |
||
1006 | * Create a ReflectionClass for a given class name. |
||
1007 | * |
||
1008 | * @param string $className |
||
1009 | * The name of the class to create a reflection for. |
||
1010 | * |
||
1011 | * @return ReflectionClass |
||
1012 | * The apropriate reflection object. |
||
1013 | */ |
||
1014 | abstract protected function createReflectionForClass($className); |
||
1015 | } |
||
1016 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: