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 EE_Admin 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 EE_Admin, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
20 | final class EE_Admin implements InterminableInterface |
||
21 | { |
||
22 | |||
23 | /** |
||
24 | * @var EE_Admin $_instance |
||
25 | */ |
||
26 | private static $_instance; |
||
27 | |||
28 | /** |
||
29 | * @var PersistentAdminNoticeManager $persistent_admin_notice_manager |
||
30 | */ |
||
31 | private $persistent_admin_notice_manager; |
||
32 | |||
33 | /** |
||
34 | * @var LoaderInterface |
||
35 | */ |
||
36 | protected $loader; |
||
37 | |||
38 | /** |
||
39 | * @singleton method used to instantiate class object |
||
40 | * @return EE_Admin |
||
41 | * @throws EE_Error |
||
42 | */ |
||
43 | public static function instance() |
||
44 | { |
||
45 | // check if class object is instantiated |
||
46 | if (! self::$_instance instanceof EE_Admin) { |
||
47 | self::$_instance = new self(); |
||
48 | } |
||
49 | return self::$_instance; |
||
50 | } |
||
51 | |||
52 | |||
53 | /** |
||
54 | * @return EE_Admin |
||
55 | * @throws EE_Error |
||
56 | */ |
||
57 | public static function reset() |
||
58 | { |
||
59 | self::$_instance = null; |
||
60 | return self::instance(); |
||
61 | } |
||
62 | |||
63 | |||
64 | /** |
||
65 | * class constructor |
||
66 | * |
||
67 | * @throws EE_Error |
||
68 | * @throws InvalidDataTypeException |
||
69 | * @throws InvalidInterfaceException |
||
70 | * @throws InvalidArgumentException |
||
71 | */ |
||
72 | protected function __construct() |
||
73 | { |
||
74 | // define global EE_Admin constants |
||
75 | $this->_define_all_constants(); |
||
76 | // set autoloaders for our admin page classes based on included path information |
||
77 | EEH_Autoloader::instance()->register_autoloaders_for_each_file_in_folder(EE_ADMIN); |
||
78 | // admin hooks |
||
79 | add_filter('plugin_action_links', array($this, 'filter_plugin_actions'), 10, 2); |
||
80 | // load EE_Request_Handler early |
||
81 | add_action('AHEE__EE_System__core_loaded_and_ready', array($this, 'get_request')); |
||
82 | add_action('AHEE__EE_System__initialize_last', array($this, 'init')); |
||
83 | add_action('AHEE__EE_Admin_Page__route_admin_request', array($this, 'route_admin_request'), 100, 2); |
||
84 | add_action('wp_loaded', array($this, 'wp_loaded'), 100); |
||
85 | add_action('admin_init', array($this, 'admin_init'), 100); |
||
86 | add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'), 20); |
||
87 | add_action('admin_notices', array($this, 'display_admin_notices'), 10); |
||
88 | add_action('network_admin_notices', array($this, 'display_admin_notices'), 10); |
||
89 | add_filter('pre_update_option', array($this, 'check_for_invalid_datetime_formats'), 100, 2); |
||
90 | add_filter('admin_footer_text', array($this, 'espresso_admin_footer')); |
||
91 | add_action('load-plugins.php', array($this, 'hookIntoWpPluginsPage')); |
||
92 | add_action('display_post_states', array($this, 'displayStateForCriticalPages'), 10, 2); |
||
93 | // reset Environment config (we only do this on admin page loads); |
||
94 | EE_Registry::instance()->CFG->environment->recheck_values(); |
||
95 | do_action('AHEE__EE_Admin__loaded'); |
||
96 | } |
||
97 | |||
98 | |||
99 | /** |
||
100 | * _define_all_constants |
||
101 | * define constants that are set globally for all admin pages |
||
102 | * |
||
103 | * @return void |
||
104 | */ |
||
105 | View Code Duplication | private function _define_all_constants() |
|
106 | { |
||
107 | if (! defined('EE_ADMIN_URL')) { |
||
108 | define('EE_ADMIN_URL', EE_PLUGIN_DIR_URL . 'core/admin/'); |
||
109 | define('EE_ADMIN_PAGES_URL', EE_PLUGIN_DIR_URL . 'admin_pages/'); |
||
110 | define('EE_ADMIN_TEMPLATE', EE_ADMIN . 'templates' . DS); |
||
111 | define('WP_ADMIN_PATH', ABSPATH . 'wp-admin/'); |
||
112 | define('WP_AJAX_URL', admin_url('admin-ajax.php')); |
||
113 | } |
||
114 | } |
||
115 | |||
116 | |||
117 | /** |
||
118 | * filter_plugin_actions - adds links to the Plugins page listing |
||
119 | * |
||
120 | * @param array $links |
||
121 | * @param string $plugin |
||
122 | * @return array |
||
123 | */ |
||
124 | public function filter_plugin_actions($links, $plugin) |
||
125 | { |
||
126 | // set $main_file in stone |
||
127 | static $main_file; |
||
128 | // if $main_file is not set yet |
||
129 | if (! $main_file) { |
||
130 | $main_file = plugin_basename(EVENT_ESPRESSO_MAIN_FILE); |
||
131 | } |
||
132 | if ($plugin === $main_file) { |
||
133 | // compare current plugin to this one |
||
134 | if (EE_Maintenance_Mode::instance()->level() === EE_Maintenance_Mode::level_2_complete_maintenance) { |
||
135 | $maintenance_link = '<a href="admin.php?page=espresso_maintenance_settings"' |
||
136 | . ' title="Event Espresso is in maintenance mode. Click this link to learn why.">' |
||
137 | . esc_html__('Maintenance Mode Active', 'event_espresso') |
||
138 | . '</a>'; |
||
139 | array_unshift($links, $maintenance_link); |
||
140 | } else { |
||
141 | $org_settings_link = '<a href="admin.php?page=espresso_general_settings">' |
||
142 | . esc_html__('Settings', 'event_espresso') |
||
143 | . '</a>'; |
||
144 | $events_link = '<a href="admin.php?page=espresso_events">' |
||
145 | . esc_html__('Events', 'event_espresso') |
||
146 | . '</a>'; |
||
147 | // add before other links |
||
148 | array_unshift($links, $org_settings_link, $events_link); |
||
149 | } |
||
150 | } |
||
151 | return $links; |
||
152 | } |
||
153 | |||
154 | |||
155 | /** |
||
156 | * _get_request |
||
157 | * |
||
158 | * @return void |
||
159 | * @throws EE_Error |
||
160 | * @throws InvalidArgumentException |
||
161 | * @throws InvalidDataTypeException |
||
162 | * @throws InvalidInterfaceException |
||
163 | * @throws ReflectionException |
||
164 | */ |
||
165 | public function get_request() |
||
166 | { |
||
167 | EE_Registry::instance()->load_core('Request_Handler'); |
||
168 | } |
||
169 | |||
170 | |||
171 | /** |
||
172 | * hide_admin_pages_except_maintenance_mode |
||
173 | * |
||
174 | * @param array $admin_page_folder_names |
||
175 | * @return array |
||
176 | */ |
||
177 | public function hide_admin_pages_except_maintenance_mode($admin_page_folder_names = array()) |
||
185 | |||
186 | |||
187 | /** |
||
188 | * init- should fire after shortcode, module, addon, other plugin (default priority), and even |
||
189 | * EE_Front_Controller's init phases have run |
||
190 | * |
||
191 | * @return void |
||
192 | * @throws EE_Error |
||
193 | * @throws InvalidArgumentException |
||
194 | * @throws InvalidDataTypeException |
||
195 | * @throws InvalidInterfaceException |
||
196 | * @throws ReflectionException |
||
197 | * @throws ServiceNotFoundException |
||
198 | */ |
||
199 | public function init() |
||
221 | |||
222 | |||
223 | /** |
||
224 | * Gets the loader (and if it wasn't previously set, sets it) |
||
225 | * @return LoaderInterface |
||
226 | * @throws InvalidArgumentException |
||
227 | * @throws InvalidDataTypeException |
||
228 | * @throws InvalidInterfaceException |
||
229 | */ |
||
230 | protected function getLoader() |
||
237 | |||
238 | |||
239 | /** |
||
240 | * Method that's fired on admin requests (including admin ajax) but only when the models are usable |
||
241 | * (ie, the site isn't in maintenance mode) |
||
242 | * @since 4.9.63.p |
||
243 | * @return void |
||
244 | */ |
||
245 | protected function initModelsReady() |
||
266 | |||
267 | |||
268 | /** |
||
269 | * get_persistent_admin_notices |
||
270 | * |
||
271 | * @access public |
||
272 | * @return void |
||
273 | * @throws EE_Error |
||
274 | * @throws InvalidArgumentException |
||
275 | * @throws InvalidDataTypeException |
||
276 | * @throws InvalidInterfaceException |
||
277 | */ |
||
278 | public function maybeSetDatetimeWarningNotice() |
||
313 | |||
314 | |||
315 | /** |
||
316 | * this simply hooks into the nav menu setup of pages metabox and makes sure that we remove EE critical pages from |
||
317 | * the list of options. the wp function "wp_nav_menu_item_post_type_meta_box" found in |
||
318 | * wp-admin/includes/nav-menu.php looks for the "_default_query" property on the post_type object and it uses that |
||
319 | * to override any queries found in the existing query for the given post type. Note that _default_query is not a |
||
320 | * normal property on the post_type object. It's found ONLY in this particular context. |
||
321 | * |
||
322 | * @param WP_Post $post_type WP post type object |
||
323 | * @return WP_Post |
||
324 | * @throws InvalidArgumentException |
||
325 | * @throws InvalidDataTypeException |
||
326 | * @throws InvalidInterfaceException |
||
327 | */ |
||
328 | public function remove_pages_from_nav_menu($post_type) |
||
340 | |||
341 | |||
342 | /** |
||
343 | * WP by default only shows three metaboxes in "nav-menus.php" for first times users. We want to make sure our |
||
344 | * metaboxes get shown as well |
||
345 | * |
||
346 | * @return void |
||
347 | */ |
||
348 | public function enable_hidden_ee_nav_menu_metaboxes() |
||
387 | |||
388 | |||
389 | /** |
||
390 | * This method simply registers custom nav menu boxes for "nav_menus.php route" |
||
391 | * Currently EE is using this to make sure there are menu options for our CPT archive page routes. |
||
392 | * |
||
393 | * @todo modify this so its more dynamic and automatic for all ee CPTs and setups and can also be hooked into by |
||
394 | * addons etc. |
||
395 | * @return void |
||
396 | */ |
||
397 | public function register_custom_nav_menu_boxes() |
||
408 | |||
409 | |||
410 | /** |
||
411 | * Use this to edit the post link for our cpts so that the edit link points to the correct page. |
||
412 | * |
||
413 | * @since 4.3.0 |
||
414 | * @param string $link the original link generated by wp |
||
415 | * @param int $id post id |
||
416 | * @return string the (maybe) modified link |
||
417 | */ |
||
418 | public function modify_edit_post_link($link, $id) |
||
435 | |||
436 | |||
437 | public function ee_cpt_archive_pages() |
||
517 | |||
518 | |||
519 | /** |
||
520 | * Returns an array of event archive nav items. |
||
521 | * |
||
522 | * @todo for now this method is just in place so when it gets abstracted further we can substitute in whatever |
||
523 | * method we use for getting the extra nav menu items |
||
524 | * @return array |
||
525 | */ |
||
526 | private function _get_extra_nav_menu_pages_items() |
||
535 | |||
536 | |||
537 | /** |
||
538 | * Setup nav menu walker item for usage in the event archive nav menu metabox. It receives a menu_item array with |
||
539 | * the properties and converts it to the menu item object. |
||
540 | * |
||
541 | * @see wp_setup_nav_menu_item() in wp-includes/nav-menu.php |
||
542 | * @param $menu_item_values |
||
543 | * @return stdClass |
||
544 | */ |
||
545 | private function _setup_extra_nav_menu_pages_items($menu_item_values) |
||
571 | |||
572 | |||
573 | /** |
||
574 | * This is the action hook for the AHEE__EE_Admin_Page__route_admin_request hook that fires off right before an |
||
575 | * EE_Admin_Page route is called. |
||
576 | * |
||
577 | * @return void |
||
578 | */ |
||
579 | public function route_admin_request() |
||
582 | |||
583 | |||
584 | /** |
||
585 | * wp_loaded should fire on the WordPress wp_loaded hook. This fires on a VERY late priority. |
||
586 | * |
||
587 | * @return void |
||
588 | */ |
||
589 | public function wp_loaded() |
||
592 | |||
593 | |||
594 | /** |
||
595 | * admin_init |
||
596 | * |
||
597 | * @return void |
||
598 | * @throws EE_Error |
||
599 | * @throws InvalidArgumentException |
||
600 | * @throws InvalidDataTypeException |
||
601 | * @throws InvalidInterfaceException |
||
602 | * @throws ReflectionException |
||
603 | */ |
||
604 | public function admin_init() |
||
633 | |||
634 | |||
635 | /** |
||
636 | * Runs on admin_init but only if models are usable (ie, we're not in maintenanc emode) |
||
637 | */ |
||
638 | protected function adminInitModelsReady() |
||
644 | |||
645 | |||
646 | /** |
||
647 | * Callback for wp_dropdown_pages hook to remove ee critical pages from the dropdown selection. |
||
648 | * |
||
649 | * @param string $output Current output. |
||
650 | * @return string |
||
651 | * @throws InvalidArgumentException |
||
652 | * @throws InvalidDataTypeException |
||
653 | * @throws InvalidInterfaceException |
||
654 | */ |
||
655 | public function modify_dropdown_pages($output) |
||
675 | |||
676 | |||
677 | /** |
||
678 | * enqueue all admin scripts that need loaded for admin pages |
||
679 | * |
||
680 | * @return void |
||
681 | */ |
||
682 | public function enqueue_admin_scripts() |
||
733 | |||
734 | |||
735 | /** |
||
736 | * display_admin_notices |
||
737 | * |
||
738 | * @return void |
||
739 | */ |
||
740 | public function display_admin_notices() |
||
744 | |||
745 | |||
746 | /** |
||
747 | * @param array $elements |
||
748 | * @return array |
||
749 | * @throws EE_Error |
||
750 | * @throws InvalidArgumentException |
||
751 | * @throws InvalidDataTypeException |
||
752 | * @throws InvalidInterfaceException |
||
753 | */ |
||
754 | public function dashboard_glance_items($elements) |
||
793 | |||
794 | |||
795 | /** |
||
796 | * check_for_invalid_datetime_formats |
||
797 | * if an admin changes their date or time format settings on the WP General Settings admin page, verify that |
||
798 | * their selected format can be parsed by PHP |
||
799 | * |
||
800 | * @param $value |
||
801 | * @param $option |
||
802 | * @throws EE_Error |
||
803 | * @return string |
||
804 | */ |
||
805 | public function check_for_invalid_datetime_formats($value, $option) |
||
870 | |||
871 | |||
872 | /** |
||
873 | * its_eSpresso - converts the less commonly used spelling of "Expresso" to "Espresso" |
||
874 | * |
||
875 | * @param $content |
||
876 | * @return string |
||
877 | */ |
||
878 | public function its_eSpresso($content) |
||
882 | |||
883 | |||
884 | /** |
||
885 | * espresso_admin_footer |
||
886 | * |
||
887 | * @return string |
||
888 | */ |
||
889 | public function espresso_admin_footer() |
||
893 | |||
894 | |||
895 | /** |
||
896 | * static method for registering ee admin page. |
||
897 | * This method is deprecated in favor of the new location in EE_Register_Admin_Page::register. |
||
898 | * |
||
899 | * @since 4.3.0 |
||
900 | * @deprecated 4.3.0 Use EE_Register_Admin_Page::register() instead |
||
901 | * @see EE_Register_Admin_Page::register() |
||
902 | * @param $page_basename |
||
903 | * @param $page_path |
||
904 | * @param array $config |
||
905 | * @return void |
||
906 | * @throws EE_Error |
||
907 | */ |
||
908 | public static function register_ee_admin_page($page_basename, $page_path, $config = array()) |
||
926 | |||
927 | |||
928 | /** |
||
929 | * @deprecated 4.8.41 |
||
930 | * @param int $post_ID |
||
931 | * @param \WP_Post $post |
||
932 | * @return void |
||
933 | */ |
||
934 | public static function parse_post_content_on_save($post_ID, $post) |
||
942 | |||
943 | |||
944 | /** |
||
945 | * @deprecated 4.8.41 |
||
946 | * @param $option |
||
947 | * @param $old_value |
||
948 | * @param $value |
||
949 | * @return void |
||
950 | */ |
||
951 | public function reset_page_for_posts_on_change($option, $old_value, $value) |
||
959 | |||
960 | |||
961 | /** |
||
962 | * @deprecated 4.9.27 |
||
963 | * @return void |
||
964 | */ |
||
965 | public function get_persistent_admin_notices() |
||
976 | |||
977 | |||
978 | /** |
||
979 | * @deprecated 4.9.27 |
||
980 | * @throws InvalidInterfaceException |
||
981 | * @throws InvalidDataTypeException |
||
982 | * @throws DomainException |
||
983 | */ |
||
984 | public function dismiss_ee_nag_notice_callback() |
||
996 | |||
997 | |||
998 | /** |
||
999 | * Callback on load-plugins.php hook for setting up anything hooking into the wp plugins page. |
||
1000 | * |
||
1001 | * @throws InvalidArgumentException |
||
1002 | * @throws InvalidDataTypeException |
||
1003 | * @throws InvalidInterfaceException |
||
1004 | */ |
||
1005 | public function hookIntoWpPluginsPage() |
||
1012 | |||
1013 | |||
1014 | /** |
||
1015 | * Hooks into the "post states" filter in a wp post type list table. |
||
1016 | * |
||
1017 | * @param array $post_states |
||
1018 | * @param WP_Post $post |
||
1019 | * @return array |
||
1020 | * @throws InvalidArgumentException |
||
1021 | * @throws InvalidDataTypeException |
||
1022 | * @throws InvalidInterfaceException |
||
1023 | */ |
||
1024 | public function displayStateForCriticalPages($post_states, $post) |
||
1041 | } |
||
1042 |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.