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 Jetpack_Admin_Page 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 Jetpack_Admin_Page, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
4 | abstract class Jetpack_Admin_Page { |
||
5 | // Add page specific actions given the page hook |
||
6 | abstract function add_page_actions( $hook ); |
||
7 | |||
8 | // Create a menu item for the page and returns the hook |
||
9 | abstract function get_page_hook(); |
||
10 | |||
11 | // Enqueue and localize page specific scripts |
||
12 | abstract function page_admin_scripts(); |
||
13 | |||
14 | // Render page specific HTML |
||
15 | abstract function page_render(); |
||
16 | |||
17 | /** |
||
18 | * Should we block the page rendering because the site is in IDC? |
||
19 | * |
||
20 | * @var bool |
||
21 | */ |
||
22 | static $block_page_rendering_for_idc; |
||
23 | |||
24 | /** |
||
25 | * Function called after admin_styles to load any additional needed styles. |
||
26 | * |
||
27 | * @since 4.3.0 |
||
28 | */ |
||
29 | function additional_styles() {} |
||
30 | |||
31 | function __construct() { |
||
32 | $this->jetpack = Jetpack::init(); |
||
|
|||
33 | self::$block_page_rendering_for_idc = ( |
||
34 | Jetpack::validate_sync_error_idc_option() && ! Jetpack_Options::get_option( 'safe_mode_confirmed' ) |
||
35 | ); |
||
36 | } |
||
37 | |||
38 | function add_actions() { |
||
39 | global $pagenow; |
||
40 | |||
41 | // If user is not an admin and site is in Dev Mode, don't do anything |
||
42 | if ( ! current_user_can( 'manage_options' ) && Jetpack::is_development_mode() ) { |
||
43 | return; |
||
44 | } |
||
45 | |||
46 | // Don't add in the modules page unless modules are available! |
||
47 | if ( $this->dont_show_if_not_active && ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) { |
||
48 | return; |
||
49 | } |
||
50 | |||
51 | // Initialize menu item for the page in the admin |
||
52 | $hook = $this->get_page_hook(); |
||
53 | |||
54 | // Attach hooks common to all Jetpack admin pages based on the created |
||
55 | // hook |
||
56 | add_action( "load-$hook", array( $this, 'admin_help' ) ); |
||
57 | add_action( "load-$hook", array( $this, 'admin_page_load' ) ); |
||
58 | add_action( "admin_print_styles-$hook", array( $this, 'admin_styles' ) ); |
||
59 | add_action( "admin_print_scripts-$hook", array( $this, 'admin_scripts' ) ); |
||
60 | |||
61 | if ( ! self::$block_page_rendering_for_idc ) { |
||
62 | add_action( "admin_print_styles-$hook", array( $this, 'additional_styles' ) ); |
||
63 | } |
||
64 | // If someone just activated Jetpack, let's show them a fullscreen connection banner. |
||
65 | if ( |
||
66 | ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] ) |
||
67 | && ! Jetpack::is_active() |
||
68 | && current_user_can( 'jetpack_connect' ) |
||
69 | && ! Jetpack::is_development_mode() |
||
70 | ) { |
||
71 | add_action( 'admin_enqueue_scripts', array( 'Jetpack_Connection_Banner', 'enqueue_banner_scripts' ) ); |
||
72 | add_action( 'admin_print_styles', array( Jetpack::init(), 'admin_banner_styles' ) ); |
||
73 | add_action( 'admin_notices', array( 'Jetpack_Connection_Banner', 'render_connect_prompt_full_screen' ) ); |
||
74 | delete_transient( 'activated_jetpack' ); |
||
75 | } |
||
76 | |||
77 | // Check if the site plan changed and deactivate modules accordingly. |
||
78 | add_action( 'current_screen', array( $this, 'check_plan_deactivate_modules' ) ); |
||
79 | |||
80 | // Attach page specific actions in addition to the above |
||
81 | $this->add_page_actions( $hook ); |
||
82 | } |
||
83 | |||
84 | // Render the page with a common top and bottom part, and page specific content |
||
85 | function render() { |
||
86 | // We're in an IDC: we need a decision made before we show the UI again. |
||
87 | if ( self::$block_page_rendering_for_idc ) { |
||
88 | return; |
||
89 | } |
||
90 | |||
91 | // Check if we are looking at the main dashboard |
||
92 | if ( isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] ) { |
||
93 | $this->page_render(); |
||
94 | return; |
||
95 | } |
||
96 | self::wrap_ui( array( $this, 'page_render' ) ); |
||
97 | } |
||
98 | |||
99 | function admin_help() { |
||
100 | $this->jetpack->admin_help(); |
||
101 | } |
||
102 | |||
103 | function admin_page_load() { |
||
104 | // This is big. For the moment, just call the existing one. |
||
105 | $this->jetpack->admin_page_load(); |
||
106 | } |
||
107 | |||
108 | // Add page specific scripts and jetpack stats for all menu pages |
||
109 | function admin_scripts() { |
||
110 | $this->page_admin_scripts(); // Delegate to inheriting class |
||
111 | add_action( 'admin_footer', array( $this->jetpack, 'do_stats' ) ); |
||
112 | } |
||
113 | |||
114 | // Enqueue the Jetpack admin stylesheet |
||
115 | function admin_styles() { |
||
116 | $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; |
||
117 | |||
118 | wp_enqueue_style( 'jetpack-admin', plugins_url( "css/jetpack-admin{$min}.css", JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION . '-20121016' ); |
||
119 | wp_style_add_data( 'jetpack-admin', 'rtl', 'replace' ); |
||
120 | wp_style_add_data( 'jetpack-admin', 'suffix', $min ); |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Checks if REST API is enabled. |
||
125 | * |
||
126 | * @since 4.4.2 |
||
127 | * |
||
128 | * @return bool |
||
129 | */ |
||
130 | function is_rest_api_enabled() { |
||
131 | return /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ |
||
132 | apply_filters( 'rest_enabled', true ) && |
||
133 | /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ |
||
134 | apply_filters( 'rest_jsonp_enabled', true ) && |
||
135 | /** This filter is documented in wp-includes/rest-api/class-wp-rest-server.php */ |
||
136 | apply_filters( 'rest_authentication_errors', true ); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Checks the site plan and deactivates modules that were active but are no longer included in the plan. |
||
141 | * |
||
142 | * @since 4.4.0 |
||
143 | * |
||
144 | * @param $page |
||
145 | * |
||
146 | * @return array |
||
147 | */ |
||
148 | function check_plan_deactivate_modules( $page ) { |
||
202 | |||
203 | static function load_wrapper_styles() { |
||
204 | $rtl = is_rtl() ? '.rtl' : ''; |
||
205 | wp_enqueue_style( 'dops-css', plugins_url( "_inc/build/admin.dops-style{$rtl}.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION ); |
||
206 | wp_enqueue_style( 'components-css', plugins_url( "_inc/build/style.min{$rtl}.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION ); |
||
207 | $custom_css = ' |
||
208 | #wpcontent { |
||
209 | padding-left: 0 !important; |
||
210 | } |
||
211 | #wpbody-content { |
||
243 | |||
244 | public static function wrap_ui( $callback, $args = array() ) { |
||
353 | } |
||
354 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: