Passed
Push — master ( 17dbd8...fc5da4 )
by Brian
06:03
created

GetPaid_Customer   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 399
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 120
dl 0
loc 399
rs 7.92
c 1
b 0
f 0
wmc 51

17 Methods

Rating   Name   Duplication   Size   Complexity  
A set_purchase_value() 0 2 1
A clear_cache() 0 4 1
A get() 0 11 2
A setup_default_data() 0 37 5
A set_user_id() 0 2 1
A set_email_cc() 0 3 1
A set_status() 0 7 2
B __construct() 0 23 7
B get_customer_id_by() 0 37 7
A set_date_created() 0 10 4
A set_date_modified() 0 10 4
A set() 0 8 2
A set_email() 0 3 2
A migrate_from_user() 0 4 1
A set_purchase_count() 0 2 1
B clone_user() 0 34 7
A save() 0 18 3

How to fix   Complexity   

Complex Class

Complex classes like GetPaid_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.

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 GetPaid_Customer, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Contains the customer class
4
 *
5
 * @since   1.0.15
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * Customer class.
12
 *
13
 * @since 1.0.15
14
 *
15
 */
16
class GetPaid_Customer extends GetPaid_Data {
17
18
	/**
19
	 * Which data store to load.
20
	 *
21
	 * @var string
22
	 */
23
    protected $data_store_name = 'customer';
24
25
    /**
26
	 * This is the name of this object type.
27
	 *
28
	 * @var string
29
	 */
30
	protected $object_type = 'customer';
31
32
	/**
33
	 * Get the customer if ID is passed, otherwise the customer is new and empty.
34
	 *
35
	 * @param int|string|GetPaid_Customer|object $customer customer id, object, or email.
36
	 */
37
	public function __construct( $customer = 0 ) {
38
39
        // Setup default customer data.
40
        $this->setup_default_data();
41
42
		if ( is_numeric( $customer ) ) {
43
			$this->set_id( $customer );
0 ignored issues
show
Bug introduced by
It seems like $customer can also be of type string; however, parameter $id of GetPaid_Data::set_id() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
			$this->set_id( /** @scrutinizer ignore-type */ $customer );
Loading history...
44
		} elseif ( $customer instanceof self ) {
45
			$this->set_id( $customer->get_id() );
46
		} elseif ( is_string( $customer ) && $customer_id = self::get_customer_id_by( $customer, 'email' ) ) {
47
			$this->set_id( $customer_id );
48
		} elseif ( ! empty( $customer->id ) ) {
49
			$this->set_id( $customer->id );
50
		}
51
52
        // Load the datastore.
53
		$this->data_store = GetPaid_Data_Store::load( $this->data_store_name );
54
55
		if ( $this->get_id() > 0 ) {
56
			$this->data_store->read( $this );
57
        }
58
59
        $this->set_object_read( true );
60
	}
61
62
    /**
63
	 * Sets up default customer data.
64
	 */
65
	private function setup_default_data() {
66
67
        $this->data = array(
68
			'user_id'        => 0,
69
			'email'          => '',
70
			'email_cc'       => '',
71
			'status'         => 'active',
72
			'purchase_value' => 0,
73
			'purchase_count' => 0,
74
			'date_created'   => current_time( 'mysql' ),
75
			'date_modified'  => current_time( 'mysql' ),
76
			'uuid'           => wp_generate_uuid4(),
77
		);
78
79
        // Add address fields.
80
		foreach ( array_keys( getpaid_user_address_fields() ) as $field ) {
81
82
            if ( isset( $this->data[ $field ] ) ) {
83
                continue;
84
            }
85
86
            // Country.
87
            if ( 'country' === $field ) {
88
                $this->data[ $field ] = wpinv_get_default_country();
89
                continue;
90
            }
91
92
            // State.
93
            if ( 'state' === $field ) {
94
                $this->data[ $field ] = wpinv_get_default_state();
95
                continue;
96
            }
97
98
			$this->data[ $field ] = '';
99
		}
100
101
        $this->default_data = $this->data;
102
	}
103
104
	/**
105
	 * Given a customer email or user id, it returns a customer id.
106
	 *
107
	 * @static
108
	 * @param string $value
109
	 * @since 1.0.15
110
	 * @return int
111
	 */
112
	public static function get_customer_id_by( $value, $by = 'email' ) {
113
		global $wpdb;
114
115
        // Prepare value.
116
        if ( 'email' === $by ) {
117
            $value = sanitize_email( $value );
118
        } elseif ( 'user_id' === $by ) {
119
            $value = absint( $value );
120
        } else {
121
            return 0;
122
        }
123
124
        if ( empty( $value ) ) {
125
            return 0;
126
        }
127
128
		// Maybe retrieve from the cache.
129
        $cache_key   = 'getpaid_customer_ids_by_' . $by;
130
		$customer_id = wp_cache_get( $value, $cache_key );
131
		if ( false !== $customer_id ) {
132
			return $customer_id;
133
		}
134
135
        if ( 'email' === $by ) {
136
            $customer_id = (int) $wpdb->get_var(
137
                $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE email=%s LIMIT 1", $value )
138
            );
139
        } elseif ( 'user_id' === $by ) {
140
            $customer_id = (int) $wpdb->get_var(
141
                $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE user_id=%d LIMIT 1", $value )
142
            );
143
        }
144
145
		// Update the cache with our data
146
		wp_cache_set( $value, $customer_id, $cache_key );
147
148
		return $customer_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $customer_id could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
149
150
	}
151
152
	/**
153
     * Clears the customer's cache.
154
     */
155
    public function clear_cache() {
156
        wp_cache_delete( $this->get( 'email' ), 'getpaid_customer_ids_by_email' );
157
        wp_cache_delete( $this->get( 'user_id' ), 'getpaid_customer_ids_by_user_id' );
158
		wp_cache_delete( $this->get_id(), 'getpaid_customers' );
159
	}
160
161
	/*
162
	|--------------------------------------------------------------------------
163
	| CRUD methods
164
	|--------------------------------------------------------------------------
165
	|
166
	| Methods which create, read, update and delete discounts from the database.
167
	|
168
    */
169
170
    /*
171
	|--------------------------------------------------------------------------
172
	| Getters
173
	|--------------------------------------------------------------------------
174
	*/
175
176
    /**
177
     * Margic method for retrieving a property.
178
     *
179
     * @param  string $key The key to fetch.
180
     * @param  string $context View or edit context.
181
     */
182
    public function get( $key, $context = 'view' ) {
183
184
        // Maybe strip _wpinv_ prefix from key.
185
        $key = str_replace( '_wpinv_', '', $key );
186
187
        // Check if we have a helper method for that.
188
        if ( method_exists( $this, 'get_' . $key ) ) {
189
            return call_user_func( array( $this, 'get_' . $key ), $context );
190
        }
191
192
		return $this->get_prop( $key, $context );
193
194
    }
195
196
	/*
197
	|--------------------------------------------------------------------------
198
	| Setters
199
	|--------------------------------------------------------------------------
200
	|
201
	| Functions for setting customer data. These should not update anything in the
202
	| database itself and should only change what is stored in the class
203
	| object.
204
	*/
205
206
    /**
207
     * Margic method for setting a property.
208
     *
209
     * @param string $key The key to fetch.
210
     * @param mixed $value The new value.
211
     */
212
    public function set( $key, $value ) {
213
214
        // Check if we have a helper method for that.
215
        if ( method_exists( $this, 'set_' . $key ) ) {
216
            return call_user_func( array( $this, 'set_' . $key ), $value );
217
        }
218
219
		return $this->set_prop( $key, $value );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->set_prop($key, $value) targeting GetPaid_Data::set_prop() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
220
221
    }
222
223
	/**
224
	 * Sets customer status.
225
	 *
226
	 * @since 1.0.0
227
	 * @param  string $status New status.
228
	 */
229
	public function set_status( $status ) {
230
231
		if ( in_array( $status, array( 'active', 'inactive', 'blocked' ), true ) ) {
232
			return $this->set_prop( 'status', $status );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->set_prop('status', $status) targeting GetPaid_Data::set_prop() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
233
		}
234
235
		$this->set_prop( 'status', 'inactive' );
236
	}
237
238
	/**
239
	 * Sets the purchase value.
240
	 *
241
	 * @since 1.0.0
242
	 * @param float $purchase_value.
243
	 */
244
	public function set_purchase_value( $purchase_value ) {
245
		$this->set_prop( 'purchase_value', (float) $purchase_value );
246
	}
247
248
    /**
249
	 * Sets the purchase count.
250
	 *
251
	 * @since 1.0.0
252
	 * @param int $purchase_count.
253
	 */
254
	public function set_purchase_count( $purchase_count ) {
255
		$this->set_prop( 'purchase_count', absint( $purchase_count ) );
256
	}
257
258
    /**
259
	 * Sets the user id.
260
	 *
261
	 * @since 1.0.0
262
	 * @param int $user_id.
263
	 */
264
	public function set_user_id( $user_id ) {
265
		$this->set_prop( 'user_id', absint( $user_id ) );
266
	}
267
268
    /**
269
	 * Sets the email.
270
	 *
271
	 * @since 1.0.0
272
	 * @param string $email.
273
	 */
274
	public function set_email( $email ) {
275
        $email = is_string( $email ) ? sanitize_email( $email ) : '';
276
		$this->set_prop( 'email', $email );
277
	}
278
279
    /**
280
	 * Sets the email cc.
281
	 *
282
	 * @since 1.0.0
283
	 * @param string $email_cc.
284
	 */
285
	public function set_email_cc( $email_cc ) {
286
        $email_cc = implode( ', ', wp_parse_list( $email_cc ) );
287
		$this->set_prop( 'email_cc', $email_cc );
288
	}
289
290
    /**
291
	 * Sets the created date.
292
	 *
293
	 * @since 1.0.0
294
	 * @param  string $date_created date created.
295
	 */
296
	public function set_date_created( $date_created ) {
297
298
		$date = strtotime( $date_created );
299
300
        if ( $date && $date_created !== '0000-00-00 00:00:00'  && $date_created !== '0000-00-00 00:00' ) {
301
            $this->set_prop( 'date_created', gmdate( 'Y-m-d H:i:s', $date ) );
302
            return;
303
		}
304
305
		$this->set_prop( 'date_created', null );
306
	}
307
308
    /**
309
	 * Sets the created date.
310
	 *
311
	 * @since 1.0.0
312
	 * @param  string $date_modified date created.
313
	 */
314
	public function set_date_modified( $date_modified ) {
315
316
		$date = strtotime( $date_modified );
317
318
        if ( $date && $date_modified !== '0000-00-00 00:00:00'  && $date_modified !== '0000-00-00 00:00' ) {
319
            $this->set_prop( 'date_modified', gmdate( 'Y-m-d H:i:s', $date ) );
320
            return;
321
		}
322
323
		$this->set_prop( 'date_modified', null );
324
	}
325
326
	/*
327
	|--------------------------------------------------------------------------
328
	| Additional methods
329
	|--------------------------------------------------------------------------
330
	|
331
	| This method help you manipulate a customer.
332
	|
333
	*/
334
335
	/**
336
	 * Saves the customer.
337
	 *
338
	 * @since 1.0.0
339
	 */
340
	public function save() {
341
342
        $maybe_set = array(
343
            'uuid'         => wp_generate_uuid4(),
344
            'date_created' => current_time( 'mysql' ),
345
        );
346
347
        foreach ( $maybe_set as $key => $value ) {
348
            $current_value = $this->get( $key );
349
350
            if ( empty( $current_value ) ) {
351
                $this->set( $key, $value );
352
            }
353
        }
354
355
        $this->set( 'date_modified', current_time( 'mysql' ) );
356
357
		return parent::save();
358
	}
359
360
    /**
361
	 * Helper method to clone a customer from a user ID.
362
	 *
363
	 * @since 1.0.0
364
	 * @param int $user_id.
365
	 */
366
	public function clone_user( $user_id ) {
367
        $user = get_userdata( $user_id );
368
369
        if ( empty( $user ) ) {
370
            return;
371
        }
372
373
		$this->set_user_id( $user->ID );
374
        $this->set_email( $user->user_email );
375
        $this->set_purchase_value( getpaid_get_user_total_spend( $user->ID ) );
376
        $this->set_purchase_count( getpaid_count_user_invoices( $user->ID ) );
377
        $this->set( 'first_name', $user->first_name );
378
        $this->set( 'last_name', $user->last_name );
379
380
        // Fetch extra data from WC or old GetPaid.
381
        $prefixes = array(
382
            '_wpinv_',
383
            'billing_',
384
            '',
385
        );
386
387
        foreach ( array_keys( getpaid_user_address_fields() ) as $field ) {
388
389
            foreach ( $prefixes as $prefix ) {
390
391
                // Meta table.
392
                $value = get_user_meta( $user_id, $prefix . $field, true );
393
394
                // UWP table.
395
                $value = ( empty( $value ) && function_exists( 'uwp_get_usermeta' ) ) ? uwp_get_usermeta( $user_id, $prefix . $field ) : $value;
396
397
                if ( ! empty( $value ) ) {
398
                    $this->set( $field, $value );
399
                    continue;
400
                }
401
            }
402
		}
403
	}
404
405
    /**
406
	 * Helper method to migrate an existing user ID to the new customers table.
407
	 *
408
	 * @since 1.0.0
409
	 * @param int $user_id.
410
	 */
411
	public function migrate_from_user( $user_id ) {
412
        $this->clone_user( $user_id );
413
        do_action( 'getpaid_customer_migrated_from_user', $this, $user_id );
414
        $this->save();
415
	}
416
}
417