Complex classes like Component 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 Component, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
101 | class Component extends BaseObject |
||
102 | { |
||
103 | /** |
||
104 | * @var array the attached event handlers (event name => handlers) |
||
105 | */ |
||
106 | private $_events = []; |
||
107 | /** |
||
108 | * @var array the event handlers attached for wildcard patterns (event name wildcard => handlers) |
||
109 | * @since 2.0.14 |
||
110 | */ |
||
111 | private $_eventWildcards = []; |
||
112 | /** |
||
113 | * @var Behavior[]|null the attached behaviors (behavior name => behavior). This is `null` when not initialized. |
||
114 | */ |
||
115 | private $_behaviors; |
||
116 | |||
117 | |||
118 | /** |
||
119 | * Returns the value of a component property. |
||
120 | * |
||
121 | * This method will check in the following order and act accordingly: |
||
122 | * |
||
123 | * - a property defined by a getter: return the getter result |
||
124 | * - a property of a behavior: return the behavior property value |
||
125 | * |
||
126 | * Do not call this method directly as it is a PHP magic method that |
||
127 | * will be implicitly called when executing `$value = $component->property;`. |
||
128 | * @param string $name the property name |
||
129 | * @return mixed the property value or the value of a behavior's property |
||
130 | * @throws UnknownPropertyException if the property is not defined |
||
131 | * @throws InvalidCallException if the property is write-only. |
||
132 | * @see __set() |
||
133 | */ |
||
134 | 1592 | public function __get($name) |
|
156 | |||
157 | /** |
||
158 | * Sets the value of a component property. |
||
159 | * |
||
160 | * This method will check in the following order and act accordingly: |
||
161 | * |
||
162 | * - a property defined by a setter: set the property value |
||
163 | * - an event in the format of "on xyz": attach the handler to the event "xyz" |
||
164 | * - a behavior in the format of "as xyz": attach the behavior named as "xyz" |
||
165 | * - a property of a behavior: set the behavior property value |
||
166 | * |
||
167 | * Do not call this method directly as it is a PHP magic method that |
||
168 | * will be implicitly called when executing `$component->property = $value;`. |
||
169 | * @param string $name the property name or the event name |
||
170 | * @param mixed $value the property value |
||
171 | * @throws UnknownPropertyException if the property is not defined |
||
172 | * @throws InvalidCallException if the property is read-only. |
||
173 | * @see __get() |
||
174 | */ |
||
175 | 3643 | public function __set($name, $value) |
|
211 | |||
212 | /** |
||
213 | * Checks if a property is set, i.e. defined and not null. |
||
214 | * |
||
215 | * This method will check in the following order and act accordingly: |
||
216 | * |
||
217 | * - a property defined by a setter: return whether the property is set |
||
218 | * - a property of a behavior: return whether the property is set |
||
219 | * - return `false` for non existing properties |
||
220 | * |
||
221 | * Do not call this method directly as it is a PHP magic method that |
||
222 | * will be implicitly called when executing `isset($component->property)`. |
||
223 | * @param string $name the property name or the event name |
||
224 | * @return bool whether the named property is set |
||
225 | * @see http://php.net/manual/en/function.isset.php |
||
226 | */ |
||
227 | 26 | public function __isset($name) |
|
244 | |||
245 | /** |
||
246 | * Sets a component property to be null. |
||
247 | * |
||
248 | * This method will check in the following order and act accordingly: |
||
249 | * |
||
250 | * - a property defined by a setter: set the property value to be null |
||
251 | * - a property of a behavior: set the property value to be null |
||
252 | * |
||
253 | * Do not call this method directly as it is a PHP magic method that |
||
254 | * will be implicitly called when executing `unset($component->property)`. |
||
255 | * @param string $name the property name |
||
256 | * @throws InvalidCallException if the property is read only. |
||
257 | * @see http://php.net/manual/en/function.unset.php |
||
258 | */ |
||
259 | 2 | public function __unset($name) |
|
260 | { |
||
261 | 2 | $setter = 'set' . $name; |
|
262 | 2 | if (method_exists($this, $setter)) { |
|
263 | 1 | $this->$setter(null); |
|
264 | 1 | return; |
|
265 | } |
||
266 | |||
267 | // behavior property |
||
268 | 2 | $this->ensureBehaviors(); |
|
269 | 2 | foreach ($this->_behaviors as $behavior) { |
|
270 | 1 | if ($behavior->canSetProperty($name)) { |
|
271 | 1 | $behavior->$name = null; |
|
272 | return; |
||
273 | } |
||
274 | } |
||
275 | |||
276 | 1 | throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name); |
|
277 | } |
||
278 | |||
279 | /** |
||
280 | * Calls the named method which is not a class method. |
||
281 | * |
||
282 | * This method will check if any attached behavior has |
||
283 | * the named method and will execute it if available. |
||
284 | * |
||
285 | * Do not call this method directly as it is a PHP magic method that |
||
286 | * will be implicitly called when an unknown method is being invoked. |
||
287 | * @param string $name the method name |
||
288 | * @param array $params method parameters |
||
289 | * @return mixed the method return value |
||
290 | * @throws UnknownMethodException when calling unknown method |
||
291 | */ |
||
292 | 14 | public function __call($name, $params) |
|
302 | |||
303 | /** |
||
304 | * This method is called after the object is created by cloning an existing one. |
||
305 | * It removes all behaviors because they are attached to the old object. |
||
306 | */ |
||
307 | 37 | public function __clone() |
|
313 | |||
314 | /** |
||
315 | * Returns a value indicating whether a property is defined for this component. |
||
316 | * |
||
317 | * A property is defined if: |
||
318 | * |
||
319 | * - the class has a getter or setter method associated with the specified name |
||
320 | * (in this case, property name is case-insensitive); |
||
321 | * - the class has a member variable with the specified name (when `$checkVars` is true); |
||
322 | * - an attached behavior has a property of the given name (when `$checkBehaviors` is true). |
||
323 | * |
||
324 | * @param string $name the property name |
||
325 | * @param bool $checkVars whether to treat member variables as properties |
||
326 | * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component |
||
327 | * @return bool whether the property is defined |
||
328 | * @see canGetProperty() |
||
329 | * @see canSetProperty() |
||
330 | */ |
||
331 | 5 | public function hasProperty($name, $checkVars = true, $checkBehaviors = true) |
|
335 | |||
336 | /** |
||
337 | * Returns a value indicating whether a property can be read. |
||
338 | * |
||
339 | * A property can be read if: |
||
340 | * |
||
341 | * - the class has a getter method associated with the specified name |
||
342 | * (in this case, property name is case-insensitive); |
||
343 | * - the class has a member variable with the specified name (when `$checkVars` is true); |
||
344 | * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true). |
||
345 | * |
||
346 | * @param string $name the property name |
||
347 | * @param bool $checkVars whether to treat member variables as properties |
||
348 | * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component |
||
349 | * @return bool whether the property can be read |
||
350 | * @see canSetProperty() |
||
351 | */ |
||
352 | 7 | public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) |
|
367 | |||
368 | /** |
||
369 | * Returns a value indicating whether a property can be set. |
||
370 | * |
||
371 | * A property can be written if: |
||
372 | * |
||
373 | * - the class has a setter method associated with the specified name |
||
374 | * (in this case, property name is case-insensitive); |
||
375 | * - the class has a member variable with the specified name (when `$checkVars` is true); |
||
376 | * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true). |
||
377 | * |
||
378 | * @param string $name the property name |
||
379 | * @param bool $checkVars whether to treat member variables as properties |
||
380 | * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component |
||
381 | * @return bool whether the property can be written |
||
382 | * @see canGetProperty() |
||
383 | */ |
||
384 | 18 | public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) |
|
399 | |||
400 | /** |
||
401 | * Returns a value indicating whether a method is defined. |
||
402 | * |
||
403 | * A method is defined if: |
||
404 | * |
||
405 | * - the class has a method with the specified name |
||
406 | * - an attached behavior has a method with the given name (when `$checkBehaviors` is true). |
||
407 | * |
||
408 | * @param string $name the property name |
||
409 | * @param bool $checkBehaviors whether to treat behaviors' methods as methods of this component |
||
410 | * @return bool whether the method is defined |
||
411 | */ |
||
412 | 100 | public function hasMethod($name, $checkBehaviors = true) |
|
427 | |||
428 | /** |
||
429 | * Returns a list of behaviors that this component should behave as. |
||
430 | * |
||
431 | * Child classes may override this method to specify the behaviors they want to behave as. |
||
432 | * |
||
433 | * The return value of this method should be an array of behavior objects or configurations |
||
434 | * indexed by behavior names. A behavior configuration can be either a string specifying |
||
435 | * the behavior class or an array of the following structure: |
||
436 | * |
||
437 | * ```php |
||
438 | * 'behaviorName' => [ |
||
439 | * 'class' => 'BehaviorClass', |
||
440 | * 'property1' => 'value1', |
||
441 | * 'property2' => 'value2', |
||
442 | * ] |
||
443 | * ``` |
||
444 | * |
||
445 | * Note that a behavior class must extend from [[Behavior]]. Behaviors can be attached using a name or anonymously. |
||
446 | * When a name is used as the array key, using this name, the behavior can later be retrieved using [[getBehavior()]] |
||
447 | * or be detached using [[detachBehavior()]]. Anonymous behaviors can not be retrieved or detached. |
||
448 | * |
||
449 | * Behaviors declared in this method will be attached to the component automatically (on demand). |
||
450 | * |
||
451 | * @return array the behavior configurations. |
||
452 | */ |
||
453 | 2020 | public function behaviors() |
|
457 | |||
458 | /** |
||
459 | * Returns a value indicating whether there is any handler attached to the named event. |
||
460 | * @param string $name the event name |
||
461 | * @return bool whether there is any handler attached to the event. |
||
462 | */ |
||
463 | 98 | public function hasEventHandlers($name) |
|
475 | |||
476 | /** |
||
477 | * Attaches an event handler to an event. |
||
478 | * |
||
479 | * The event handler must be a valid PHP callback. The following are |
||
480 | * some examples: |
||
481 | * |
||
482 | * ``` |
||
483 | * function ($event) { ... } // anonymous function |
||
484 | * [$object, 'handleClick'] // $object->handleClick() |
||
485 | * ['Page', 'handleClick'] // Page::handleClick() |
||
486 | * 'handleClick' // global function handleClick() |
||
487 | * ``` |
||
488 | * |
||
489 | * The event handler must be defined with the following signature, |
||
490 | * |
||
491 | * ``` |
||
492 | * function ($event) |
||
493 | * ``` |
||
494 | * |
||
495 | * where `$event` is an [[Event]] object which includes parameters associated with the event. |
||
496 | * |
||
497 | * Since 2.0.14 you can specify event name as a wildcard pattern: |
||
498 | * |
||
499 | * ```php |
||
500 | * $component->on('event.group.*', function ($event) { |
||
501 | * Yii::trace($event->name . ' is triggered.'); |
||
502 | * }); |
||
503 | * ``` |
||
504 | * |
||
505 | * @param string $name the event name |
||
506 | * @param callable $handler the event handler |
||
507 | * @param mixed $data the data to be passed to the event handler when the event is triggered. |
||
508 | * When the event handler is invoked, this data can be accessed via [[Event::data]]. |
||
509 | * @param bool $append whether to append new event handler to the end of the existing |
||
510 | * handler list. If false, the new handler will be inserted at the beginning of the existing |
||
511 | * handler list. |
||
512 | * @see off() |
||
513 | */ |
||
514 | 148 | public function on($name, $handler, $data = null, $append = true) |
|
533 | |||
534 | /** |
||
535 | * Detaches an existing event handler from this component. |
||
536 | * |
||
537 | * This method is the opposite of [[on()]]. |
||
538 | * |
||
539 | * Note: in case wildcard pattern is passed for event name, only the handlers registered with this |
||
540 | * wildcard will be removed, while handlers registered with plain names matching this wildcard will remain. |
||
541 | * |
||
542 | * @param string $name event name |
||
543 | * @param callable $handler the event handler to be removed. |
||
544 | * If it is null, all handlers attached to the named event will be removed. |
||
545 | * @return bool if a handler is found and detached |
||
546 | * @see on() |
||
547 | */ |
||
548 | 66 | public function off($name, $handler = null) |
|
593 | |||
594 | /** |
||
595 | * Triggers an event. |
||
596 | * This method represents the happening of an event. It invokes |
||
597 | * all attached handlers for the event including class-level handlers. |
||
598 | * @param string $name the event name |
||
599 | * @param Event $event the event parameter. If not set, a default [[Event]] object will be created. |
||
600 | */ |
||
601 | 1942 | public function trigger($name, Event $event = null) |
|
638 | |||
639 | /** |
||
640 | * Returns the named behavior object. |
||
641 | * @param string $name the behavior name |
||
642 | * @return null|Behavior the behavior object, or null if the behavior does not exist |
||
643 | */ |
||
644 | 29 | public function getBehavior($name) |
|
649 | |||
650 | /** |
||
651 | * Returns all behaviors attached to this component. |
||
652 | * @return Behavior[] list of behaviors attached to this component |
||
653 | */ |
||
654 | 1 | public function getBehaviors() |
|
659 | |||
660 | /** |
||
661 | * Attaches a behavior to this component. |
||
662 | * This method will create the behavior object based on the given |
||
663 | * configuration. After that, the behavior object will be attached to |
||
664 | * this component by calling the [[Behavior::attach()]] method. |
||
665 | * @param string $name the name of the behavior. |
||
666 | * @param string|array|Behavior $behavior the behavior configuration. This can be one of the following: |
||
667 | * |
||
668 | * - a [[Behavior]] object |
||
669 | * - a string specifying the behavior class |
||
670 | * - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object. |
||
671 | * |
||
672 | * @return Behavior the behavior object |
||
673 | * @see detachBehavior() |
||
674 | */ |
||
675 | 13 | public function attachBehavior($name, $behavior) |
|
680 | |||
681 | /** |
||
682 | * Attaches a list of behaviors to the component. |
||
683 | * Each behavior is indexed by its name and should be a [[Behavior]] object, |
||
684 | * a string specifying the behavior class, or an configuration array for creating the behavior. |
||
685 | * @param array $behaviors list of behaviors to be attached to the component |
||
686 | * @see attachBehavior() |
||
687 | */ |
||
688 | 3 | public function attachBehaviors($behaviors) |
|
695 | |||
696 | /** |
||
697 | * Detaches a behavior from the component. |
||
698 | * The behavior's [[Behavior::detach()]] method will be invoked. |
||
699 | * @param string $name the behavior's name. |
||
700 | * @return null|Behavior the detached behavior. Null if the behavior does not exist. |
||
701 | */ |
||
702 | 4 | public function detachBehavior($name) |
|
714 | |||
715 | /** |
||
716 | * Detaches all behaviors from the component. |
||
717 | */ |
||
718 | 1 | public function detachBehaviors() |
|
725 | |||
726 | /** |
||
727 | * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component. |
||
728 | */ |
||
729 | 2021 | public function ensureBehaviors() |
|
738 | |||
739 | /** |
||
740 | * Attaches a behavior to this component. |
||
741 | * @param string|int $name the name of the behavior. If this is an integer, it means the behavior |
||
742 | * is an anonymous one. Otherwise, the behavior is a named one and any existing behavior with the same name |
||
743 | * will be detached first. |
||
744 | * @param string|array|Behavior $behavior the behavior to be attached |
||
745 | * @return Behavior the attached behavior. |
||
746 | */ |
||
747 | 130 | private function attachBehaviorInternal($name, $behavior) |
|
765 | } |
||
766 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: