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 Hooks 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 Hooks, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
42 | class Hooks |
||
43 | { |
||
44 | /** |
||
45 | * Filters - holds list of hooks |
||
46 | * |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $filters = []; |
||
50 | |||
51 | /** |
||
52 | * Merged Filters |
||
53 | * |
||
54 | * @var array |
||
55 | */ |
||
56 | protected $merged_filters = []; |
||
57 | |||
58 | /** |
||
59 | * Actions |
||
60 | * |
||
61 | * @var array |
||
62 | */ |
||
63 | protected $actions = []; |
||
64 | |||
65 | /** |
||
66 | * Current Filter - holds the name of the current filter |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | protected $current_filter = []; |
||
71 | |||
72 | /** |
||
73 | * Container for storing shortcode tags and their hook to call for the shortcode |
||
74 | * |
||
75 | * @var array |
||
76 | */ |
||
77 | public static $shortcode_tags = []; |
||
78 | |||
79 | /** |
||
80 | * Default priority |
||
81 | * |
||
82 | * @const int |
||
83 | */ |
||
84 | const PRIORITY_NEUTRAL = 50; |
||
85 | |||
86 | /** |
||
87 | 1 | * This class is not allowed to call from outside: private! |
|
88 | */ |
||
89 | 1 | protected function __construct() |
|
92 | |||
93 | /** |
||
94 | * Prevent the object from being cloned. |
||
95 | */ |
||
96 | protected function __clone() |
||
99 | |||
100 | /** |
||
101 | * Avoid serialization. |
||
102 | */ |
||
103 | public function __wakeup() |
||
106 | |||
107 | /** |
||
108 | * Returns a Singleton instance of this class. |
||
109 | * |
||
110 | 10 | * @return Hooks |
|
111 | */ |
||
112 | 10 | public static function getInstance(): self |
|
122 | |||
123 | /** |
||
124 | * FILTERS |
||
125 | */ |
||
126 | |||
127 | /** |
||
128 | * Adds Hooks to a function or method to a specific filter action. |
||
129 | * |
||
130 | * @param string $tag <p> |
||
131 | * The name of the filter to hook the |
||
132 | * {@link $function_to_add} to. |
||
133 | * </p> |
||
134 | * @param string|array|object $function_to_add <p> |
||
135 | * The name of the function to be called |
||
136 | * when the filter is applied. |
||
137 | * </p> |
||
138 | * @param int $priority <p> |
||
139 | * [optional] Used to specify the order in |
||
140 | * which the functions associated with a |
||
141 | * particular action are executed (default: 50). |
||
142 | * Lower numbers correspond with earlier execution, |
||
143 | * and functions with the same priority are executed |
||
144 | * in the order in which they were added to the action. |
||
145 | * </p> |
||
146 | * @param string $include_path <p> |
||
147 | * [optional] File to include before executing the callback. |
||
148 | * </p> |
||
149 | * |
||
150 | 7 | * @return bool |
|
151 | */ |
||
152 | 7 | public function add_filter(string $tag, $function_to_add, int $priority = self::PRIORITY_NEUTRAL, string $include_path = null): bool |
|
165 | |||
166 | /** |
||
167 | * Removes a function from a specified filter hook. |
||
168 | * |
||
169 | * @param string $tag <p>The filter hook to which the function to be removed is |
||
170 | * hooked.</p> |
||
171 | * @param string|array|object $function_to_remove <p>The name of the function which should be removed.</p> |
||
172 | * @param int $priority <p>[optional] The priority of the function (default: 50).</p> |
||
173 | 1 | * |
|
174 | * @return bool |
||
175 | 1 | */ |
|
176 | public function remove_filter(string $tag, $function_to_remove, int $priority = self::PRIORITY_NEUTRAL): bool |
||
193 | |||
194 | /** |
||
195 | * Remove all of the hooks from a filter. |
||
196 | * |
||
197 | * @param string $tag <p>The filter to remove hooks from.</p> |
||
198 | * @param false|int $priority <p>The priority number to remove.</p> |
||
199 | 4 | * |
|
200 | * @return bool |
||
201 | 4 | */ |
|
202 | public function remove_all_filters(string $tag, $priority = false): bool |
||
220 | |||
221 | /** |
||
222 | * Check if any filter has been registered for the given hook. |
||
223 | * |
||
224 | * <p> |
||
225 | * <br /> |
||
226 | * <strong>INFO:</strong> Use !== false to check if it's true! |
||
227 | * </p> |
||
228 | * |
||
229 | * @param string $tag <p>The name of the filter hook.</p> |
||
230 | * @param false|string $function_to_check <p>[optional] Callback function name to check for </p> |
||
231 | * |
||
232 | * @return mixed <p> |
||
233 | * If {@link $function_to_check} is omitted, |
||
234 | * returns boolean for whether the hook has |
||
235 | * anything registered. |
||
236 | * When checking a specific function, the priority |
||
237 | * of that hook is returned, or false if the |
||
238 | * function is not attached. |
||
239 | * When using the {@link $function_to_check} argument, |
||
240 | * this function may return a non-boolean value that |
||
241 | * evaluates to false |
||
242 | 3 | * (e.g.) 0, so use the === operator for testing the return value. |
|
243 | * </p> |
||
244 | 3 | */ |
|
245 | 3 | public function has_filter(string $tag, $function_to_check = false) |
|
264 | |||
265 | /** |
||
266 | * Call the functions added to a filter hook. |
||
267 | * |
||
268 | * <p> |
||
269 | * <br /> |
||
270 | * <strong>INFO:</strong> Additional variables passed to the functions hooked to <tt>$tag</tt>. |
||
271 | * </p> |
||
272 | * |
||
273 | * @param string $tag <p>The name of the filter hook.</p> |
||
274 | * @param mixed $value <p>The value on which the filters hooked to <tt>$tag</tt> are applied on.</p> |
||
275 | 4 | * |
|
276 | * @return mixed <p>The filtered value after all hooked functions are applied to it.</p> |
||
277 | 4 | */ |
|
278 | public function apply_filters(string $tag, $value) |
||
334 | |||
335 | /** |
||
336 | * Execute functions hooked on a specific filter hook, specifying arguments in an array. |
||
337 | * |
||
338 | * @param string $tag <p>The name of the filter hook.</p> |
||
339 | * @param array $args <p>The arguments supplied to the functions hooked to <tt>$tag</tt></p> |
||
340 | 1 | * |
|
341 | * @return mixed <p>The filtered value after all hooked functions are applied to it.</p> |
||
342 | */ |
||
343 | 1 | public function apply_filters_ref_array(string $tag, array $args) |
|
390 | |||
391 | /** |
||
392 | * Hooks a function on to a specific action. |
||
393 | * |
||
394 | * @param string $tag <p> |
||
395 | * The name of the action to which the |
||
396 | * <tt>$function_to_add</tt> is hooked. |
||
397 | * </p> |
||
398 | * @param string|array $function_to_add <p>The name of the function you wish to be called.</p> |
||
399 | * @param int $priority <p> |
||
400 | * [optional] Used to specify the order in which |
||
401 | * the functions associated with a particular |
||
402 | * action are executed (default: 50). |
||
403 | * Lower numbers correspond with earlier execution, |
||
404 | * and functions with the same priority are executed |
||
405 | * in the order in which they were added to the action. |
||
406 | * </p> |
||
407 | * @param string $include_path <p>[optional] File to include before executing the callback.</p> |
||
408 | 5 | * |
|
409 | * @return bool |
||
410 | 5 | */ |
|
411 | public function add_action( |
||
420 | |||
421 | /** |
||
422 | * Check if any action has been registered for a hook. |
||
423 | * |
||
424 | * <p> |
||
425 | * <br /> |
||
426 | * <strong>INFO:</strong> Use !== false to check if it's true! |
||
427 | * </p> |
||
428 | * |
||
429 | * @param string $tag <p>The name of the action hook.</p> |
||
430 | * @param false|string $function_to_check <p>[optional]</p> |
||
431 | * |
||
432 | * @return mixed <p> |
||
433 | * If <tt>$function_to_check</tt> is omitted, |
||
434 | * returns boolean for whether the hook has |
||
435 | * anything registered. |
||
436 | * When checking a specific function, |
||
437 | 3 | * the priority of that hook is returned, |
|
438 | * or false if the function is not attached. |
||
439 | 3 | * When using the <tt>$function_to_check</tt> |
|
440 | * argument, this function may return a non-boolean |
||
441 | * value that evaluates to false (e.g.) 0, |
||
442 | * so use the === operator for testing the return value. |
||
443 | * </p> |
||
444 | */ |
||
445 | public function has_action(string $tag, $function_to_check = false) |
||
449 | |||
450 | /** |
||
451 | 1 | * Removes a function from a specified action hook. |
|
452 | * |
||
453 | 1 | * @param string $tag <p>The action hook to which the function to be removed is hooked.</p> |
|
454 | * @param mixed $function_to_remove <p>The name of the function which should be removed.</p> |
||
455 | * @param int $priority <p>[optional] The priority of the function (default: 50).</p> |
||
456 | * |
||
457 | * @return bool <p>Whether the function is removed.</p> |
||
458 | */ |
||
459 | public function remove_action(string $tag, $function_to_remove, int $priority = self::PRIORITY_NEUTRAL): bool |
||
463 | |||
464 | 4 | /** |
|
465 | * Remove all of the hooks from an action. |
||
466 | 4 | * |
|
467 | * @param string $tag <p>The action to remove hooks from.</p> |
||
468 | * @param false|int $priority <p>The priority number to remove them from.</p> |
||
469 | * |
||
470 | * @return bool |
||
471 | */ |
||
472 | public function remove_all_actions(string $tag, $priority = false): bool |
||
476 | |||
477 | /** |
||
478 | * Execute functions hooked on a specific action hook. |
||
479 | * |
||
480 | 2 | * @param string $tag <p>The name of the action to be executed.</p> |
|
481 | * @param mixed $arg <p> |
||
482 | 2 | * [optional] Additional arguments which are passed on |
|
483 | * to the functions hooked to the action. |
||
484 | * </p> |
||
485 | * |
||
486 | 2 | * @return bool <p>Will return false if $tag does not exist in $filter array.</p> |
|
487 | 2 | */ |
|
488 | 2 | public function do_action(string $tag, $arg = ''): bool |
|
567 | |||
568 | 1 | /** |
|
569 | * Execute functions hooked on a specific action hook, specifying arguments in an array. |
||
570 | 1 | * |
|
571 | * @param string $tag <p>The name of the action to be executed.</p> |
||
572 | * @param array $args <p>The arguments supplied to the functions hooked to <tt>$tag</tt></p> |
||
573 | * |
||
574 | 1 | * @return bool <p>Will return false if $tag does not exist in $filter array.</p> |
|
575 | 1 | */ |
|
576 | 1 | public function do_action_ref_array(string $tag, array $args): bool |
|
633 | 1 | ||
634 | /** |
||
635 | 1 | * Retrieve the number of times an action has fired. |
|
636 | * |
||
637 | * @param string $tag <p>The name of the action hook.</p> |
||
638 | * |
||
639 | 1 | * @return int <p>The number of times action hook <tt>$tag</tt> is fired.</p> |
|
640 | */ |
||
641 | public function did_action(string $tag): int |
||
649 | |||
650 | /** |
||
651 | * Retrieve the name of the current filter or action. |
||
652 | * |
||
653 | * @return string <p>Hook name of the current filter or action.</p> |
||
654 | */ |
||
655 | public function current_filter(): string |
||
659 | |||
660 | /** |
||
661 | * Build Unique ID for storage and retrieval. |
||
662 | * |
||
663 | 7 | * @param string|array|object $function <p>Used for creating unique id.</p> |
|
664 | * |
||
665 | 7 | * @return string|false <p> |
|
666 | 3 | * Unique ID for usage as array key or false if |
|
667 | * $priority === false and $function is an |
||
668 | * object reference, and it does not already have a unique id. |
||
669 | 4 | * </p> |
|
670 | */ |
||
671 | private function _filter_build_unique_id($function) |
||
699 | 1 | ||
700 | /** |
||
701 | * Call "All" Hook |
||
702 | 1 | * |
|
703 | 1 | * @param array $args |
|
704 | */ |
||
705 | 1 | public function _call_all_hook(array $args) |
|
723 | |||
724 | /** @noinspection MagicMethodsValidityInspection */ |
||
725 | /** |
||
726 | * @param array $args |
||
727 | * |
||
728 | * @deprecated use "this->_call_all_hook()" |
||
729 | */ |
||
730 | public function __call_all_hook(array $args) |
||
737 | |||
738 | /** |
||
739 | * Add hook for shortcode tag. |
||
740 | * |
||
741 | * <p> |
||
742 | * <br /> |
||
743 | * There can only be one hook for each shortcode. Which means that if another |
||
744 | * plugin has a similar shortcode, it will override yours or yours will override |
||
745 | * theirs depending on which order the plugins are included and/or ran. |
||
746 | * <br /> |
||
747 | * <br /> |
||
748 | * </p> |
||
749 | * |
||
750 | * Simplest example of a shortcode tag using the API: |
||
751 | * |
||
752 | * <code> |
||
753 | * // [footag foo="bar"] |
||
754 | * function footag_func($atts) { |
||
755 | * return "foo = {$atts[foo]}"; |
||
756 | * } |
||
757 | * add_shortcode('footag', 'footag_func'); |
||
758 | * </code> |
||
759 | * |
||
760 | * Example with nice attribute defaults: |
||
761 | * |
||
762 | * <code> |
||
763 | * // [bartag foo="bar"] |
||
764 | * function bartag_func($atts) { |
||
765 | * $args = shortcode_atts(array( |
||
766 | * 'foo' => 'no foo', |
||
767 | * 'baz' => 'default baz', |
||
768 | * ), $atts); |
||
769 | * |
||
770 | * return "foo = {$args['foo']}"; |
||
771 | * } |
||
772 | * add_shortcode('bartag', 'bartag_func'); |
||
773 | * </code> |
||
774 | * |
||
775 | * Example with enclosed content: |
||
776 | * |
||
777 | * <code> |
||
778 | * // [baztag]content[/baztag] |
||
779 | * function baztag_func($atts, $content='') { |
||
780 | * return "content = $content"; |
||
781 | * } |
||
782 | 2 | * add_shortcode('baztag', 'baztag_func'); |
|
783 | * </code> |
||
784 | 2 | * |
|
785 | 2 | * @param string $tag <p>Shortcode tag to be searched in post content.</p> |
|
786 | * @param callable $func <p>Hook to run when shortcode is found.</p> |
||
787 | 2 | * |
|
788 | * @return bool |
||
789 | */ |
||
790 | public function add_shortcode(string $tag, $func): bool |
||
800 | 1 | ||
801 | /** |
||
802 | 1 | * Removes hook for shortcode. |
|
803 | 1 | * |
|
804 | * @param string $tag <p>shortcode tag to remove hook for.</p> |
||
805 | 1 | * |
|
806 | * @return bool |
||
807 | */ |
||
808 | public function remove_shortcode(string $tag): bool |
||
818 | 1 | ||
819 | /** |
||
820 | 1 | * This function is simple, it clears all of the shortcode tags by replacing the |
|
821 | * shortcodes by a empty array. This is actually a very efficient method |
||
822 | 1 | * for removing all shortcodes. |
|
823 | * |
||
824 | * @return bool |
||
825 | */ |
||
826 | public function remove_all_shortcodes(): bool |
||
832 | 1 | ||
833 | /** |
||
834 | 1 | * Whether a registered shortcode exists named $tag |
|
835 | * |
||
836 | * @param string $tag |
||
837 | * |
||
838 | * @return bool |
||
839 | */ |
||
840 | public function shortcode_exists(string $tag): bool |
||
844 | |||
845 | /** |
||
846 | * Whether the passed content contains the specified shortcode. |
||
847 | * |
||
848 | * @param string $content |
||
849 | * @param string $tag |
||
850 | * |
||
851 | * @return bool |
||
852 | */ |
||
853 | public function has_shortcode(string $content, string $tag): bool |
||
878 | |||
879 | /** |
||
880 | * Search content for shortcodes and filter shortcodes through their hooks. |
||
881 | * |
||
882 | * <p> |
||
883 | * <br /> |
||
884 | * If there are no shortcode tags defined, then the content will be returned |
||
885 | 2 | * without any filtering. This might cause issues when plugins are disabled but |
|
886 | * the shortcode will still show up in the post or content. |
||
887 | 2 | * </p> |
|
888 | 1 | * |
|
889 | * @param string $content <p>Content to search for shortcodes.</p> |
||
890 | * |
||
891 | 2 | * @return string <p>Content with shortcodes filtered out.</p> |
|
892 | */ |
||
893 | 2 | View Code Duplication | public function do_shortcode(string $content): string |
910 | |||
911 | /** |
||
912 | * Retrieve the shortcode regular expression for searching. |
||
913 | * |
||
914 | * <p> |
||
915 | * <br /> |
||
916 | * The regular expression combines the shortcode tags in the regular expression |
||
917 | * in a regex class. |
||
918 | * <br /><br /> |
||
919 | * |
||
920 | * The regular expression contains 6 different sub matches to help with parsing. |
||
921 | * <br /><br /> |
||
922 | * |
||
923 | * 1 - An extra [ to allow for escaping shortcodes with double [[]]<br /> |
||
924 | * 2 - The shortcode name<br /> |
||
925 | 2 | * 3 - The shortcode argument list<br /> |
|
926 | * 4 - The self closing /<br /> |
||
927 | 2 | * 5 - The content of a shortcode when it wraps some content.<br /> |
|
928 | 2 | * 6 - An extra ] to allow for escaping shortcodes with double [[]]<br /> |
|
929 | * </p> |
||
930 | * |
||
931 | * @return string The shortcode search regular expression |
||
932 | */ |
||
933 | public function get_shortcode_regex(): string |
||
970 | |||
971 | /** |
||
972 | 2 | * Regular Expression callable for do_shortcode() for calling shortcode hook. |
|
973 | * |
||
974 | * @see self::get_shortcode_regex for details of the match array contents. |
||
975 | 2 | * |
|
976 | * @param array $m <p>regular expression match array</p> |
||
977 | * |
||
978 | * @return mixed <p><strong>false</strong> on failure</p> |
||
979 | 2 | */ |
|
980 | 2 | private function _do_shortcode_tag(array $m) |
|
998 | |||
999 | /** |
||
1000 | * Retrieve all attributes from the shortcodes tag. |
||
1001 | * |
||
1002 | * <p> |
||
1003 | * <br /> |
||
1004 | * The attributes list has the attribute name as the key and the value of the |
||
1005 | 2 | * attribute as the value in the key/value pair. This allows for easier |
|
1006 | * retrieval of the attributes, since all attributes have to be known. |
||
1007 | 2 | * </p> |
|
1008 | 2 | * |
|
1009 | 2 | * @param string $text |
|
1010 | 2 | * |
|
1011 | 2 | * @return array <p>List of attributes and their value.</p> |
|
1012 | 2 | */ |
|
1013 | 1 | public function shortcode_parse_atts(string $text): array |
|
1039 | |||
1040 | /** |
||
1041 | * Combine user attributes with known attributes and fill in defaults when needed. |
||
1042 | * |
||
1043 | * <p> |
||
1044 | * <br /> |
||
1045 | * The pairs should be considered to be all of the attributes which are |
||
1046 | * supported by the caller and given as a list. The returned attributes will |
||
1047 | * only contain the attributes in the $pairs list. |
||
1048 | * |
||
1049 | * <br /><br /> |
||
1050 | * If the $atts list has unsupported attributes, then they will be ignored and |
||
1051 | 2 | * removed from the final returned list. |
|
1052 | * </p> |
||
1053 | 2 | * |
|
1054 | 2 | * @param array $pairs <p>Entire list of supported attributes and their defaults.</p> |
|
1055 | 2 | * @param array $atts <p>User defined attributes in shortcode tag.</p> |
|
1056 | 2 | * @param string $shortcode <p>[optional] The name of the shortcode, provided for context to enable filtering.</p> |
|
1057 | 1 | * |
|
1058 | 1 | * @return array <p>Combined and filtered attribute list.</p> |
|
1059 | 2 | */ |
|
1060 | public function shortcode_atts($pairs, $atts, $shortcode = ''): array |
||
1096 | |||
1097 | 1 | /** |
|
1098 | * Remove all shortcode tags from the given content. |
||
1099 | * |
||
1100 | * @param string $content <p>Content to remove shortcode tags.</p> |
||
1101 | 1 | * |
|
1102 | * @return string <p>Content without shortcode tags.</p> |
||
1103 | 1 | */ |
|
1104 | 1 | View Code Duplication | public function strip_shortcodes(string $content): string |
1122 | |||
1123 | 1 | /** |
|
1124 | * Strip shortcode by tag. |
||
1125 | * |
||
1126 | * @param array $m |
||
1127 | 1 | * |
|
1128 | * @return string |
||
1129 | */ |
||
1130 | private function _strip_shortcode_tag(array $m): string |
||
1139 | |||
1140 | } |
||
1141 |
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.