Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AbstractViewHelper 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 AbstractViewHelper, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | abstract class AbstractViewHelper extends AbstractNode implements ViewHelperInterface |
||
25 | { |
||
26 | |||
27 | /** |
||
28 | * Stores all \TYPO3Fluid\Fluid\ArgumentDefinition instances |
||
29 | * @var ArgumentDefinition[] |
||
30 | */ |
||
31 | protected $argumentDefinitions = []; |
||
32 | |||
33 | /** |
||
34 | * Cache of argument definitions; the key is the ViewHelper class name, and the |
||
35 | * value is the array of argument definitions. |
||
36 | * |
||
37 | * In our benchmarks, this cache leads to a 40% improvement when using a certain |
||
38 | * ViewHelper class many times throughout the rendering process. |
||
39 | * @var array |
||
40 | */ |
||
41 | static private $argumentDefinitionCache = []; |
||
42 | |||
43 | /** |
||
44 | * Current view helper node |
||
45 | * @var ViewHelperNode |
||
46 | */ |
||
47 | protected $viewHelperNode; |
||
48 | |||
49 | /** |
||
50 | * Arguments array. |
||
51 | * @var array |
||
52 | * @api |
||
53 | */ |
||
54 | protected $arguments = []; |
||
55 | |||
56 | /** |
||
57 | * @var array |
||
58 | */ |
||
59 | protected $parsedArguments = []; |
||
60 | |||
61 | /** |
||
62 | * @var NodeInterface[] array |
||
63 | * @api |
||
64 | */ |
||
65 | protected $childNodes = []; |
||
66 | |||
67 | /** |
||
68 | * Current variable container reference. |
||
69 | * @var VariableProviderInterface |
||
70 | * @api |
||
71 | */ |
||
72 | protected $templateVariableContainer; |
||
73 | |||
74 | /** |
||
75 | * @var RenderingContextInterface |
||
76 | */ |
||
77 | protected $renderingContext; |
||
78 | |||
79 | /** |
||
80 | * @var \Closure |
||
81 | */ |
||
82 | protected $renderChildrenClosure = null; |
||
83 | |||
84 | /** |
||
85 | * ViewHelper Variable Container |
||
86 | * @var ViewHelperVariableContainer |
||
87 | * @api |
||
88 | */ |
||
89 | protected $viewHelperVariableContainer; |
||
90 | |||
91 | /** |
||
92 | * Specifies whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper |
||
93 | * @see isChildrenEscapingEnabled() |
||
94 | * |
||
95 | * Note: If this is NULL the value of $this->escapingInterceptorEnabled is considered for backwards compatibility |
||
96 | * |
||
97 | * @var boolean |
||
98 | * @api |
||
99 | */ |
||
100 | protected $escapeChildren = null; |
||
101 | |||
102 | /** |
||
103 | * Specifies whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper |
||
104 | * @see isOutputEscapingEnabled() |
||
105 | * |
||
106 | * @var boolean |
||
107 | * @api |
||
108 | */ |
||
109 | protected $escapeOutput = null; |
||
110 | |||
111 | /** |
||
112 | * @param array $arguments |
||
113 | * @param ParsedTemplateInterface $parsedTemplate |
||
114 | * @param RenderingContextInterface $renderingContext |
||
115 | * @return NodeInterface |
||
116 | */ |
||
117 | public function postParse(array $arguments, ParsedTemplateInterface $parsedTemplate, RenderingContextInterface $renderingContext): NodeInterface |
||
125 | |||
126 | public function setParsedArguments(array $parsedArguments): NodeInterface |
||
131 | |||
132 | public function getParsedArguments(): array |
||
136 | |||
137 | public function evaluate(RenderingContextInterface $renderingContext) |
||
149 | |||
150 | /** |
||
151 | * @param NodeInterface[]|mixed[] $arguments |
||
152 | * @throws Exception |
||
153 | */ |
||
154 | View Code Duplication | protected function validateParsedArguments(array $arguments) |
|
165 | |||
166 | /** |
||
167 | * Creates arguments by padding with missing+optional arguments |
||
168 | * and casting or creating BooleanNode where appropriate. Input |
||
169 | * array may not contain all arguments - output array will. |
||
170 | * |
||
171 | * @param array $arguments |
||
172 | * @return array |
||
173 | */ |
||
174 | protected function createArguments(array $arguments): array |
||
197 | |||
198 | /** |
||
199 | * @param array $arguments |
||
200 | * @return void |
||
201 | */ |
||
202 | public function setArguments(array $arguments) |
||
206 | |||
207 | /** |
||
208 | * @param RenderingContextInterface $renderingContext |
||
209 | * @return void |
||
210 | */ |
||
211 | public function setRenderingContext(RenderingContextInterface $renderingContext) |
||
217 | |||
218 | /** |
||
219 | * Returns whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper |
||
220 | * |
||
221 | * Note: This method is no public API, use $this->escapeChildren instead! |
||
222 | * |
||
223 | * @return boolean |
||
224 | */ |
||
225 | public function isChildrenEscapingEnabled() |
||
233 | |||
234 | /** |
||
235 | * Returns whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper |
||
236 | * |
||
237 | * Note: This method is no public API, use $this->escapeChildren instead! |
||
238 | * |
||
239 | * @return boolean |
||
240 | */ |
||
241 | public function isOutputEscapingEnabled() |
||
245 | |||
246 | /** |
||
247 | * Register a new argument. Call this method from your ViewHelper subclass |
||
248 | * inside the initializeArguments() method. |
||
249 | * |
||
250 | * @param string $name Name of the argument |
||
251 | * @param string $type Type of the argument |
||
252 | * @param string $description Description of the argument |
||
253 | * @param boolean $required If TRUE, argument is required. Defaults to FALSE. |
||
254 | * @param mixed $defaultValue Default value of argument |
||
255 | * @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining. |
||
256 | * @throws Exception |
||
257 | * @api |
||
258 | */ |
||
259 | View Code Duplication | protected function registerArgument($name, $type, $description, $required = false, $defaultValue = null) |
|
270 | |||
271 | /** |
||
272 | * Overrides a registered argument. Call this method from your ViewHelper subclass |
||
273 | * inside the initializeArguments() method if you want to override a previously registered argument. |
||
274 | * @see registerArgument() |
||
275 | * |
||
276 | * @param string $name Name of the argument |
||
277 | * @param string $type Type of the argument |
||
278 | * @param string $description Description of the argument |
||
279 | * @param boolean $required If TRUE, argument is required. Defaults to FALSE. |
||
280 | * @param mixed $defaultValue Default value of argument |
||
281 | * @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining. |
||
282 | * @throws Exception |
||
283 | * @api |
||
284 | */ |
||
285 | View Code Duplication | protected function overrideArgument($name, $type, $description, $required = false, $defaultValue = null) |
|
296 | |||
297 | /** |
||
298 | * Sets all needed attributes needed for the rendering. Called by the |
||
299 | * framework. Populates $this->viewHelperNode. |
||
300 | * This is PURELY INTERNAL! Never override this method!! |
||
301 | * |
||
302 | * @param ViewHelperNode $node View Helper node to be set. |
||
303 | * @return void |
||
304 | */ |
||
305 | public function setViewHelperNode(ViewHelperNode $node) |
||
309 | |||
310 | /** |
||
311 | * Sets all needed attributes needed for the rendering. Called by the |
||
312 | * framework. Populates $this->viewHelperNode. |
||
313 | * This is PURELY INTERNAL! Never override this method!! |
||
314 | * |
||
315 | * @param NodeInterface[] $childNodes |
||
316 | * @return void |
||
317 | */ |
||
318 | public function setChildNodes(array $childNodes) |
||
322 | |||
323 | /** |
||
324 | * Called when being inside a cached template. |
||
325 | * |
||
326 | * @param \Closure $renderChildrenClosure |
||
327 | * @return void |
||
328 | */ |
||
329 | public function setRenderChildrenClosure(\Closure $renderChildrenClosure) |
||
333 | |||
334 | /** |
||
335 | * Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper. |
||
336 | * |
||
337 | * @return string the rendered ViewHelper. |
||
338 | */ |
||
339 | public function initializeArgumentsAndRender() |
||
346 | |||
347 | /** |
||
348 | * Call the render() method and handle errors. |
||
349 | * |
||
350 | * @return string the rendered ViewHelper |
||
351 | * @throws Exception |
||
352 | */ |
||
353 | protected function callRenderMethod() |
||
372 | |||
373 | /** |
||
374 | * Initializes the view helper before invoking the render method. |
||
375 | * |
||
376 | * Override this method to solve tasks before the view helper content is rendered. |
||
377 | * |
||
378 | * @return void |
||
379 | * @api |
||
380 | */ |
||
381 | public function initialize() |
||
384 | |||
385 | /** |
||
386 | * Helper method which triggers the rendering of everything between the |
||
387 | * opening and the closing tag. |
||
388 | * |
||
389 | * @return mixed The finally rendered child nodes. |
||
390 | * @api |
||
391 | */ |
||
392 | public function renderChildren() |
||
400 | |||
401 | /** |
||
402 | * Helper which is mostly needed when calling renderStatic() from within |
||
403 | * render(). |
||
404 | * |
||
405 | * No public API yet. |
||
406 | * |
||
407 | * @return \Closure |
||
408 | */ |
||
409 | protected function buildRenderChildrenClosure() |
||
416 | |||
417 | /** |
||
418 | * Initialize all arguments and return them |
||
419 | * |
||
420 | * @return ArgumentDefinition[] |
||
421 | */ |
||
422 | public function prepareArguments() |
||
433 | |||
434 | /** |
||
435 | * Validate arguments, and throw exception if arguments do not validate. |
||
436 | * |
||
437 | * @return void |
||
438 | * @throws \InvalidArgumentException |
||
439 | */ |
||
440 | public function validateArguments() |
||
460 | |||
461 | /** |
||
462 | * Check whether the defined type matches the value type |
||
463 | * |
||
464 | * @param string $type |
||
465 | * @param mixed $value |
||
466 | * @return boolean |
||
467 | */ |
||
468 | protected function isValidType($type, $value) |
||
497 | |||
498 | /** |
||
499 | * Return the first element of the given array, ArrayAccess or Traversable |
||
500 | * that is not empty |
||
501 | * |
||
502 | * @param mixed $value |
||
503 | * @return mixed |
||
504 | */ |
||
505 | protected function getFirstElementOfNonEmpty($value) |
||
516 | |||
517 | /** |
||
518 | * Initialize all arguments. You need to override this method and call |
||
519 | * $this->registerArgument(...) inside this method, to register all your arguments. |
||
520 | * |
||
521 | * @return void |
||
522 | * @api |
||
523 | */ |
||
524 | public function initializeArguments() |
||
527 | |||
528 | /** |
||
529 | * Tests if the given $argumentName is set, and not NULL. |
||
530 | * The isset() test used fills both those requirements. |
||
531 | * |
||
532 | * @param string $argumentName |
||
533 | * @return boolean TRUE if $argumentName is found, FALSE otherwise |
||
534 | * @api |
||
535 | */ |
||
536 | protected function hasArgument($argumentName) |
||
540 | |||
541 | /** |
||
542 | * Default implementation of "handling" additional, undeclared arguments. |
||
543 | * In this implementation the behavior is to consistently throw an error |
||
544 | * about NOT supporting any additional arguments. This method MUST be |
||
545 | * overridden by any ViewHelper that desires this support and this inherited |
||
546 | * method must not be called, obviously. |
||
547 | * |
||
548 | * @throws Exception |
||
549 | * @param array $arguments |
||
550 | * @return void |
||
551 | */ |
||
552 | public function handleAdditionalArguments(array $arguments) |
||
555 | |||
556 | /** |
||
557 | * Default implementation of validating additional, undeclared arguments. |
||
558 | * In this implementation the behavior is to consistently throw an error |
||
559 | * about NOT supporting any additional arguments. This method MUST be |
||
560 | * overridden by any ViewHelper that desires this support and this inherited |
||
561 | * method must not be called, obviously. |
||
562 | * |
||
563 | * @throws Exception |
||
564 | * @param array $arguments |
||
565 | * @return void |
||
566 | */ |
||
567 | public function validateAdditionalArguments(array $arguments) |
||
580 | |||
581 | /** |
||
582 | * You only should override this method *when you absolutely know what you |
||
583 | * are doing*, and really want to influence the generated PHP code during |
||
584 | * template compilation directly. |
||
585 | * |
||
586 | * @param string $argumentsName |
||
587 | * @param string $closureName |
||
588 | * @param string $initializationPhpCode |
||
589 | * @param ViewHelperNode $node |
||
590 | * @param TemplateCompiler $compiler |
||
591 | * @return string |
||
592 | */ |
||
593 | public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) |
||
602 | |||
603 | /** |
||
604 | * Default implementation of static rendering; useful API method if your ViewHelper |
||
605 | * when compiled is able to render itself statically to increase performance. This |
||
606 | * default implementation will simply delegate to the ViewHelperInvoker. |
||
607 | * |
||
608 | * @param array $arguments |
||
609 | * @param \Closure $renderChildrenClosure |
||
610 | * @param RenderingContextInterface $renderingContext |
||
611 | * @return mixed |
||
612 | */ |
||
613 | public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) |
||
618 | |||
619 | /** |
||
620 | * Save the associated ViewHelper node in a static public class variable. |
||
621 | * called directly after the ViewHelper was built. |
||
622 | * |
||
623 | * @param ViewHelperNode $node |
||
624 | * @param TextNode[] $arguments |
||
625 | * @param VariableProviderInterface $variableContainer |
||
626 | * @return void |
||
627 | */ |
||
628 | public static function postParseEvent(ViewHelperNode $node, array $arguments, VariableProviderInterface $variableContainer) |
||
631 | |||
632 | /** |
||
633 | * Resets the ViewHelper state. |
||
634 | * |
||
635 | * Overwrite this method if you need to get a clean state of your ViewHelper. |
||
636 | * |
||
637 | * @return void |
||
638 | */ |
||
639 | public function resetState() |
||
642 | } |
||
643 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.