Completed
Pull Request — master (#11821)
by Mike
14:32
created

WC_Customer::has_calculated_shipping()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
include_once( 'legacy/class-wc-legacy-customer.php' );
3
4
if ( ! defined( 'ABSPATH' ) ) {
5
	exit;
6
}
7
8
/**
9
 * The WooCommerce customer class handles storage of the current customer's data, such as location.
10
 *
11
 * @class    WC_Customer
12
 * @version  2.7.0
13
 * @package  WooCommerce/Classes
14
 * @category Class
15
 * @author   WooThemes
16
 */
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
		'date_created'       => '',
26
		'date_modified'      => '',
27
		'email'              => '',
28
		'first_name'         => '',
29
		'last_name'          => '',
30
		'role'               => 'customer',
31
		'username'           => '',
32
		'billing'            => array(
33
			'first_name'     => '',
34
			'last_name'      => '',
35
			'company'        => '',
36
			'address_1'      => '',
37
			'address_2'      => '',
38
			'city'           => '',
39
			'state'          => '',
40
			'postcode'       => '',
41
			'country'        => '',
42
			'email'          => '',
43
			'phone'          => '',
44
		),
45
		'shipping'           => array(
46
			'first_name'     => '',
47
			'last_name'      => '',
48
			'company'        => '',
49
			'address_1'      => '',
50
			'address_2'      => '',
51
			'city'           => '',
52
			'state'          => '',
53
			'postcode'       => '',
54
			'country'        => '',
55
		),
56
		'is_paying_customer' => false,
57
	);
58
59
	/**
60
	 * Keys which are also stored in a session (so we can make sure they get updated...)
61
	 * @var array
62
	 */
63
	protected $_session_keys = array(
64
		'billing_postcode', 'billing_city', 'billing_address_1', 'billing_address', 'billing_address_2',
65
		'billing_state', 'billing_country', 'shipping_postcode', 'shipping_city', 'shipping_address_1', 'shipping_address',
66
		'shipping_address_2', 'shipping_state', 'shipping_country', 'is_vat_exempt', 'calculated_shipping',
67
		'billing_first_name', 'billing_last_name', 'billing_company', 'billing_phone', 'billing_email',
68
		'shipping_first_name', 'shipping_last_name', 'shipping_company',
69
	);
70
71
	/**
72
	 * Data stored in meta keys, but not considered "meta"
73
	 * @since 2.7.0
74
	 * @var array
75
	 */
76
	protected $_internal_meta_keys = array(
77
		'billing_postcode', 'billing_city', 'billing_address_1', 'billing_address_2', 'billing_state',
78
		'billing_country', 'shipping_postcode', 'shipping_city', 'shipping_address_1',
79
		'shipping_address_2', 'shipping_state', 'shipping_country', 'paying_customer',
80
		'last_update', 'first_name', 'last_name', 'show_admin_bar_front',
81
		'use_ssl', 'admin_color', 'rich_editing', 'comment_shortcuts', 'dismissed_wp_pointers', 'show_welcome_panel',
82
		'_woocommerce_persistent_cart', 'session_tokens', 'nickname', 'description',
83
		'billing_first_name', 'billing_last_name', 'billing_company', 'billing_phone', 'billing_email',
84
		'shipping_first_name', 'shipping_last_name', 'shipping_company', 'default_password_nag',
85
		'primary_blog', 'source_domain',
86
	);
87
88
	/**
89
	 * Internal meta type used to store user data.
90
	 * @var string
91
	 */
92
	protected $_meta_type = 'user';
93
94
	/**
95
	 * If this is the customer session, this is true. When true, guest accounts will not be saved to the DB.
96
	 * @var boolean
97
	 */
98
	protected $_is_session = false;
99
100
	/**
101
	 * Stores a password if this needs to be changed. Write-only and hidden from _data.
102
	 * @var string
103
	 */
104
	protected $_password = '';
105
106
	/**
107
	 * Stores if user is VAT exempt for this session.
108
	 * @var string
109
	 */
110
	protected $_is_vat_exempt = false;
111
112
	/**
113
	 * Stores if user has calculated shipping in this session.
114
	 * @var string
115
	 */
116
	protected $_calculated_shipping = false;
117
118
	/**
119
	 * Load customer data based on how WC_Customer is called.
120
	 *
121
	 * If $customer is 'new', you can build a new WC_Customer object. If it's empty, some
122
	 * data will be pulled from the session for the current user/customer.
123
	 *
124
	 * @param int $customer_id Customer ID
125
	 * @param bool $is_session True if this is the customer session
126
	 */
127
	public function __construct( $customer_id = 0, $is_session = false ) {
128
		if ( $customer_id > 0 ) {
129
			$this->read( $customer_id );
130
		}
131
		if ( $is_session ) {
132
			$this->_is_session = true;
133
			$this->load_session();
134
			add_action( 'shutdown', array( $this, 'save_to_session' ), 10 );
135
		}
136
	}
137
138
	/**
139
	 * Loads a WC session into the customer class.
140
	 */
141
	public function load_session() {
142
		$data = (array) WC()->session->get( 'customer' );
143
		if ( ! empty( $data ) ) {
144
			foreach ( $this->_session_keys as $session_key ) {
145
				$function_key = $session_key;
146
				if ( 'billing_' === substr( $session_key, 0, 8 ) ) {
147
					$session_key = str_replace( 'billing_', '', $session_key );
148
				}
149
				if ( ! empty( $data[ $session_key ] ) && is_callable( array( $this, "set_{$function_key}" ) ) ) {
150
					$this->{"set_{$function_key}"}( $data[ $session_key ] );
151
				}
152
			}
153
		}
154
		$this->load_defaults();
155
	}
156
157
	/**
158
	 * Load default values if props are unset.
159
	 */
160
	protected function load_defaults() {
161
		$default = wc_get_customer_default_location();
162
163
		// Set some defaults if some of our values are still not set.
164
		if ( ! $this->get_billing_country() ) {
165
			$this->set_billing_country( $default['country'] );
166
		}
167
168
		if ( ! $this->get_shipping_country() ) {
169
			$this->set_shipping_country( $this->get_billing_country() );
170
		}
171
172
		if ( ! $this->get_billing_state() ) {
173
			$this->set_billing_state( $default['state'] );
174
		}
175
176
		if ( ! $this->get_shipping_state() ) {
177
			$this->set_shipping_state( $this->get_billing_state() );
178
		}
179
	}
180
181
	/**
182
	 * Gets the customers last order.
183
	 * @return WC_Order|false
184
	 */
185
	public function get_last_order() {
186
		global $wpdb;
187
188
		$last_order = $wpdb->get_var( "SELECT posts.ID
189
			FROM $wpdb->posts AS posts
190
			LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
191
			WHERE meta.meta_key = '_customer_user'
192
			AND   meta.meta_value = '" . esc_sql( $this->get_id() ) . "'
193
			AND   posts.post_type = 'shop_order'
194
			AND   posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
195
			ORDER BY posts.ID DESC
196
		" );
197
198
		if ( $last_order ) {
199
			return wc_get_order( absint( $last_order ) );
200
		} else {
201
			return false;
202
		}
203
	}
204
205
	/**
206
	 * Return the number of orders this customer has.
207
	 * @since 2.7.0
208
	 * @return integer
209
	 */
210
	public function get_order_count() {
211
		global $wpdb;
212
213
		$count = $wpdb->get_var( "SELECT COUNT(*)
214
			FROM $wpdb->posts as posts
215
			LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
216
			WHERE   meta.meta_key = '_customer_user'
217
			AND     posts.post_type = 'shop_order'
218
			AND     posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
219
			AND     meta_value = '" . esc_sql( $this->get_id() ) . "'
220
		" );
221
222
		return absint( $count );
223
	}
224
225
	/**
226
	 * Return how much money this customer has spent.
227
	 * @since 2.7.0
228
	 * @return float
229
	 */
230
	public function get_total_spent() {
231
		global $wpdb;
232
233
		$spent = $wpdb->get_var( "SELECT SUM(meta2.meta_value)
234
			FROM $wpdb->posts as posts
235
			LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
236
			LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id
237
			WHERE   meta.meta_key       = '_customer_user'
238
			AND     meta.meta_value     = '" . esc_sql( $this->get_id() ) . "'
239
			AND     posts.post_type     = 'shop_order'
240
			AND     posts.post_status   IN ( 'wc-completed', 'wc-processing' )
241
			AND     meta2.meta_key      = '_order_total'
242
		" );
243
244
		if ( ! $spent ) {
245
			$spent = 0;
246
		}
247
248
		return wc_format_decimal( $spent, 2 );
249
	}
250
251
	/**
252
	 * Is customer outside base country (for tax purposes)?
253
	 * @return bool
254
	 */
255
	public function is_customer_outside_base() {
256
		list( $country, $state ) = $this->get_taxable_address();
257
		if ( $country ) {
258
			$default = wc_get_base_location();
259
			if ( $default['country'] !== $country ) {
260
				return true;
261
			}
262
			if ( $default['state'] && $default['state'] !== $state ) {
263
				return true;
264
			}
265
		}
266
		return false;
267
	}
268
269
	/**
270
	 * Is customer VAT exempt?
271
	 * @return bool
272
	 */
273
	public function is_vat_exempt() {
274
		return $this->get_is_vat_exempt();
275
	}
276
277
	/**
278
	 * Has calculated shipping?
279
	 * @return bool
280
	 */
281
	public function has_calculated_shipping() {
282
		return $this->get_calculated_shipping();
283
	}
284
285
	/*
286
	 |--------------------------------------------------------------------------
287
	 | Getters
288
	 |--------------------------------------------------------------------------
289
	 | Methods for getting data from the customer object.
290
	 */
291
292
	/**
293
	 * Return a customer's user ID. Logged out users have ID 0.
294
	 * @since 2.7.0
295
	 * @return int
296
	 */
297
	public function get_id() {
298
		return $this->_data['id'];
299
	}
300
301
	/**
302
	 * Return the customer's username.
303
	 * @since 2.7.0
304
	 * @return string
305
	 */
306
	public function get_username() {
307
		return $this->_data['username'];
308
	}
309
310
	/**
311
	 * Return the customer's email.
312
	 * @since 2.7.0
313
	 * @return string
314
	 */
315
	public function get_email() {
316
		return $this->_data['email'];
317
	}
318
319
	/**
320
	 * Return customer's first name.
321
	 * @since 2.7.0
322
	 * @return string
323
	 */
324
	public function get_first_name() {
325
		return $this->_data['first_name'];
326
	}
327
328
	/**
329
	 * Return customer's last name.
330
	 * @since 2.7.0
331
	 * @return string
332
	 */
333
	public function get_last_name() {
334
		return $this->_data['last_name'];
335
	}
336
337
	/**
338
	 * Return customer's user role.
339
	 * @since 2.7.0
340
	 * @return string
341
	 */
342
	public function get_role() {
343
		return $this->_data['role'];
344
	}
345
346
	/**
347
	 * Return this customer's avatar.
348
	 * @since 2.7.0
349
	 * @return string
350
	 */
351 View Code Duplication
	public function get_avatar_url() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
352
		$avatar_html = get_avatar( $this->get_email() );
353
354
		// Get the URL of the avatar from the provided HTML
355
		preg_match( '/src=["|\'](.+)[\&|"|\']/U', $avatar_html, $matches );
356
357
		if ( isset( $matches[1] ) && ! empty( $matches[1] ) ) {
358
			return esc_url( $matches[1] );
359
		}
360
361
		return '';
362
	}
363
364
	/**
365
	 * Return the date this customer was created.
366
	 * @since 2.7.0
367
	 * @return integer
368
	 */
369
	public function get_date_created() {
370
		return absint( $this->_data['date_created'] );
371
	}
372
373
	/**
374
	 * Return the date this customer was last updated.
375
	 * @since 2.7.0
376
	 * @return integer
377
	 */
378
	public function get_date_modified() {
379
		return absint( $this->_data['date_modified'] );
380
	}
381
382
	/**
383
	 * Gets customer billing first name.
384
	 * @return string
385
	 */
386
	public function get_billing_first_name() {
387
		return $this->_data['billing']['first_name'];
388
	}
389
390
	/**
391
	 * Gets customer billing last name.
392
	 * @return string
393
	 */
394
	public function get_billing_last_name() {
395
		return $this->_data['billing']['last_name'];
396
	}
397
398
	/**
399
	 * Gets customer billing company.
400
	 * @return string
401
	 */
402
	public function get_billing_company() {
403
		return $this->_data['billing']['company'];
404
	}
405
406
	/**
407
	 * Gets billing phone.
408
	 * @return string
409
	 */
410
	public function get_billing_phone() {
411
		return $this->_data['billing']['phone'];
412
	}
413
414
	/**
415
	 * Gets billing email.
416
	 * @return string
417
	 */
418
	public function get_billing_email() {
419
		return $this->_data['billing']['email'];
420
	}
421
422
	/**
423
	 * Gets customer postcode.
424
	 * @return string
425
	 */
426
	public function get_billing_postcode() {
427
		return wc_format_postcode( $this->_data['billing']['postcode'], $this->get_billing_country() );
428
	}
429
430
	/**
431
	 * Get customer city.
432
	 * @return string
433
	 */
434
	public function get_billing_city() {
435
		return $this->_data['billing']['city'];
436
	}
437
438
	/**
439
	 * Get customer address.
440
	 * @return string
441
	 */
442
	public function get_billing_address() {
443
		return $this->_data['billing']['address_1'];
444
	}
445
446
	/**
447
	 * Get customer address.
448
	 * @return string
449
	 */
450
	public function get_billing_address_1() {
451
		return $this->get_billing_address();
452
	}
453
454
	/**
455
	 * Get customer's second address.
456
	 * @return string
457
	 */
458
	public function get_billing_address_2() {
459
		return $this->_data['billing']['address_2'];
460
	}
461
462
	/**
463
	 * Get customer state.
464
	 * @return string
465
	 */
466
	public function get_billing_state() {
467
		return $this->_data['billing']['state'];
468
	}
469
470
	/**
471
	 * Get customer country.
472
	 * @return string
473
	 */
474
	public function get_billing_country() {
475
		return $this->_data['billing']['country'];
476
	}
477
478
	/**
479
	 * Gets customer shipping first name.
480
	 * @return string
481
	 */
482
	public function get_shipping_first_name() {
483
		return $this->_data['shipping']['first_name'];
484
	}
485
486
	/**
487
	 * Gets customer shipping last name.
488
	 * @return string
489
	 */
490
	public function get_shipping_last_name() {
491
		return $this->_data['shipping']['last_name'];
492
	}
493
494
	/**
495
	 * Gets customer shipping company.
496
	 * @return string
497
	 */
498
	public function get_shipping_company() {
499
		return $this->_data['shipping']['company'];
500
	}
501
502
	/**
503
	 * Get customer's shipping state.
504
	 * @return string
505
	 */
506
	public function get_shipping_state() {
507
		return $this->_data['shipping']['state'];
508
	}
509
510
	/**
511
	 * Get customer's shipping country.
512
	 * @return string
513
	 */
514
	public function get_shipping_country() {
515
		return $this->_data['shipping']['country'];
516
	}
517
518
	/**
519
	 * Get customer's shipping postcode.
520
	 * @return string
521
	 */
522
	public function get_shipping_postcode() {
523
		return wc_format_postcode( $this->_data['shipping']['postcode'], $this->get_shipping_country() );
524
	}
525
526
	/**
527
	 * Get customer's shipping city.
528
	 * @return string
529
	 */
530
	public function get_shipping_city() {
531
		return $this->_data['shipping']['city'];
532
	}
533
534
	/**
535
	 * Get customer's shipping address.
536
	 * @return string
537
	 */
538
	public function get_shipping_address() {
539
		return $this->_data['shipping']['address_1'];
540
	}
541
542
	/**
543
	 * Get customer address.
544
	 * @return string
545
	 */
546
	public function get_shipping_address_1() {
547
		return $this->get_shipping_address();
548
	}
549
550
	/**
551
	 * Get customer's second shipping address.
552
	 * @return string
553
	 */
554
	public function get_shipping_address_2() {
555
		return $this->_data['shipping']['address_2'];
556
	}
557
558
	/**
559
	 * Get if customer is VAT exempt?
560
	 * @since 2.7.0
561
	 * @return bool
562
	 */
563
	public function get_is_vat_exempt() {
564
		return $this->_is_vat_exempt;
565
	}
566
567
	/**
568
	 * Has customer calculated shipping?
569
	 * @return bool
570
	 */
571
	public function get_calculated_shipping() {
572
		return $this->_calculated_shipping;
573
	}
574
575
	/**
576
	 * Get taxable address.
577
	 * @return array
578
	 */
579
	public function get_taxable_address() {
580
		$tax_based_on = get_option( 'woocommerce_tax_based_on' );
581
582
		// Check shipping method at this point to see if we need special handling
583
		if ( true === apply_filters( 'woocommerce_apply_base_tax_for_local_pickup', true ) && sizeof( array_intersect( wc_get_chosen_shipping_method_ids(), apply_filters( 'woocommerce_local_pickup_methods', array( 'legacy_local_pickup', 'local_pickup' ) ) ) ) > 0 ) {
584
			$tax_based_on = 'base';
585
		}
586
587
		if ( 'base' === $tax_based_on ) {
588
			$country  = WC()->countries->get_base_country();
589
			$state    = WC()->countries->get_base_state();
590
			$postcode = WC()->countries->get_base_postcode();
591
			$city     = WC()->countries->get_base_city();
592
		} elseif ( 'billing' === $tax_based_on ) {
593
			$country  = $this->get_billing_country();
594
			$state    = $this->get_billing_state();
595
			$postcode = $this->get_billing_postcode();
596
			$city     = $this->get_billing_city();
597
		} else {
598
			$country  = $this->get_shipping_country();
599
			$state    = $this->get_shipping_state();
600
			$postcode = $this->get_shipping_postcode();
601
			$city     = $this->get_shipping_city();
602
		}
603
604
		return apply_filters( 'woocommerce_customer_taxable_address', array( $country, $state, $postcode, $city ) );
605
	}
606
607
	/**
608
	 * Gets a customer's downloadable products.
609
	 * @return array Array of downloadable products
610
	 */
611
	public function get_downloadable_products() {
612
		$downloads = array();
613
		if ( $this->get_id() ) {
614
			$downloads = wc_get_customer_available_downloads( $this->get_id() );
615
		}
616
		return apply_filters( 'woocommerce_customer_get_downloadable_products', $downloads );
617
	}
618
619
	/**
620
	 * Is the user a paying customer?
621
	 * @since 2.7.0
622
	 * @return bool
623
	 */
624
	function get_is_paying_customer() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
625
		return (bool) $this->_data['is_paying_customer'];
626
	}
627
628
	/*
629
	|--------------------------------------------------------------------------
630
	| Setters
631
	|--------------------------------------------------------------------------
632
	| Functions for setting customer data. These should not update anything in the
633
	| database itself and should only change what is stored in the class
634
	| object.
635
	*/
636
637
	/**
638
	 * Set customer ID.
639
	 * @since 2.7.0
640
	 * @param int $value
641
	 * @throws WC_Data_Exception
642
	 */
643
	protected function set_id( $value ) {
644
		$this->_data['id'] = absint( $value );
645
	}
646
647
	/**
648
	 * Set customer's username.
649
	 * @since 2.7.0
650
	 * @param string $username
651
	 * @throws WC_Data_Exception
652
	 */
653
	public function set_username( $username ) {
654
		$this->_data['username'] = $username;
655
	}
656
657
	/**
658
	 * Set customer's email.
659
	 * @since 2.7.0
660
	 * @param string $value
661
	 * @throws WC_Data_Exception
662
	 */
663
	public function set_email( $value ) {
664
		if ( $value && ! is_email( $value ) ) {
665
			$this->error( 'customer_invalid_email', __( 'Invalid email address', 'woocommerce' ) );
666
		}
667
		$this->_data['email'] = sanitize_email( $value );
668
	}
669
670
	/**
671
	 * Set customer's first name.
672
	 * @since 2.7.0
673
	 * @param string $first_name
674
	 * @throws WC_Data_Exception
675
	 */
676
	public function set_first_name( $first_name ) {
677
		$this->_data['first_name'] = $first_name;
678
	}
679
680
	/**
681
	 * Set customer's last name.
682
	 * @since 2.7.0
683
	 * @param string $last_name
684
	 * @throws WC_Data_Exception
685
	 */
686
	public function set_last_name( $last_name ) {
687
		$this->_data['last_name'] = $last_name;
688
	}
689
690
	/**
691
	 * Set customer's user role(s).
692
	 * @since 2.7.0
693
	 * @param mixed $role
694
	 * @throws WC_Data_Exception
695
	 */
696
	public function set_role( $role ) {
697
		global $wp_roles;
698
699
		if ( $role && ! empty( $wp_roles->roles ) && ! in_array( $role, array_keys( $wp_roles->roles ) ) ) {
700
			$this->error( 'customer_invalid_role', __( 'Invalid role', 'woocommerce' ) );
701
		}
702
		$this->_data['role'] = $role;
703
	}
704
705
	/**
706
	 * Set customer's password.
707
	 * @since 2.7.0
708
	 * @param string $password
709
	 * @throws WC_Data_Exception
710
	 */
711
	public function set_password( $password ) {
712
		$this->_password = wc_clean( $password );
0 ignored issues
show
Documentation Bug introduced by
It seems like wc_clean($password) can also be of type array. However, the property $_password is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
713
	}
714
715
	/**
716
	 * Set the date this customer was last updated.
717
	 * @since 2.7.0
718
	 * @param integer $timestamp
719
	 * @throws WC_Data_Exception
720
	 */
721
	public function set_date_modified( $timestamp ) {
722
		$this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
723
	}
724
725
	/**
726
	 * Set the date this customer was last updated.
727
	 * @since 2.7.0
728
	 * @param integer $timestamp
729
	 * @throws WC_Data_Exception
730
	 */
731
	public function set_date_created( $timestamp ) {
732
		$this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
733
	}
734
735
	/**
736
	 * Set customer address to match shop base address.
737
	 * @since 2.7.0
738
	 * @throws WC_Data_Exception
739
	 */
740
	public function set_billing_address_to_base() {
741
		$base = wc_get_customer_default_location();
742
		$this->_data['billing']['country']  = $base['country'];
743
		$this->_data['billing']['state']    = $base['state'];
744
		$this->_data['billing']['postcode'] = '';
745
		$this->_data['billing']['city']     = '';
746
	}
747
748
	/**
749
	 * Set customer shipping address to base address.
750
	 * @since 2.7.0
751
	 * @throws WC_Data_Exception
752
	 */
753
	public function set_shipping_address_to_base() {
754
		$base = wc_get_customer_default_location();
755
		$this->_data['shipping']['country']  = $base['country'];
756
		$this->_data['shipping']['state']    = $base['state'];
757
		$this->_data['shipping']['postcode'] = '';
758
		$this->_data['shipping']['city']     = '';
759
	}
760
761
	/**
762
	 * Sets all shipping info at once.
763
	 * @param string $country
764
	 * @param string $state
765
	 * @param string $postcode
766
	 * @param string $city
767
	 * @throws WC_Data_Exception
768
	 */
769 View Code Duplication
	public function set_shipping_location( $country, $state = '', $postcode = '', $city = '' ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
770
		$this->_data['shipping']['country']  = $country;
771
		$this->_data['shipping']['state']    = $state;
772
		$this->_data['shipping']['postcode'] = $postcode;
773
		$this->_data['shipping']['city']     = $city;
774
	}
775
776
	/**
777
	 * Sets all address info at once.
778
	 * @param string $country
779
	 * @param string $state
780
	 * @param string $postcode
781
	 * @param string $city
782
	 * @throws WC_Data_Exception
783
	 */
784 View Code Duplication
	public function set_billing_location( $country, $state, $postcode = '', $city = '' ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
785
		$this->_data['billing']['country']  = $country;
786
		$this->_data['billing']['state']    = $state;
787
		$this->_data['billing']['postcode'] = $postcode;
788
		$this->_data['billing']['city']     = $city;
789
	}
790
791
	/**
792
	 * Set billing first name.
793
	 * @return string
794
	 * @throws WC_Data_Exception
795
	 */
796
	public function set_billing_first_name( $value ) {
797
		$this->_data['billing']['first_name'] = $value;
798
	}
799
800
	/**
801
	 * Set billing last name.
802
	 * @return string
803
	 * @throws WC_Data_Exception
804
	 */
805
	public function set_billing_last_name( $value ) {
806
		$this->_data['billing']['last_name'] = $value;
807
	}
808
809
	/**
810
	 * Set billing company.
811
	 * @return string
812
	 * @throws WC_Data_Exception
813
	 */
814
	public function set_billing_company( $value ) {
815
		$this->_data['billing']['company'] = $value;
816
	}
817
818
	/**
819
	 * Set billing phone.
820
	 * @return string
821
	 * @throws WC_Data_Exception
822
	 */
823
	public function set_billing_phone( $value ) {
824
		$this->_data['billing']['phone'] = $value;
825
	}
826
827
	/**
828
	 * Set billing email.
829
	 * @param string $value
830
	 * @return string
831
	 * @throws WC_Data_Exception
832
	 */
833
	public function set_billing_email( $value ) {
834
		if ( $value && ! is_email( $value ) ) {
835
			$this->error( 'customer_invalid_billing_email', __( 'Invalid billing email address', 'woocommerce' ) );
836
		}
837
		$this->_data['billing']['email'] = sanitize_email( $value );
838
	}
839
840
	/**
841
	 * Set customer country.
842
	 * @param mixed $country
843
	 * @throws WC_Data_Exception
844
	 */
845
	public function set_billing_country( $country ) {
846
		$this->_data['billing']['country'] = $country;
847
	}
848
849
	/**
850
	 * Set customer state.
851
	 * @param mixed $state
852
	 * @throws WC_Data_Exception
853
	 */
854
	public function set_billing_state( $state ) {
855
		$this->_data['billing']['state'] = $state;
856
	}
857
858
	/**
859
	 * Sets customer postcode.
860
	 * @param mixed $postcode
861
	 * @throws WC_Data_Exception
862
	 */
863
	public function set_billing_postcode( $postcode ) {
864
		$this->_data['billing']['postcode'] = $postcode;
865
	}
866
867
	/**
868
	 * Sets customer city.
869
	 * @param mixed $city
870
	 * @throws WC_Data_Exception
871
	 */
872
	public function set_billing_city( $city ) {
873
		$this->_data['billing']['city'] = $city;
874
	}
875
876
	/**
877
	 * Set customer address.
878
	 * @param mixed $address
879
	 * @throws WC_Data_Exception
880
	 */
881
	public function set_billing_address( $address ) {
882
		$this->_data['billing']['address_1'] = $address;
883
	}
884
885
	/**
886
	 * Set customer address.
887
	 * @param mixed $address
888
	 * @throws WC_Data_Exception
889
	 */
890
	public function set_billing_address_1( $address ) {
891
		$this->set_billing_address( $address );
892
	}
893
894
	/**
895
	 * Set customer's second address.
896
	 * @param mixed $address
897
	 * @throws WC_Data_Exception
898
	 */
899
	public function set_billing_address_2( $address ) {
900
		$this->_data['billing']['address_2'] = $address;
901
	}
902
903
	/**
904
	 * Sets customer shipping first name.
905
	 * @param string $first_name
906
	 * @throws WC_Data_Exception
907
	 */
908
	public function set_shipping_first_name( $first_name ) {
909
		$this->_data['shipping']['first_name'] = $first_name;
910
	}
911
912
	/**
913
	 * Sets customer shipping last name.
914
	 * @param string $last_name
915
	 * @throws WC_Data_Exception
916
	 */
917
	public function set_shipping_last_name( $last_name ) {
918
		$this->_data['shipping']['last_name'] = $last_name;
919
	}
920
921
	/**
922
	 * Sets customer shipping company.
923
	 * @param string $company.
0 ignored issues
show
Documentation introduced by
There is no parameter named $company.. Did you maybe mean $company?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
924
	 * @throws WC_Data_Exception
925
	 */
926
	public function set_shipping_company( $company ) {
927
		$this->_data['shipping']['company'] = $company;
928
	}
929
930
	/**
931
	 * Set shipping country.
932
	 * @param string $country
933
	 * @throws WC_Data_Exception
934
	 */
935
	public function set_shipping_country( $country ) {
936
		$this->_data['shipping']['country'] = $country;
937
	}
938
939
	/**
940
	 * Set shipping state.
941
	 * @param string $state
942
	 * @throws WC_Data_Exception
943
	 */
944
	public function set_shipping_state( $state ) {
945
		$this->_data['shipping']['state'] = $state;
946
	}
947
948
	/**
949
	 * Set shipping postcode.
950
	 * @param string $postcode
951
	 * @throws WC_Data_Exception
952
	 */
953
	public function set_shipping_postcode( $postcode ) {
954
		$this->_data['shipping']['postcode'] = $postcode;
955
	}
956
957
	/**
958
	 * Sets shipping city.
959
	 * @param string $city
960
	 * @throws WC_Data_Exception
961
	 */
962
	public function set_shipping_city( $city ) {
963
		$this->_data['shipping']['city'] = $city;
964
	}
965
966
	/**
967
	 * Set shipping address.
968
	 * @param string $address
969
	 * @throws WC_Data_Exception
970
	 */
971
	public function set_shipping_address( $address ) {
972
		$this->_data['shipping']['address_1'] = $address;
973
	}
974
975
	/**
976
	 * Set customer shipping address.
977
	 * @param mixed $address
978
	 * @throws WC_Data_Exception
979
	 */
980
	public function set_shipping_address_1( $address ) {
981
		$this->set_shipping_address( $address );
982
	}
983
984
	/**
985
	 * Set second shipping address.
986
	 * @param string $address
987
	 * @throws WC_Data_Exception
988
	 */
989
	public function set_shipping_address_2( $address ) {
990
		$this->_data['shipping']['address_2'] = $address;
991
	}
992
993
	/**
994
	 * Set if the user a paying customer.
995
	 * @since 2.7.0
996
	 * @param boolean $is_paying_customer
997
	 * @throws WC_Data_Exception
998
	 */
999
	function set_is_paying_customer( $is_paying_customer ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1000
		$this->_data['is_paying_customer'] = (bool) $is_paying_customer;
1001
	}
1002
1003
	/**
1004
	 * Set if customer has tax exemption.
1005
	 * @param bool $is_vat_exempt
1006
	 */
1007
	public function set_is_vat_exempt( $is_vat_exempt ) {
1008
		$this->_is_vat_exempt = (bool) $is_vat_exempt;
0 ignored issues
show
Documentation Bug introduced by
The property $_is_vat_exempt was declared of type string, but (bool) $is_vat_exempt is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1009
	}
1010
1011
	/**
1012
	 * Calculated shipping?
1013
	 * @param boolean $calculated
1014
	 */
1015
	public function set_calculated_shipping( $calculated = true ) {
1016
		$this->_calculated_shipping = (bool) $calculated;
0 ignored issues
show
Documentation Bug introduced by
The property $_calculated_shipping was declared of type string, but (bool) $calculated is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1017
	}
1018
1019
	/*
1020
	 |--------------------------------------------------------------------------
1021
	 | CRUD methods
1022
	 |--------------------------------------------------------------------------
1023
	 | Methods which create, read, update and delete from the database.
1024
	 |
1025
	 | A save method is included for convenience (chooses update or create based
1026
	 | on if the order exists yet).
1027
	 */
1028
1029
	 /**
1030
	  * Create a customer.
1031
	  * @since 2.7.0.
1032
	  */
1033
	public function create() {
1034
		$customer_id = wc_create_new_customer( $this->get_email(), $this->get_username(), $this->_password );
1035
1036
		if ( ! is_wp_error( $customer_id ) ) {
1037
			$this->_data['id'] = $customer_id;
1038
			update_user_meta( $this->get_id(), 'billing_first_name', $this->get_billing_first_name() );
1039
			update_user_meta( $this->get_id(), 'billing_last_name', $this->get_billing_last_name() );
1040
			update_user_meta( $this->get_id(), 'billing_company', $this->get_billing_company() );
1041
			update_user_meta( $this->get_id(), 'billing_phone', $this->get_billing_phone() );
1042
			update_user_meta( $this->get_id(), 'billing_email', $this->get_billing_email() );
1043
			update_user_meta( $this->get_id(), 'billing_postcode', $this->get_billing_postcode() );
1044
			update_user_meta( $this->get_id(), 'billing_city', $this->get_billing_city() );
1045
			update_user_meta( $this->get_id(), 'billing_address_1', $this->get_billing_address() );
1046
			update_user_meta( $this->get_id(), 'billing_address_2', $this->get_billing_address_2() );
1047
			update_user_meta( $this->get_id(), 'billing_state', $this->get_billing_state() );
1048
			update_user_meta( $this->get_id(), 'billing_country', $this->get_billing_country() );
1049
			update_user_meta( $this->get_id(), 'shipping_first_name', $this->get_shipping_first_name() );
1050
			update_user_meta( $this->get_id(), 'shipping_last_name', $this->get_shipping_last_name() );
1051
			update_user_meta( $this->get_id(), 'shipping_company', $this->get_shipping_company() );
1052
			update_user_meta( $this->get_id(), 'shipping_postcode', $this->get_shipping_postcode() );
1053
			update_user_meta( $this->get_id(), 'shipping_city', $this->get_shipping_city() );
1054
			update_user_meta( $this->get_id(), 'shipping_address_1', $this->get_shipping_address() );
1055
			update_user_meta( $this->get_id(), 'shipping_address_2', $this->get_shipping_address_2() );
1056
			update_user_meta( $this->get_id(), 'shipping_state', $this->get_shipping_state() );
1057
			update_user_meta( $this->get_id(), 'shipping_country', $this->get_shipping_country() );
1058
			update_user_meta( $this->get_id(), 'paying_customer', $this->get_is_paying_customer() );
1059
			update_user_meta( $this->get_id(), 'last_update',  $this->get_date_modified() );
1060
			update_user_meta( $this->get_id(), 'first_name', $this->get_first_name() );
1061
			update_user_meta( $this->get_id(), 'last_name', $this->get_last_name() );
1062
			wp_update_user( array( 'ID' => $this->get_id(), 'role' => $this->get_role() ) );
1063
			$wp_user = new WP_User( $this->get_id() );
1064
			$this->set_date_created( strtotime( $wp_user->user_registered ) );
1065
			$this->set_date_modified( get_user_meta( $this->get_id(), 'last_update', true ) );
1066
			$this->read_meta_data();
1067
		}
1068
	}
1069
1070
	/**
1071
	 * Callback which flattens post meta (gets the first value).
1072
	 * @param  array $value
1073
	 * @return mixed
1074
	 */
1075
	private function flatten_post_meta( $value ) {
1076
		return is_array( $value ) ? current( $value ) : $value;
1077
	}
1078
1079
	/**
1080
	 * Read a customer from the database.
1081
	 * @since 2.7.0
1082
	 * @param integer $id
1083
	 */
1084
	public function read( $id ) {
1085
		global $wpdb;
1086
1087
		// User object is required.
1088
		if ( ! $id || ! ( $user_object = get_user_by( 'id', $id ) ) || empty( $user_object->ID ) ) {
1089
			$this->set_id( 0 );
1090
			return;
1091
		}
1092
1093
		// Only users on this site should be read.
1094
		if ( is_multisite() && ! is_user_member_of_blog( $id ) ) {
1095
			$this->set_id( 0 );
1096
			return;
1097
		}
1098
1099
		$this->set_id( $user_object->ID );
1100
		$this->set_props( array_map( array( $this, 'flatten_post_meta'), get_user_meta( $id ) ) );
1101
		$this->set_props( array(
1102
			'is_paying_customer' => get_user_meta( $id, 'paying_customer', true ),
1103
			'email'              => $user_object->user_email,
1104
			'username'           => $user_object->user_login,
1105
			'date_created'       => strtotime( $user_object->user_registered ),
1106
			'date_modified'      => get_user_meta( $id, 'last_update', true ),
1107
			'role'               => ! empty ( $user_object->roles[0] ) ? $user_object->roles[0] : 'customer',
1108
		) );
1109
		$this->read_meta_data();
1110
	}
1111
1112
	/**
1113
	 * Update a customer.
1114
	 * @since 2.7.0
1115
	 */
1116
	public function update() {
1117
		wp_update_user( array( 'ID' => $this->get_id(), 'user_email' => $this->get_email() ) );
1118
		// Only update password if a new one was set with set_password
1119
		if ( ! empty( $this->_password ) ) {
1120
			wp_update_user( array( 'ID' => $this->get_id(), 'user_pass' => $this->_password ) );
1121
			$this->_password = '';
1122
		}
1123
1124
		update_user_meta( $this->get_id(), 'billing_first_name', $this->get_billing_first_name() );
1125
		update_user_meta( $this->get_id(), 'billing_last_name', $this->get_billing_last_name() );
1126
		update_user_meta( $this->get_id(), 'billing_company', $this->get_billing_company() );
1127
		update_user_meta( $this->get_id(), 'billing_phone', $this->get_billing_phone() );
1128
		update_user_meta( $this->get_id(), 'billing_email', $this->get_billing_email() );
1129
		update_user_meta( $this->get_id(), 'billing_postcode', $this->get_billing_postcode() );
1130
		update_user_meta( $this->get_id(), 'billing_city', $this->get_billing_city() );
1131
		update_user_meta( $this->get_id(), 'billing_address_1', $this->get_billing_address() );
1132
		update_user_meta( $this->get_id(), 'billing_address_2', $this->get_billing_address_2() );
1133
		update_user_meta( $this->get_id(), 'billing_state', $this->get_billing_state() );
1134
		update_user_meta( $this->get_id(), 'shipping_first_name', $this->get_shipping_first_name() );
1135
		update_user_meta( $this->get_id(), 'shipping_last_name', $this->get_shipping_last_name() );
1136
		update_user_meta( $this->get_id(), 'shipping_company', $this->get_shipping_company() );
1137
		update_user_meta( $this->get_id(), 'billing_country', $this->get_billing_country() );
1138
		update_user_meta( $this->get_id(), 'shipping_first_name', $this->get_shipping_first_name() );
1139
		update_user_meta( $this->get_id(), 'shipping_last_name', $this->get_shipping_last_name() );
1140
		update_user_meta( $this->get_id(), 'shipping_company', $this->get_shipping_company() );
1141
		update_user_meta( $this->get_id(), 'shipping_postcode', $this->get_shipping_postcode() );
1142
		update_user_meta( $this->get_id(), 'shipping_city', $this->get_shipping_city() );
1143
		update_user_meta( $this->get_id(), 'shipping_address_1', $this->get_shipping_address() );
1144
		update_user_meta( $this->get_id(), 'shipping_address_2', $this->get_shipping_address_2() );
1145
		update_user_meta( $this->get_id(), 'shipping_state', $this->get_shipping_state() );
1146
		update_user_meta( $this->get_id(), 'shipping_country', $this->get_shipping_country() );
1147
		update_user_meta( $this->get_id(), 'paying_customer', $this->get_is_paying_customer() );
1148
		update_user_meta( $this->get_id(), 'first_name', $this->get_first_name() );
1149
		update_user_meta( $this->get_id(), 'last_name', $this->get_last_name() );
1150
		wp_update_user( array( 'ID' => $this->get_id(), 'role' => $this->get_role() ) );
1151
		$this->set_date_modified( get_user_meta( $this->get_id(), 'last_update', true ) );
1152
		$this->save_meta_data();
1153
	}
1154
1155
	/**
1156
	 * Delete a customer.
1157
	 * @since 2.7.0
1158
	 */
1159
	public function delete() {
1160
		if ( ! $this->get_id() ) {
1161
			return;
1162
		}
1163
		return wp_delete_user( $this->get_id() );
1164
	}
1165
1166
	/**
1167
	 * Delete a customer and reassign posts..
1168
	 *
1169
	 * @param int $reassign Reassign posts and links to new User ID.
1170
	 * @since 2.7.0
1171
	 */
1172
	public function delete_and_reassign( $reassign = null ) {
1173
		if ( ! $this->get_id() ) {
1174
			return;
1175
		}
1176
		return wp_delete_user( $this->get_id(), $reassign );
1177
	}
1178
1179
	/**
1180
	 * Save data. Create when creating a new user/class, update when editing
1181
	 * an existing user, and save session when working on a logged out guest
1182
	 * session.
1183
	 * @since 2.7.0
1184
	 */
1185
	public function save() {
1186
		if ( $this->_is_session ) {
1187
			$this->save_to_session();
1188
		} elseif ( ! $this->get_id() ) {
1189
			$this->create();
1190
		} else {
1191
			$this->update();
1192
		}
1193
	}
1194
1195
	/**
1196
	 * Saves data to the session only (does not overwrite DB values).
1197
	 * @since 2.7.0
1198
	 */
1199
	public function save_to_session() {
1200
		$data = array();
1201
		foreach ( $this->_session_keys as $session_key ) {
1202
			$function_key = $session_key;
1203
			if ( 'billing_' === substr( $session_key, 0, 8 ) ) {
1204
				$session_key = str_replace( 'billing_', '', $session_key );
1205
			}
1206
			$data[ $session_key ] = $this->{"get_$function_key"}();
1207
		}
1208
		if ( $data !== WC()->session->get( 'customer' ) ) {
1209
			WC()->session->set( 'customer', $data );
1210
		}
1211
	}
1212
1213
	/**
1214
	 * Callback to remove unwanted meta data.
1215
	 *
1216
	 * @param object $meta
1217
	 * @return bool
1218
	 */
1219
	protected function exclude_internal_meta_keys( $meta ) {
1220
		global $wpdb;
1221
		return ! in_array( $meta->meta_key, $this->get_internal_meta_keys() )
1222
			&& 0 !== strpos( $meta->meta_key, 'closedpostboxes_' )
1223
			&& 0 !== strpos( $meta->meta_key, 'metaboxhidden_' )
1224
			&& 0 !== strpos( $meta->meta_key, 'manageedit-' )
1225
			&& ! strstr( $meta->meta_key, $wpdb->prefix );
1226
	}
1227
}
1228