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_Emails 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_Emails, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class WC_Emails { |
||
19 | |||
20 | /** @var array Array of email notification classes */ |
||
21 | public $emails; |
||
22 | |||
23 | /** @var WC_Emails The single instance of the class */ |
||
24 | protected static $_instance = null; |
||
25 | |||
26 | /** |
||
27 | * Main WC_Emails Instance. |
||
28 | * |
||
29 | * Ensures only one instance of WC_Emails is loaded or can be loaded. |
||
30 | * |
||
31 | * @since 2.1 |
||
32 | * @static |
||
33 | * @return WC_Emails Main instance |
||
34 | */ |
||
35 | public static function instance() { |
||
36 | if ( is_null( self::$_instance ) ) { |
||
37 | self::$_instance = new self(); |
||
38 | } |
||
39 | return self::$_instance; |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Cloning is forbidden. |
||
44 | * |
||
45 | * @since 2.1 |
||
46 | */ |
||
47 | public function __clone() { |
||
50 | |||
51 | /** |
||
52 | * Unserializing instances of this class is forbidden. |
||
53 | * |
||
54 | * @since 2.1 |
||
55 | */ |
||
56 | public function __wakeup() { |
||
59 | |||
60 | /** |
||
61 | * Hook in all transactional emails. |
||
62 | */ |
||
63 | public static function init_transactional_emails() { |
||
64 | $email_actions = apply_filters( 'woocommerce_email_actions', array( |
||
65 | 'woocommerce_low_stock', |
||
66 | 'woocommerce_no_stock', |
||
67 | 'woocommerce_product_on_backorder', |
||
68 | 'woocommerce_order_status_pending_to_processing', |
||
69 | 'woocommerce_order_status_pending_to_completed', |
||
70 | 'woocommerce_order_status_pending_to_cancelled', |
||
71 | 'woocommerce_order_status_pending_to_failed', |
||
72 | 'woocommerce_order_status_pending_to_on-hold', |
||
73 | 'woocommerce_order_status_failed_to_processing', |
||
74 | 'woocommerce_order_status_failed_to_completed', |
||
75 | 'woocommerce_order_status_failed_to_on-hold', |
||
76 | 'woocommerce_order_status_on-hold_to_processing', |
||
77 | 'woocommerce_order_status_on-hold_to_cancelled', |
||
78 | 'woocommerce_order_status_on-hold_to_failed', |
||
79 | 'woocommerce_order_status_completed', |
||
80 | 'woocommerce_order_fully_refunded', |
||
81 | 'woocommerce_order_partially_refunded', |
||
82 | 'woocommerce_new_customer_note', |
||
83 | 'woocommerce_created_customer' |
||
84 | ) ); |
||
85 | |||
86 | foreach ( $email_actions as $action ) { |
||
87 | add_action( $action, array( __CLASS__, 'send_transactional_email' ), 10, 10 ); |
||
88 | } |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Init the mailer instance and call the notifications for the current filter. |
||
93 | * @internal param array $args (default: array()) |
||
94 | */ |
||
95 | public static function send_transactional_email() { |
||
100 | |||
101 | /** |
||
102 | * Constructor for the email class hooks in all emails that can be sent. |
||
103 | * |
||
104 | */ |
||
105 | public function __construct() { |
||
106 | $this->init(); |
||
107 | |||
108 | // Email Header, Footer and content hooks |
||
109 | add_action( 'woocommerce_email_header', array( $this, 'email_header' ) ); |
||
110 | add_action( 'woocommerce_email_footer', array( $this, 'email_footer' ) ); |
||
111 | add_action( 'woocommerce_email_order_details', array( $this, 'order_details' ), 10, 4 ); |
||
112 | add_action( 'woocommerce_email_order_details', array( $this, 'order_schema_markup' ), 20, 4 ); |
||
113 | add_action( 'woocommerce_email_order_meta', array( $this, 'order_meta' ), 10, 3 ); |
||
114 | add_action( 'woocommerce_email_customer_details', array( $this, 'customer_details' ), 10, 3 ); |
||
115 | add_action( 'woocommerce_email_customer_details', array( $this, 'email_addresses' ), 20, 3 ); |
||
116 | |||
117 | // Hooks for sending emails during store events |
||
118 | add_action( 'woocommerce_low_stock_notification', array( $this, 'low_stock' ) ); |
||
119 | add_action( 'woocommerce_no_stock_notification', array( $this, 'no_stock' ) ); |
||
120 | add_action( 'woocommerce_product_on_backorder_notification', array( $this, 'backorder' ) ); |
||
121 | add_action( 'woocommerce_created_customer_notification', array( $this, 'customer_new_account' ), 10, 3 ); |
||
122 | |||
123 | // Let 3rd parties unhook the above via this hook |
||
124 | do_action( 'woocommerce_email', $this ); |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Init email classes. |
||
129 | */ |
||
130 | public function init() { |
||
153 | |||
154 | /** |
||
155 | * Return the email classes - used in admin to load settings. |
||
156 | * |
||
157 | * @return array |
||
158 | */ |
||
159 | public function get_emails() { |
||
162 | |||
163 | /** |
||
164 | * Get from name for email. |
||
165 | * |
||
166 | * @return string |
||
167 | */ |
||
168 | public function get_from_name() { |
||
171 | |||
172 | /** |
||
173 | * Get from email address. |
||
174 | * |
||
175 | * @return string |
||
176 | */ |
||
177 | public function get_from_address() { |
||
180 | |||
181 | /** |
||
182 | * Get the email header. |
||
183 | * |
||
184 | * @param mixed $email_heading heading for the email |
||
185 | */ |
||
186 | public function email_header( $email_heading ) { |
||
189 | |||
190 | /** |
||
191 | * Get the email footer. |
||
192 | */ |
||
193 | public function email_footer() { |
||
196 | |||
197 | /** |
||
198 | * Wraps a message in the woocommerce mail template. |
||
199 | * |
||
200 | * @param mixed $email_heading |
||
201 | * @param string $message |
||
202 | * @return string |
||
203 | */ |
||
204 | public function wrap_message( $email_heading, $message, $plain_text = false ) { |
||
219 | |||
220 | /** |
||
221 | * Send the email. |
||
222 | * |
||
223 | * @param mixed $to |
||
224 | * @param mixed $subject |
||
225 | * @param mixed $message |
||
226 | * @param string $headers (default: "Content-Type: text/html\r\n") |
||
227 | * @param string $attachments (default: "") |
||
228 | * @return bool |
||
229 | */ |
||
230 | public function send( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = "" ) { |
||
235 | |||
236 | /** |
||
237 | * Prepare and send the customer invoice email on demand. |
||
238 | */ |
||
239 | public function customer_invoice( $order ) { |
||
243 | |||
244 | /** |
||
245 | * Customer new account welcome email. |
||
246 | * |
||
247 | * @param int $customer_id |
||
248 | * @param array $new_customer_data |
||
249 | */ |
||
250 | public function customer_new_account( $customer_id, $new_customer_data = array(), $password_generated = false ) { |
||
260 | |||
261 | /** |
||
262 | * Show the order details table |
||
263 | */ |
||
264 | public function order_details( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) { |
||
271 | |||
272 | /** |
||
273 | * Adds Schema.org markup for order in JSON-LD format. |
||
274 | * |
||
275 | * @since 2.6.0 |
||
276 | * @param mixed $order |
||
277 | * @param bool $sent_to_admin (default: false) |
||
278 | * @param bool $plain_text (default: false) |
||
279 | */ |
||
280 | public function order_schema_markup( $order, $sent_to_admin = false, $plain_text = false ) { |
||
378 | |||
379 | /** |
||
380 | * Add order meta to email templates. |
||
381 | * |
||
382 | * @param mixed $order |
||
383 | * @param bool $sent_to_admin (default: false) |
||
384 | * @param bool $plain_text (default: false) |
||
385 | * @return string |
||
386 | */ |
||
387 | public function order_meta( $order, $sent_to_admin = false, $plain_text = false ) { |
||
430 | |||
431 | /** |
||
432 | * Is customer detail field valid? |
||
433 | * @param array $field |
||
434 | * @return boolean |
||
435 | */ |
||
436 | public function customer_detail_field_is_valid( $field ) { |
||
439 | |||
440 | /** |
||
441 | * Add customer details to email templates. |
||
442 | * |
||
443 | * @param mixed $order |
||
444 | * @param bool $sent_to_admin (default: false) |
||
445 | * @param bool $plain_text (default: false) |
||
446 | * @return string |
||
447 | */ |
||
448 | public function customer_details( $order, $sent_to_admin = false, $plain_text = false ) { |
||
480 | |||
481 | /** |
||
482 | * Get the email addresses. |
||
483 | */ |
||
484 | public function email_addresses( $order, $sent_to_admin = false, $plain_text = false ) { |
||
491 | |||
492 | /** |
||
493 | * Get blog name formatted for emails. |
||
494 | * @return string |
||
495 | */ |
||
496 | private function get_blogname() { |
||
499 | |||
500 | /** |
||
501 | * Low stock notification email. |
||
502 | * |
||
503 | * @param WC_Product $product |
||
504 | */ |
||
505 | public function low_stock( $product ) { |
||
517 | |||
518 | /** |
||
519 | * No stock notification email. |
||
520 | * |
||
521 | * @param WC_Product $product |
||
522 | */ |
||
523 | public function no_stock( $product ) { |
||
535 | |||
536 | /** |
||
537 | * Backorder notification email. |
||
538 | * |
||
539 | * @param array $args |
||
540 | */ |
||
541 | public function backorder( $args ) { |
||
565 | } |
||
566 |
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.