Complex classes like ClassProxy 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 ClassProxy, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
35 | class ClassProxy extends AbstractProxy |
||
36 | { |
||
37 | /** |
||
38 | * Parent class reflection |
||
39 | * |
||
40 | * @var null|ParsedClass |
||
41 | */ |
||
42 | protected $class = null; |
||
43 | |||
44 | /** |
||
45 | * Parent class name, can be changed manually |
||
46 | * |
||
47 | * @var string |
||
48 | */ |
||
49 | protected $parentClassName = null; |
||
50 | |||
51 | /** |
||
52 | * Source code for methods |
||
53 | * |
||
54 | * @var array Name of method => source code for it |
||
55 | */ |
||
56 | protected $methodsCode = []; |
||
57 | |||
58 | /** |
||
59 | * Static mappings for class name for excluding if..else check |
||
60 | * |
||
61 | * @var null|array |
||
62 | */ |
||
63 | protected static $invocationClassMap = null; |
||
64 | |||
65 | /** |
||
66 | * List of additional interfaces to implement |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | protected $interfaces = []; |
||
71 | |||
72 | /** |
||
73 | * List of additional traits for using |
||
74 | * |
||
75 | * @var array |
||
76 | */ |
||
77 | protected $traits = []; |
||
78 | |||
79 | /** |
||
80 | * Source code for properties |
||
81 | * |
||
82 | * @var array Name of property => source code for it |
||
83 | */ |
||
84 | protected $propertiesCode = []; |
||
85 | |||
86 | /** |
||
87 | * Name for the current class |
||
88 | * |
||
89 | * @var string |
||
90 | */ |
||
91 | protected $name = ''; |
||
92 | |||
93 | /** |
||
94 | * Flag to determine if we need to add a code for property interceptors |
||
95 | * |
||
96 | * @var bool |
||
97 | */ |
||
98 | private $isFieldsIntercepted = false; |
||
99 | |||
100 | /** |
||
101 | * List of intercepted properties names |
||
102 | * |
||
103 | * @var array |
||
104 | */ |
||
105 | private $interceptedProperties = []; |
||
106 | |||
107 | /** |
||
108 | * Generates an child code by parent class reflection and joinpoints for it |
||
109 | * |
||
110 | * @param ParsedClass $parent Parent class reflection |
||
111 | * @param array|Advice[] $classAdvices List of advices for class |
||
112 | * |
||
113 | * @throws \InvalidArgumentException if there are unknown type of advices |
||
114 | */ |
||
115 | 5 | public function __construct(ParsedClass $parent, array $classAdvices) |
|
171 | |||
172 | |||
173 | /** |
||
174 | * Updates parent name for child |
||
175 | * |
||
176 | * @param string $newParentName New class name |
||
177 | * |
||
178 | * @return static |
||
179 | */ |
||
180 | 5 | public function setParentName($newParentName) |
|
186 | |||
187 | /** |
||
188 | * Override parent method with new body |
||
189 | * |
||
190 | * @param string $methodName Method name to override |
||
191 | * @param string $body New body for method |
||
192 | * |
||
193 | * @return static |
||
194 | */ |
||
195 | 5 | public function override($methodName, $body) |
|
201 | |||
202 | /** |
||
203 | * Creates a method |
||
204 | * |
||
205 | * @param int $methodFlags See ReflectionMethod modifiers |
||
206 | * @param string $methodName Name of the method |
||
207 | * @param bool $byReference Is method should return value by reference |
||
208 | * @param string $body Body of method |
||
209 | * @param string $parameters Definition of parameters |
||
210 | * |
||
211 | * @return static |
||
212 | */ |
||
213 | public function setMethod($methodFlags, $methodName, $byReference, $body, $parameters) |
||
214 | { |
||
215 | $this->methodsCode[$methodName] = ( |
||
216 | "/**\n * Method was created automatically, do not change it manually\n */\n" . |
||
217 | join(' ', Reflection::getModifierNames($methodFlags)) . // List of method modifiers |
||
218 | ' function ' . // 'function' keyword |
||
219 | ($byReference ? '&' : '') . // Return value by reference |
||
220 | $methodName . // Method name |
||
221 | '(' . // Start of parameter list |
||
222 | $parameters . // List of parameters |
||
223 | ")\n" . // End of parameter list |
||
224 | "{\n" . // Start of method body |
||
225 | $this->indent($body) . "\n" . // Method body |
||
226 | "}\n" // End of method body |
||
227 | ); |
||
228 | |||
229 | return $this; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Inject advices into given class |
||
234 | * |
||
235 | * NB This method will be used as a callback during source code evaluation to inject joinpoints |
||
236 | * |
||
237 | * @param string $className Aop child proxy class |
||
238 | * @param array|Advice[] $advices List of advices to inject into class |
||
239 | * |
||
240 | * @return void |
||
241 | */ |
||
242 | public static function injectJoinPoints($className, array $advices = []) |
||
256 | |||
257 | /** |
||
258 | * Initialize static mappings to reduce the time for checking features |
||
259 | * |
||
260 | * @param bool $useSplatOperator Enables usage of optimized invocation with splat operator |
||
261 | */ |
||
262 | protected static function setMappings($useSplatOperator) |
||
263 | { |
||
264 | $dynamicMethodClass = MethodInvocationComposer::compose(false, $useSplatOperator, false); |
||
265 | $staticMethodClass = MethodInvocationComposer::compose(true, $useSplatOperator, false); |
||
266 | |||
267 | // We are using LSB here and overridden static property |
||
268 | static::$invocationClassMap = array( |
||
269 | AspectContainer::METHOD_PREFIX => $dynamicMethodClass, |
||
270 | AspectContainer::STATIC_METHOD_PREFIX => $staticMethodClass, |
||
271 | AspectContainer::PROPERTY_PREFIX => ClassFieldAccess::class, |
||
272 | AspectContainer::STATIC_INIT_PREFIX => StaticInitializationJoinpoint::class, |
||
273 | AspectContainer::INIT_PREFIX => ReflectionConstructorInvocation::class |
||
274 | ); |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Wrap advices with joinpoint object |
||
279 | * |
||
280 | * @param array|Advice[] $classAdvices Advices for specific class |
||
281 | * @param string $className Name of the original class to use |
||
282 | * |
||
283 | * @throws \UnexpectedValueException If joinPoint type is unknown |
||
284 | * |
||
285 | * NB: Extension should be responsible for wrapping advice with join point. |
||
286 | * |
||
287 | * @return array|Joinpoint[] returns list of joinpoint ready to use |
||
288 | */ |
||
289 | protected static function wrapWithJoinPoints($classAdvices, $className) |
||
322 | |||
323 | /** |
||
324 | * Add an interface for child |
||
325 | * |
||
326 | * @param string|ReflectionClass|ParsedClass $interface |
||
327 | * |
||
328 | * @throws \InvalidArgumentException If object is not an interface |
||
329 | */ |
||
330 | 5 | public function addInterface($interface) |
|
342 | |||
343 | /** |
||
344 | * Add a trait for child |
||
345 | * |
||
346 | * @param string|ReflectionClass|ParsedClass $trait |
||
347 | * |
||
348 | * @throws \InvalidArgumentException If object is not a trait |
||
349 | */ |
||
350 | public function addTrait($trait) |
||
362 | |||
363 | /** |
||
364 | * Creates a property |
||
365 | * |
||
366 | * @param int $propFlags See ReflectionProperty modifiers |
||
367 | * @param string $propName Name of the property |
||
368 | * @param null|string $defaultText Default value, should be string text! |
||
369 | * |
||
370 | * @return static |
||
371 | */ |
||
372 | 5 | public function setProperty($propFlags, $propName, $defaultText = null) |
|
373 | { |
||
374 | 5 | $this->propertiesCode[$propName] = ( |
|
375 | "/**\n * Property was created automatically, do not change it manually\n */\n" . // Doc-block |
||
376 | 5 | join(' ', Reflection::getModifierNames($propFlags)) . // List of modifiers for property |
|
377 | 5 | ' $' . // Space and vaiable symbol |
|
378 | 5 | $propName . // Name of the property |
|
379 | 5 | (is_string($defaultText) ? " = $defaultText" : '') . // Default value if present |
|
380 | 5 | ";\n" // End of line with property definition |
|
381 | ); |
||
382 | |||
383 | 5 | return $this; |
|
384 | } |
||
385 | |||
386 | /** |
||
387 | * Adds a definition for joinpoints private property in the class |
||
388 | * |
||
389 | * @return void |
||
390 | */ |
||
391 | 5 | protected function addJoinpointsProperty() |
|
399 | |||
400 | /** |
||
401 | * Override parent method with joinpoint invocation |
||
402 | * |
||
403 | * @param ParsedMethod $method Method reflection |
||
404 | */ |
||
405 | 5 | protected function overrideMethod(ParsedMethod $method) |
|
412 | |||
413 | /** |
||
414 | * Creates definition for method body |
||
415 | * |
||
416 | * @param ParsedMethod $method Method reflection |
||
417 | * |
||
418 | * @return string new method body |
||
419 | */ |
||
420 | 5 | protected function getJoinpointInvocationBody(ParsedMethod $method) |
|
437 | |||
438 | /** |
||
439 | * Makes property intercepted |
||
440 | * |
||
441 | * @param ParsedProperty $property Reflection of property to intercept |
||
442 | */ |
||
443 | protected function interceptProperty(ParsedProperty $property) |
||
448 | |||
449 | /** |
||
450 | * {@inheritDoc} |
||
451 | */ |
||
452 | 5 | public function __toString() |
|
483 | |||
484 | /** |
||
485 | * Add code for intercepting properties |
||
486 | * |
||
487 | * @param null|ParsedMethod $constructor Constructor reflection or null |
||
488 | */ |
||
489 | protected function addFieldInterceptorsCode(ParsedMethod $constructor = null) |
||
502 | |||
503 | /** |
||
504 | * Creates a method code from Reflection |
||
505 | * |
||
506 | * @param ParsedMethod $method Reflection for method |
||
507 | * @param string $body Body of method |
||
508 | * |
||
509 | * @return string |
||
510 | */ |
||
511 | 5 | protected function getOverriddenMethod(ParsedMethod $method, $body) |
|
529 | |||
530 | /** |
||
531 | * Returns a code for magic getter to perform interception |
||
532 | * |
||
533 | * @return string |
||
534 | */ |
||
535 | private function getMagicGetterBody() |
||
553 | |||
554 | /** |
||
555 | * Returns a code for magic setter to perform interception |
||
556 | * |
||
557 | * @return string |
||
558 | */ |
||
559 | private function getMagicSetterBody() |
||
576 | |||
577 | /** |
||
578 | * Returns constructor code |
||
579 | * |
||
580 | * @param ParsedMethod $constructor Constructor reflection |
||
581 | * @param bool $isCallParent Is there is a need to call parent code |
||
582 | * |
||
583 | * @return string |
||
584 | */ |
||
585 | private function getConstructorBody(ParsedMethod $constructor = null, $isCallParent = false) |
||
613 | } |
||
614 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.