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 WC_Tracker 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 WC_Tracker, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class WC_Tracker { |
||
20 | |||
21 | /** |
||
22 | * URL to the WooThemes Tracker API endpoint. |
||
23 | * @var string |
||
24 | */ |
||
25 | private static $api_url = 'https://tracking.woocommerce.com/v1/'; |
||
26 | |||
27 | /** |
||
28 | * Hook into cron event. |
||
29 | */ |
||
30 | public static function init() { |
||
33 | |||
34 | /** |
||
35 | * Decide whether to send tracking data or not. |
||
36 | * |
||
37 | * @param boolean $override |
||
38 | */ |
||
39 | public static function send_tracking_data( $override = false ) { |
||
40 | // Dont trigger this on AJAX Requests |
||
41 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { |
||
42 | return; |
||
43 | } |
||
44 | |||
45 | if ( ! apply_filters( 'woocommerce_tracker_send_override', $override ) ) { |
||
46 | // Send a maximum of once per week by default. |
||
47 | $last_send = self::get_last_send_time(); |
||
48 | if ( $last_send && $last_send > apply_filters( 'woocommerce_tracker_last_send_interval', strtotime( '-1 week' ) ) ) { |
||
49 | return; |
||
50 | } |
||
51 | } else { |
||
52 | // Make sure there is at least a 1 hour delay between override sends, we dont want duplicate calls due to double clicking links. |
||
53 | $last_send = self::get_last_send_time(); |
||
54 | if ( $last_send && $last_send > strtotime( '-1 hours' ) ) { |
||
55 | return; |
||
56 | } |
||
57 | } |
||
58 | |||
59 | // Update time first before sending to ensure it is set |
||
60 | update_option( 'woocommerce_tracker_last_send', time() ); |
||
61 | |||
62 | $params = self::get_tracking_data(); |
||
63 | $response = wp_safe_remote_post( self::$api_url, array( |
||
64 | 'method' => 'POST', |
||
65 | 'timeout' => 45, |
||
66 | 'redirection' => 5, |
||
67 | 'httpversion' => '1.0', |
||
68 | 'blocking' => false, |
||
69 | 'headers' => array( 'user-agent' => 'WooCommerceTracker/' . md5( esc_url( home_url( '/' ) ) ) . ';' ), |
||
70 | 'body' => json_encode( $params ), |
||
71 | 'cookies' => array() |
||
72 | ) |
||
73 | ); |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Get the last time tracking data was sent. |
||
78 | * @return int|bool |
||
79 | */ |
||
80 | private static function get_last_send_time() { |
||
83 | |||
84 | /** |
||
85 | * Get all the tracking data. |
||
86 | * @return array |
||
87 | */ |
||
88 | private static function get_tracking_data() { |
||
89 | $data = array(); |
||
90 | |||
91 | // General site info |
||
92 | $data['url'] = home_url(); |
||
93 | $data['email'] = apply_filters( 'woocommerce_tracker_admin_email', get_option( 'admin_email' ) ); |
||
94 | $data['theme'] = self::get_theme_info(); |
||
95 | |||
96 | // WordPress Info |
||
97 | $data['wp'] = self::get_wordpress_info(); |
||
98 | |||
99 | // Server Info |
||
100 | $data['server'] = self::get_server_info(); |
||
101 | |||
102 | // Plugin info |
||
103 | $all_plugins = self::get_all_plugins(); |
||
104 | $data['active_plugins'] = $all_plugins['active_plugins']; |
||
105 | $data['inactive_plugins'] = $all_plugins['inactive_plugins']; |
||
106 | |||
107 | // Jetpack & WooCommerce Connect |
||
108 | $data['jetpack_version'] = defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : 'none'; |
||
109 | $data['jetpack_connected'] = ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_active' ) && Jetpack::is_active() ) ? 'yes' : 'no'; |
||
110 | $data['jetpack_is_staging'] = ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_staging_site' ) && Jetpack::is_staging_site() ) ? 'yes' : 'no'; |
||
111 | $data['connect_installed'] = class_exists( 'WC_Connect_Loader' ) ? 'yes' : 'no'; |
||
112 | $data['connect_active'] = ( class_exists( 'WC_Connect_Loader' ) && wp_next_scheduled( 'wc_connect_fetch_service_schemas' ) ) ? 'yes' : 'no'; |
||
113 | |||
114 | // Store count info |
||
115 | $data['users'] = self::get_user_counts(); |
||
116 | $data['products'] = self::get_product_counts(); |
||
117 | $data['orders'] = self::get_order_counts(); |
||
118 | |||
119 | // Payment gateway info |
||
120 | $data['gateways'] = self::get_active_payment_gateways(); |
||
121 | |||
122 | // Shipping method info |
||
123 | $data['shipping_methods'] = self::get_active_shipping_methods(); |
||
124 | |||
125 | // Get all WooCommerce options info |
||
126 | $data['settings'] = self::get_all_woocommerce_options_values(); |
||
127 | |||
128 | // Template overrides |
||
129 | $data['template_overrides'] = self::get_all_template_overrides(); |
||
130 | |||
131 | return apply_filters( 'woocommerce_tracker_data', $data ); |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Get the current theme info, theme name and version. |
||
136 | * @return array |
||
137 | */ |
||
138 | public static function get_theme_info() { |
||
155 | |||
156 | /** |
||
157 | * Get WordPress related data. |
||
158 | * @return array |
||
159 | */ |
||
160 | private static function get_wordpress_info() { |
||
178 | |||
179 | /** |
||
180 | * Get server related info. |
||
181 | * @return array |
||
182 | */ |
||
183 | private static function get_server_info() { |
||
212 | |||
213 | /** |
||
214 | * Get all plugins grouped into activated or not. |
||
215 | * @return array |
||
216 | */ |
||
217 | private static function get_all_plugins() { |
||
254 | |||
255 | /** |
||
256 | * Get user totals based on user role. |
||
257 | * @return array |
||
258 | */ |
||
259 | private static function get_user_counts() { |
||
271 | |||
272 | /** |
||
273 | * Get product totals based on product type. |
||
274 | * @return array |
||
275 | */ |
||
276 | private static function get_product_counts() { |
||
288 | |||
289 | /** |
||
290 | * Get order counts based on order status. |
||
291 | * @return array |
||
292 | */ |
||
293 | private static function get_order_counts() { |
||
303 | |||
304 | /** |
||
305 | * Get a list of all active payment gateways. |
||
306 | * @return array |
||
307 | */ |
||
308 | View Code Duplication | private static function get_active_payment_gateways() { |
|
319 | |||
320 | /** |
||
321 | * Get a list of all active shipping methods. |
||
322 | * @return array |
||
323 | */ |
||
324 | View Code Duplication | private static function get_active_shipping_methods() { |
|
335 | |||
336 | /** |
||
337 | * Get all options starting with woocommerce_ prefix. |
||
338 | * @return array |
||
339 | */ |
||
340 | private static function get_all_woocommerce_options_values() { |
||
361 | |||
362 | /** |
||
363 | * Look for any template override and return filenames. |
||
364 | * @return array |
||
365 | */ |
||
366 | private static function get_all_template_overrides() { |
||
398 | } |
||
399 | |||
401 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.