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_Customer 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_Customer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class WC_Customer extends WC_Legacy_Customer { |
||
18 | |||
19 | /** |
||
20 | * Stores customer data. |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $_data = array( |
||
24 | 'id' => 0, |
||
25 | 'email' => '', |
||
26 | 'first_name' => '', |
||
27 | 'last_name' => '', |
||
28 | 'role' => 'customer', |
||
29 | 'last_order_id' => null, // read only |
||
30 | 'last_order_date' => null, // read only |
||
31 | 'orders_count' => 0, // read only |
||
32 | 'total_spent' => 0, // read only |
||
33 | 'username' => '', // read only on existing users |
||
34 | 'password' => '', // write only |
||
35 | 'date_created' => '', // read only |
||
36 | 'date_modified' => '', // read only |
||
37 | 'billing_postcode' => '', |
||
38 | 'billing_city' => '', |
||
39 | 'billing_address_1' => '', |
||
40 | 'billing_address_2' => '', |
||
41 | 'billing_state' => '', |
||
42 | 'billing_country' => '', |
||
43 | 'shipping_postcode' => '', |
||
44 | 'shipping_city' => '', |
||
45 | 'shipping_address_1' => '', |
||
46 | 'shipping_address_2' => '', |
||
47 | 'shipping_state' => '', |
||
48 | 'shipping_country' => '', |
||
49 | 'is_paying_customer' => false, |
||
50 | 'is_vat_exempt' => false, // session only. |
||
51 | 'calculated_shipping' => false, // session only |
||
52 | ); |
||
53 | |||
54 | /** |
||
55 | * Keys which are also stored in a session (so we can make sure they get updated...) |
||
56 | * @var array |
||
57 | */ |
||
58 | protected $_session_keys = array( |
||
59 | 'billing_postcode', 'billing_city', 'billing_address_1', 'billing_address', 'billing_address_2', |
||
60 | 'billing_state', 'billing_country', 'shipping_postcode', 'shipping_city', 'shipping_address_1', 'shipping_address', |
||
61 | 'shipping_address_2', 'shipping_state', 'shipping_country', 'is_vat_exempt', 'calculated_shipping', |
||
62 | ); |
||
63 | |||
64 | /** |
||
65 | * Data stored in meta keys, but not considered "meta" |
||
66 | * @since 2.7.0 |
||
67 | * @var array |
||
68 | */ |
||
69 | protected $_internal_meta_keys = array( |
||
70 | 'billing_postcode', 'billing_city', 'billing_address_1', 'billing_address_2', 'billing_state', |
||
71 | 'billing_country', 'shipping_postcode', 'shipping_city', 'shipping_address_1', |
||
72 | 'shipping_address_2', 'shipping_state', 'shipping_country', 'paying_customer', |
||
73 | 'last_update', 'first_name', 'last_name', |
||
74 | ); |
||
75 | |||
76 | /** |
||
77 | * Internal meta type used to store user data. |
||
78 | * @var string |
||
79 | */ |
||
80 | protected $_meta_type = 'user'; |
||
81 | |||
82 | /** |
||
83 | * Was data changed in the database for this class? |
||
84 | * @var boolean |
||
85 | */ |
||
86 | protected $_changed = false; |
||
87 | |||
88 | /** |
||
89 | * If some of the customer information is loaded by session (instead of just from the DB). |
||
90 | * @var boolean |
||
91 | */ |
||
92 | protected $_from_session = false; |
||
93 | |||
94 | /** |
||
95 | * WC_Customer can also return an object for a logged out user (session). |
||
96 | * $_is_user will be false in this case. It will be true for all other cases |
||
97 | * (logged in users or getting a WC_Customer for another object) |
||
98 | * @var boolean |
||
99 | */ |
||
100 | protected $_is_user = false; |
||
101 | |||
102 | /** |
||
103 | * Load customer data based on how WC_Customer is called. |
||
104 | * @param mixed $customer WC_Customer object or customer ID is accepted. |
||
105 | * if $customer is 'new', you can build a new WC_Customer object. If it's empty, some |
||
106 | * data will be pulled from the session for the current user/customer. |
||
107 | */ |
||
108 | public function __construct( $customer = '' ) { |
||
109 | if ( $customer instanceof WC_Customer ) { |
||
110 | $this->_is_user = true; |
||
111 | $this->read( absint( $customer->get_id() ) ); |
||
112 | } elseif ( is_numeric( $customer ) ) { |
||
113 | $this->_is_user = true; |
||
114 | $this->read( $customer ); |
||
115 | } elseif ( empty( $customer ) ) { |
||
116 | $this->_is_user = true; // unless load_session gets called after. |
||
117 | } |
||
118 | |||
119 | if ( $this->_from_session ) { |
||
120 | add_action( 'shutdown', array( $this, 'save_session_if_changed' ), 10 ); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * Saves customer information to the current session if any data changed. |
||
126 | * @since 2.7.0 |
||
127 | */ |
||
128 | public function save_session_if_changed() { |
||
133 | |||
134 | /** |
||
135 | * Loads a WC session into the customer class. |
||
136 | */ |
||
137 | public function load_session() { |
||
147 | |||
148 | /* |
||
149 | |-------------------------------------------------------------------------- |
||
150 | | Getters |
||
151 | |-------------------------------------------------------------------------- |
||
152 | | Methods for getting data from the customer object. |
||
153 | */ |
||
154 | |||
155 | /** |
||
156 | * Return a customer's user ID. If the current customer is logged out, this will be a session key. |
||
157 | * @since 2.7.0 |
||
158 | * @return mixed |
||
159 | */ |
||
160 | public function get_id() { |
||
163 | |||
164 | /** |
||
165 | * Return the customer's username. |
||
166 | * @since 2.7.0 |
||
167 | * @return string |
||
168 | */ |
||
169 | public function get_username() { |
||
172 | |||
173 | /** |
||
174 | * Return the customer's email. |
||
175 | * @since 2.7.0 |
||
176 | * @return string |
||
177 | */ |
||
178 | public function get_email() { |
||
181 | |||
182 | /** |
||
183 | * Return customer's first name. |
||
184 | * @since 2.7.0 |
||
185 | * @return string |
||
186 | */ |
||
187 | public function get_first_name() { |
||
190 | |||
191 | /** |
||
192 | * Return customer's last name. |
||
193 | * @since 2.7.0 |
||
194 | * @return string |
||
195 | */ |
||
196 | public function get_last_name() { |
||
199 | |||
200 | /** |
||
201 | * Return customer's user role. |
||
202 | * @since 2.7.0 |
||
203 | * @return string |
||
204 | */ |
||
205 | public function get_role() { |
||
208 | |||
209 | /** |
||
210 | * Return customer's last order ID. |
||
211 | * @since 2.7.0 |
||
212 | * @return integer|null |
||
213 | */ |
||
214 | public function get_last_order_id() { |
||
217 | |||
218 | /** |
||
219 | * Return the date of the customer's last order. |
||
220 | * @since 2.7.0 |
||
221 | * @return integer|null |
||
222 | */ |
||
223 | public function get_last_order_date() { |
||
226 | |||
227 | /** |
||
228 | * Return the number of orders this customer has. |
||
229 | * @since 2.7.0 |
||
230 | * @return integer |
||
231 | */ |
||
232 | public function get_orders_count() { |
||
235 | |||
236 | /** |
||
237 | * Return how much money this customer has spent. |
||
238 | * @since 2.7.0 |
||
239 | * @return float |
||
240 | */ |
||
241 | public function get_total_spent() { |
||
244 | |||
245 | /** |
||
246 | * Return this customer's avatar. |
||
247 | * @since 2.7.0 |
||
248 | * @return string |
||
249 | */ |
||
250 | View Code Duplication | public function get_avatar_url() { |
|
262 | |||
263 | /** |
||
264 | * Return the date this customer was created. |
||
265 | * @since 2.7.0 |
||
266 | * @return integer |
||
267 | */ |
||
268 | public function get_date_created() { |
||
271 | |||
272 | /** |
||
273 | * Return the date this customer was last updated. |
||
274 | * @since 2.7.0 |
||
275 | * @return integer |
||
276 | */ |
||
277 | public function get_date_modified() { |
||
280 | |||
281 | /** |
||
282 | * Gets customer postcode. |
||
283 | * @return string |
||
284 | */ |
||
285 | public function get_billing_postcode() { |
||
288 | |||
289 | /** |
||
290 | * Get customer city. |
||
291 | * @return string |
||
292 | */ |
||
293 | public function get_billing_city() { |
||
296 | |||
297 | /** |
||
298 | * Get customer address. |
||
299 | * @return string |
||
300 | */ |
||
301 | public function get_billing_address() { |
||
304 | |||
305 | /** |
||
306 | * Get customer address. |
||
307 | * @return string |
||
308 | */ |
||
309 | public function get_billing_address_1() { |
||
312 | |||
313 | /** |
||
314 | * Get customer's second address. |
||
315 | * @return string |
||
316 | */ |
||
317 | public function get_billing_address_2() { |
||
320 | |||
321 | /** |
||
322 | * Get customer state. |
||
323 | * @return string |
||
324 | */ |
||
325 | public function get_billing_state() { |
||
328 | |||
329 | /** |
||
330 | * Get customer country. |
||
331 | * @return string |
||
332 | */ |
||
333 | public function get_billing_country() { |
||
336 | |||
337 | /** |
||
338 | * Get customer's shipping state. |
||
339 | * @return string |
||
340 | */ |
||
341 | public function get_shipping_state() { |
||
344 | |||
345 | /** |
||
346 | * Get customer's shipping country. |
||
347 | * @return string |
||
348 | */ |
||
349 | public function get_shipping_country() { |
||
352 | |||
353 | /** |
||
354 | * Get customer's shipping postcode. |
||
355 | * @return string |
||
356 | */ |
||
357 | public function get_shipping_postcode() { |
||
360 | |||
361 | /** |
||
362 | * Get customer's shipping city. |
||
363 | * @return string |
||
364 | */ |
||
365 | public function get_shipping_city() { |
||
368 | |||
369 | /** |
||
370 | * Get customer's shipping address. |
||
371 | * @return string |
||
372 | */ |
||
373 | public function get_shipping_address() { |
||
376 | |||
377 | /** |
||
378 | * Get customer address. |
||
379 | * @return string |
||
380 | */ |
||
381 | public function get_shipping_address_1() { |
||
384 | |||
385 | /** |
||
386 | * Get customer's second shipping address. |
||
387 | * @return string |
||
388 | */ |
||
389 | public function get_shipping_address_2() { |
||
392 | |||
393 | /** |
||
394 | * Get if customer is VAT exempt? |
||
395 | * @since 2.7.0 |
||
396 | * @return bool |
||
397 | */ |
||
398 | public function get_is_vat_exempt() { |
||
401 | |||
402 | /** |
||
403 | * Has customer calculated shipping? |
||
404 | * @return bool |
||
405 | */ |
||
406 | public function get_calculated_shipping() { |
||
409 | |||
410 | /** |
||
411 | * Get taxable address. |
||
412 | * @return array |
||
413 | */ |
||
414 | public function get_taxable_address() { |
||
441 | |||
442 | /** |
||
443 | * Gets a customer's downloadable products. |
||
444 | * @return array Array of downloadable products |
||
445 | */ |
||
446 | public function get_downloadable_products() { |
||
453 | |||
454 | /** |
||
455 | * Is the user a paying customer? |
||
456 | * @since 2.7.0 |
||
457 | * @return bool |
||
458 | */ |
||
459 | function get_is_paying_customer() { |
||
462 | |||
463 | /* |
||
464 | |-------------------------------------------------------------------------- |
||
465 | | Setters |
||
466 | |-------------------------------------------------------------------------- |
||
467 | | Functions for setting customer data. These should not update anything in the |
||
468 | | database itself and should only change what is stored in the class |
||
469 | | object. |
||
470 | */ |
||
471 | |||
472 | /** |
||
473 | * Set customer's username. |
||
474 | * @since 2.7.0 |
||
475 | * @param string $username |
||
476 | */ |
||
477 | public function set_username( $username ) { |
||
480 | |||
481 | /** |
||
482 | * Set customer's email. |
||
483 | * @since 2.7.0 |
||
484 | * @param string $email |
||
485 | */ |
||
486 | public function set_email( $email ) { |
||
489 | |||
490 | /** |
||
491 | * Set customer's first name. |
||
492 | * @since 2.7.0 |
||
493 | * @param string $first_name |
||
494 | */ |
||
495 | public function set_first_name( $first_name ) { |
||
498 | |||
499 | /** |
||
500 | * Set customer's last name. |
||
501 | * @since 2.7.0 |
||
502 | * @param string $last_name |
||
503 | */ |
||
504 | public function set_last_name( $last_name ) { |
||
507 | |||
508 | /** |
||
509 | * Set customer's user role(s). |
||
510 | * @since 2.7.0 |
||
511 | * @param mixed $role |
||
512 | */ |
||
513 | public function set_role( $role ) { |
||
516 | |||
517 | /** |
||
518 | * Set customer's last order ID. |
||
519 | * @since 2.7.0 |
||
520 | * @param integer|null $last_order_id |
||
521 | */ |
||
522 | public function set_last_order_id( $last_order_id ) { |
||
525 | |||
526 | /** |
||
527 | * Set the date of the customer's last order. |
||
528 | * @since 2.7.0 |
||
529 | * @param string|null $last_order_date |
||
530 | */ |
||
531 | public function set_last_order_date( $last_order_date ) { |
||
534 | |||
535 | /** |
||
536 | * Set the number of orders this customer has. |
||
537 | * @since 2.7.0 |
||
538 | * @param integer $order_count |
||
539 | */ |
||
540 | public function set_orders_count( $orders_count ) { |
||
543 | |||
544 | /** |
||
545 | * Return how much money this customer has spent. |
||
546 | * @since 2.7.0 |
||
547 | * @param float $total_spent |
||
548 | */ |
||
549 | public function set_total_spent( $total_spent ) { |
||
552 | |||
553 | /** |
||
554 | * Set customer's password. |
||
555 | * @since 2.7.0 |
||
556 | * @param string $password |
||
557 | */ |
||
558 | public function set_password( $password ) { |
||
561 | |||
562 | /** |
||
563 | * Set the date this customer was last updated. |
||
564 | * @since 2.7.0 |
||
565 | * @param integer $timestamp |
||
566 | */ |
||
567 | public function set_date_modified( $timestamp ) { |
||
570 | |||
571 | /** |
||
572 | * Set the date this customer was last updated. |
||
573 | * @since 2.7.0 |
||
574 | * @param integer $timestamp |
||
575 | */ |
||
576 | public function set_date_created( $timestamp ) { |
||
579 | |||
580 | /** |
||
581 | * Set customer address to match shop base address. |
||
582 | * @since 2.7.0 |
||
583 | */ |
||
584 | View Code Duplication | public function set_billing_address_to_base() { |
|
591 | |||
592 | /** |
||
593 | * Set customer shipping address to base address. |
||
594 | * @since 2.7.0 |
||
595 | */ |
||
596 | View Code Duplication | public function set_shipping_address_to_base() { |
|
603 | |||
604 | /** |
||
605 | * Sets all shipping info at once. |
||
606 | * @param string $country |
||
607 | * @param string $state |
||
608 | * @param string $postcode |
||
609 | * @param string $city |
||
610 | */ |
||
611 | public function set_shipping_location( $country, $state = '', $postcode = '', $city = '' ) { |
||
617 | |||
618 | /** |
||
619 | * Sets all address info at once. |
||
620 | * @param string $country |
||
621 | * @param string $state |
||
622 | * @param string $postcode |
||
623 | * @param string $city |
||
624 | */ |
||
625 | public function set_billing_location( $country, $state, $postcode = '', $city = '' ) { |
||
631 | |||
632 | /** |
||
633 | * Set customer country. |
||
634 | * @param mixed $country |
||
635 | */ |
||
636 | public function set_billing_country( $country ) { |
||
639 | |||
640 | /** |
||
641 | * Set customer state. |
||
642 | * @param mixed $state |
||
643 | */ |
||
644 | public function set_billing_state( $state ) { |
||
647 | |||
648 | /** |
||
649 | * Sets customer postcode. |
||
650 | * @param mixed $postcode |
||
651 | */ |
||
652 | public function set_billing_postcode( $postcode ) { |
||
655 | |||
656 | /** |
||
657 | * Sets customer city. |
||
658 | * @param mixed $city |
||
659 | */ |
||
660 | public function set_billing_city( $city ) { |
||
663 | |||
664 | /** |
||
665 | * Set customer address. |
||
666 | * @param mixed $address |
||
667 | */ |
||
668 | public function set_billing_address( $address ) { |
||
671 | |||
672 | /** |
||
673 | * Set customer address. |
||
674 | * @param mixed $address |
||
675 | */ |
||
676 | public function set_billing_address_1( $address ) { |
||
679 | |||
680 | /** |
||
681 | * Set customer's second address. |
||
682 | * @param mixed $address |
||
683 | */ |
||
684 | public function set_billing_address_2( $address ) { |
||
687 | |||
688 | /** |
||
689 | * Set shipping country. |
||
690 | * @param string $country |
||
691 | */ |
||
692 | public function set_shipping_country( $country ) { |
||
695 | |||
696 | /** |
||
697 | * Set shipping state. |
||
698 | * @param string $state |
||
699 | */ |
||
700 | public function set_shipping_state( $state ) { |
||
703 | |||
704 | /** |
||
705 | * Set shipping postcode. |
||
706 | * @param string $postcode |
||
707 | */ |
||
708 | public function set_shipping_postcode( $postcode ) { |
||
711 | |||
712 | /** |
||
713 | * Sets shipping city. |
||
714 | * @param string $city |
||
715 | */ |
||
716 | public function set_shipping_city( $city ) { |
||
719 | |||
720 | /** |
||
721 | * Set shipping address. |
||
722 | * @param string $address |
||
723 | */ |
||
724 | public function set_shipping_address( $address ) { |
||
727 | |||
728 | /** |
||
729 | * Set customer shipping address. |
||
730 | * @param mixed $address |
||
731 | */ |
||
732 | public function set_shipping_address_1( $address ) { |
||
735 | |||
736 | /** |
||
737 | * Set second shipping address. |
||
738 | * @param string $address |
||
739 | */ |
||
740 | public function set_shipping_address_2( $address ) { |
||
743 | |||
744 | /** |
||
745 | * Set if customer has tax exemption. |
||
746 | * @param bool $is_vat_exempt |
||
747 | */ |
||
748 | public function set_is_vat_exempt( $is_vat_exempt ) { |
||
751 | |||
752 | /** |
||
753 | * Calculated shipping? |
||
754 | * @param boolean $calculated |
||
755 | */ |
||
756 | public function set_calculated_shipping( $calculated = true ) { |
||
759 | |||
760 | /** |
||
761 | * Set if the user a paying customer. |
||
762 | * @since 2.7.0 |
||
763 | * @param boolean $is_paying_customer |
||
764 | */ |
||
765 | function set_is_paying_customer( $is_paying_customer ) { |
||
768 | |||
769 | /* |
||
770 | |-------------------------------------------------------------------------- |
||
771 | | Other methods |
||
772 | |-------------------------------------------------------------------------- |
||
773 | | Other functions for interacting with customers. |
||
774 | */ |
||
775 | |||
776 | /** |
||
777 | * Is customer outside base country (for tax purposes)? |
||
778 | * @return bool |
||
779 | */ |
||
780 | public function is_customer_outside_base() { |
||
793 | |||
794 | /* |
||
795 | |-------------------------------------------------------------------------- |
||
796 | | CRUD methods |
||
797 | |-------------------------------------------------------------------------- |
||
798 | | Methods which create, read, update and delete from the database. |
||
799 | | |
||
800 | | A save method is included for convenience (chooses update or create based |
||
801 | | on if the order exists yet). |
||
802 | */ |
||
803 | |||
804 | /** |
||
805 | * Create a customer. |
||
806 | * @since 2.7.0. |
||
807 | */ |
||
808 | public function create() { |
||
836 | |||
837 | /** |
||
838 | * Read a customer from the database. |
||
839 | * @since 2.7.0 |
||
840 | * @param integer $id |
||
841 | */ |
||
842 | public function read( $id ) { |
||
965 | |||
966 | /** |
||
967 | * Update a customer. |
||
968 | * @since 2.7.0 |
||
969 | */ |
||
970 | public function update() { |
||
1000 | |||
1001 | /** |
||
1002 | * Delete a customer. |
||
1003 | * @since 2.7.0 |
||
1004 | */ |
||
1005 | public function delete() { |
||
1011 | |||
1012 | /** |
||
1013 | * Save data. Create when creating a new user/class, update when editing |
||
1014 | * an existing user, and save session when working on a logged out guest |
||
1015 | * session. |
||
1016 | * @since 2.7.0 |
||
1017 | */ |
||
1018 | public function save() { |
||
1033 | |||
1034 | /** |
||
1035 | * Saves data to the session only (does not overwrite DB values). |
||
1036 | * @since 2.7.0 |
||
1037 | */ |
||
1038 | public function save_to_session() { |
||
1052 | |||
1053 | } |
||
1054 |
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.