PluginProbe
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.4.2
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.4.2
4.7.2 4.7.1 4.7.0 4.6.6 4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 All 281 releases
surecart / app / src / Models / User.php
User.php
710 lines 15.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace SureCart\Models;
3
4 use ArrayAccess;
5 use JsonSerializable;
6 use SureCart\Models\Traits\SyncsCustomer;
7 use WP_Error;
8
9 /**
10 * User class.
11 */
12 class User implements ArrayAccess, JsonSerializable {
13 use SyncsCustomer;
14
15 /**
16 * Holds the user.
17 *
18 * @var \WP_User|null;
19 */
20 protected $user;
21
22 /**
23 * Holds the customer
24 *
25 * @var \SureCart\Models\Customer;
26 */
27 protected $customer;
28
29 /**
30 * Stores the customer id key.
31 *
32 * @var string
33 */
34 protected $customer_id_key = 'sc_customer_ids';
35
36 /**
37 * Get the customer meta key.
38 *
39 * @return string
40 */
41 protected function getCustomerMetaKey() {
42 return $this->customer_id_key;
43 }
44
45 /**
46 * Get the user's customer id.
47 *
48 * @param string $mode Customer mode.
49 *
50 * @return int|null
51 */
52 protected function customerId( $mode = 'live' ) {
53 if ( empty( $this->user->ID ) ) {
54 return null;
55 }
56 $meta = (array) get_user_meta( $this->user->ID, $this->customer_id_key, true );
57 if ( isset( $meta[ $mode ] ) ) {
58 return $meta[ $mode ];
59 }
60 if ( isset( $meta[0] ) ) {
61 return $meta[0];
62 }
63 return null;
64 }
65
66 /**
67 * Sync the customer ids.
68 *
69 * @return array Array of synced items.
70 */
71 protected function syncCustomerIds() {
72 // syncing disabled.
73 if ( ! $this->shouldSyncCustomer() ) {
74 return false;
75 }
76
77 // get all customers by email address (live and test).
78 $customers = Customer::where(
79 [
80 'email' => strtolower( $this->user->user_email ),
81 ]
82 )->get();
83
84 if ( is_wp_error( $customers ) ) {
85 return $customers;
86 }
87
88 // we have customers.
89 $live_customer = current(
90 array_filter(
91 $customers,
92 function ( $customer ) {
93 return $customer->live_mode;
94 }
95 )
96 );
97
98 $test_customer = current(
99 array_filter(
100 $customers,
101 function ( $customer ) {
102 return ! $customer->live_mode;
103 }
104 )
105 );
106
107 if ( empty( $live_customer->id ) ) {
108 $live_customer = Customer::create(
109 [
110 'name' => $this->user->display_name,
111 'email' => strtolower( $this->user->user_email ),
112 'live_mode' => true,
113 ],
114 false // don't create a user.
115 );
116 }
117 if ( empty( $test_customer->id ) ) {
118 $test_customer = Customer::create(
119 [
120 'name' => $this->user->display_name,
121 'email' => strtolower( $this->user->user_email ),
122 'live_mode' => false,
123 ],
124 false // don't create a user.
125 );
126 }
127
128 if ( ! empty( $live_customer->id ) ) {
129 $this->setCustomerId( $live_customer->id, 'live' );
130 }
131 if ( ! empty( $test_customer->id ) ) {
132 $this->setCustomerId( $test_customer->id, 'test' );
133 }
134
135 return [
136 'live' => $this->customerId( 'live' ),
137 'test' => $this->customerId( 'test' ),
138 ];
139 }
140
141 /**
142 * List the users customer ids.
143 *
144 * @return array
145 */
146 protected function customerIds() {
147 if ( empty( $this->user->ID ) ) {
148 return [];
149 }
150 return array_filter( (array) get_user_meta( $this->user->ID, $this->customer_id_key, true ) );
151 }
152
153 /**
154 * Is this user a customer?
155 *
156 * @return boolean
157 */
158 protected function isCustomer() {
159 return ! empty( array_filter( (array) $this->customerIds() ) );
160 }
161
162 /**
163 * Does the user have this customer id?
164 *
165 * @param string $id The customer id.
166 *
167 * @return boolean
168 */
169 protected function hasCustomerId( $id ) {
170 foreach ( (array) $this->customerIds() as $saved_id ) {
171 if ( $saved_id === $id ) {
172 return true;
173 }
174 }
175 return false;
176 }
177
178 /**
179 * Set the customer id in the user meta.
180 *
181 * @param string $id Customer id.
182 * @return $this|bool
183 */
184 protected function setCustomerId( $id, $mode = 'live', $force = false ) {
185 $meta = (array) get_user_meta( $this->user->ID, $this->customer_id_key, true );
186
187 // if we are setting something here.
188 if ( ! empty( $id ) ) {
189 // if they already have one set for this mode.
190 if ( ! empty( $meta[ $mode ] ) ) {
191 // if we have not passed force = true.
192 if ( ! $force ) {
193 return new \WP_Error( 'already_linked', __( 'This user is already linked to a customer.', 'surecart' ) );
194 }
195 }
196 }
197
198 // update id.
199 $meta[ $mode ] = $id;
200 // update meta.
201 update_user_meta( $this->user->ID, $this->customer_id_key, $meta );
202 return $this;
203 }
204
205 /**
206 * Remove the the customer id from the user meta.
207 *
208 * @param string $mode Customer mode.
209 * @return $this
210 */
211 protected function removeCustomerId( $mode = 'live' ) {
212 $meta = (array) get_user_meta( $this->user->ID, $this->customer_id_key, true );
213
214 // if we are setting something here.
215 if ( ! empty( $meta[ $mode ] ) ) {
216 // set mode empty.
217 unset( $meta[ $mode ] );
218 }
219
220 // update meta.
221 update_user_meta( $this->user->ID, $this->customer_id_key, $meta );
222
223 // return this.
224 return $this;
225 }
226
227 /**
228 * Find an existing live customer by email and link it to this user.
229 *
230 * @return string|null|WP_Error Customer ID if found and linked, null if not found, WP_Error on failure.
231 */
232 protected function findAndLinkLiveCustomerByEmail() {
233 $email = strtolower( $this->user->user_email );
234
235 // Try to find an existing customer by email.
236 $existing_customer = Customer::where(
237 [
238 'email' => $email,
239 'live_mode' => true,
240 ]
241 )->first();
242
243 // No customer found or error occurred.
244 if ( is_wp_error( $existing_customer ) || empty( $existing_customer->id ) ) {
245 return null;
246 }
247
248 // Link the existing customer to this user.
249 $linked = $this->setCustomerId( $existing_customer->id, 'live' );
250 if ( is_wp_error( $linked ) ) {
251 return $linked;
252 }
253
254 return $existing_customer->id;
255 }
256
257 /**
258 * Create a new live customer and link it to this user.
259 *
260 * @return string|WP_Error Customer ID on success, WP_Error on failure.
261 */
262 protected function createAndLinkLiveCustomer() {
263 $email = strtolower( $this->user->user_email );
264
265 $customer = Customer::create(
266 [
267 'name' => $this->user->display_name,
268 'email' => $email,
269 'live_mode' => true,
270 ],
271 false
272 );
273
274 if ( is_wp_error( $customer ) ) {
275 return $customer;
276 }
277
278 $customer_id = $customer->id ?? null;
279 if ( empty( $customer_id ) ) {
280 return new WP_Error(
281 'sc_no_customer_created',
282 __( 'Sorry, no customer is being created, please contact with admin.', 'surecart' )
283 );
284 }
285
286 $linked = $this->setCustomerId( $customer_id, 'live' );
287 if ( is_wp_error( $linked ) ) {
288 return $linked;
289 }
290
291 return $customer_id;
292 }
293
294 /**
295 * Get or create the live customer id for this user.
296 *
297 * This method first checks for an existing linked customer ID,
298 * then tries to find and link an existing customer by email,
299 * and finally creates a new customer if none is found.
300 *
301 * @return string|null|WP_Error Customer ID on success, WP_Error on failure.
302 */
303 protected function getOrCreateLiveCustomerId() {
304 // Check for already linked customer.
305 $customer_id = $this->customerId( 'live' );
306 if ( ! empty( $customer_id ) ) {
307 return $customer_id;
308 }
309
310 // Try to find and link existing customer by email.
311 $found_customer_id = $this->findAndLinkLiveCustomerByEmail();
312 if ( is_wp_error( $found_customer_id ) ) {
313 return $found_customer_id;
314 }
315 if ( ! empty( $found_customer_id ) ) {
316 return $found_customer_id;
317 }
318
319 // No existing customer found, create a new one.
320 return $this->createAndLinkLiveCustomer();
321 }
322
323 /**
324 * Disallow overriding the constructor in child classes and make the code safe that way.
325 */
326 final public function __construct() {
327 }
328
329 /**
330 * Get a user's subscriptions
331 *
332 * @return mixed
333 */
334 protected function subscriptions() {
335 return Subscription::where( [ 'customer_ids' => [ $this->customerId() ] ] );
336 }
337
338 /**
339 * Get a users orders
340 *
341 * @param array $query Query args.
342 * @return SureCart\Models\Order[];
343 */
344 protected function orders() {
345 return Order::where( [ 'customer_ids' => [ $this->customerId() ] ] );
346 }
347
348 /**
349 * Login the user.
350 *
351 * @return true|\WP_Error
352 */
353 protected function login() {
354 if ( empty( $this->user->ID ) ) {
355 return new WP_Error( 'not_found', esc_html__( 'This user could not be found.', 'surecart' ) );
356 }
357
358 clean_user_cache( $this->user->ID );
359 if ( class_exists( \WP_Session_Tokens::class ) ) {
360 \WP_Session_Tokens::get_instance( $this->user->ID )->destroy_all();
361 }
362 wp_set_current_user( $this->user->ID );
363
364 // Inject the fresh cookie into $_COOKIE so wp_create_nonce() in this same
365 // request uses the real session token — otherwise the returned nonce fails
366 // on the next REST call (rest_cookie_invalid_nonce 403).
367 $populate_cookie = function ( $logged_in_cookie ) {
368 $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
369 };
370 add_action( 'set_logged_in_cookie', $populate_cookie );
371 wp_set_auth_cookie( $this->user->ID );
372 remove_action( 'set_logged_in_cookie', $populate_cookie );
373
374 update_user_caches( $this->user );
375
376 return true;
377 }
378
379 /**
380 * Create a new user and return this model context
381 * If the user already exists, just set the customer and role in that case.
382 */
383 protected function create( $args ) {
384 $args = wp_parse_args(
385 $args,
386 [
387 'user_name' => '',
388 'user_email' => '',
389 'user_password' => '',
390 'first_name' => '',
391 'last_name' => '',
392 ]
393 );
394
395 // use the username or the email as a fallback.
396 $name = ! empty( sanitize_user( $args['user_name'], true ) ) ? sanitize_user( $args['user_name'], true ) : $args['user_email'];
397 $username = $this->createUniqueUsername( sanitize_user( $name, true ) );
398
399 $user_password = trim( $args['user_password'] );
400 $user_created = false;
401
402 // password is not provided.
403 if ( empty( $user_password ) ) {
404 $user_password = wp_generate_password( 12, false );
405 $user_id = wp_create_user( sanitize_user( $username, true ), $user_password, $args['user_email'] );
406 // turn off this feature with a filter.
407 if ( apply_filters( 'surecart/default_password_nag', true, $user_id ) ) {
408 update_user_meta( $user_id, 'default_password_nag', true );
409 }
410 $user_created = true;
411 } else {
412 // Password has been provided.
413 $user_id = wp_create_user( sanitize_user( $username, true ), $user_password, $args['user_email'] );
414 $user_created = true;
415 }
416
417 if ( is_wp_error( $user_id ) ) {
418 return $user_id;
419 }
420
421 $user = new \WP_User( $user_id );
422 $user->add_role( 'sc_customer' );
423
424 if ( $args['first_name'] ) {
425 $user->first_name = $args['first_name'];
426 }
427 if ( $args['last_name'] ) {
428 $user->last_name = $args['last_name'];
429 }
430
431 if ( $user_created ) {
432 wp_update_user( $user );
433 }
434
435 return $this->find( $user_id );
436 }
437
438 /**
439 * Create a unique username for the user.
440 *
441 * @param string $name The username.
442 *
443 * @return string
444 */
445 protected function createUniqueUsername( $name ) {
446 $base_name = $name;
447 $i = 0;
448 while ( username_exists( $name ) ) {
449 $name = $base_name . ( ++$i );
450 }
451 return $name;
452 }
453
454 /**
455 * Retrieve user info by a given field
456 *
457 * @param string $field The field to retrieve the user with. id | ID | slug | email | login.
458 * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
459 * @return this|false This object on success, false on failure.
460 */
461 protected function getUserBy( $field, $value ) {
462 $this->user = get_user_by( $field, $value ?? '' );
463 return $this->user ? $this : false;
464 }
465
466 /**
467 * Get the customer from the user.
468 *
469 * @param string $mode Customer mode.
470 * @param array $with With array.
471 *
472 * @return \SureCart\Models\Customer|false
473 */
474 protected function customer( $mode = 'live', $with = [] ) {
475 $id = $this->customerId( $mode );
476 if ( ! $id ) {
477 return false;
478 }
479 return Customer::with( $with )->find( $this->customerId( $mode ) );
480 }
481
482 /**
483 * Get the current user
484 *
485 * @return $this
486 */
487 protected function current() {
488 $this->user = wp_get_current_user();
489 return $this;
490 }
491
492 /**
493 * Find the user.
494 *
495 * @param integer $id ID of the WordPress user.
496 * @return $this
497 */
498 protected function find( $id ) {
499 $this->user = get_user_by( 'id', $id );
500 return $this;
501 }
502
503 /**
504 * Find the user by customer id.
505 *
506 * @param string $id Customer id string.
507 * @return $this
508 */
509 protected function findByCustomerId( $id ) {
510 if ( ! is_string( $id ) || empty( $id ) ) {
511 return false;
512 }
513
514 $users = new \WP_User_Query(
515 [
516 'meta_query' => [
517 [
518 'key' => $this->customer_id_key,
519 'value' => $id,
520 'compare' => 'LIKE',
521 ],
522 ],
523 ]
524 );
525
526 if ( empty( $users->results ) ) {
527 return false;
528 }
529
530 $this->user = $users->results[0];
531 return $this;
532 }
533
534 /**
535 * Get the WordPress user.
536 *
537 * @return \WP_User|null;
538 */
539 public function getWPUser() {
540 return $this->user;
541 }
542
543 /**
544 * Get a specific attribute
545 *
546 * @param string $key Attribute name.
547 *
548 * @return mixed
549 */
550 public function getAttribute( $key ) {
551 $attribute = null;
552
553 if ( $this->hasAttribute( $key ) ) {
554 $attribute = $this->user->$key;
555 }
556
557 return $attribute;
558 }
559
560 /**
561 * Sets a user attribute
562 * Optionally calls a mutator based on set{Attribute}Attribute
563 *
564 * @param string $key Attribute key.
565 * @param mixed $value Attribute value.
566 *
567 * @return mixed|void
568 */
569 public function setAttribute( $key, $value ) {
570 $this->user->$key = $value;
571 }
572
573 /**
574 * Does it have the attribute
575 *
576 * @param string $key Attribute key.
577 *
578 * @return boolean
579 */
580 public function hasAttribute( $key ) {
581 return $this->user->$key ?? false;
582 }
583
584 /**
585 * Serialize to json.
586 *
587 * @return Array
588 */
589 #[\ReturnTypeWillChange]
590 public function jsonSerialize() {
591 return $this->user->to_array();
592 }
593
594 /**
595 * Get the attribute
596 *
597 * @param string $key Attribute name.
598 *
599 * @return mixed
600 */
601 public function __get( $key ) {
602 return $this->getAttribute( $key );
603 }
604
605 /**
606 * Set the attribute
607 *
608 * @param string $key Attribute name.
609 * @param mixed $value Value of attribute.
610 *
611 * @return void
612 */
613 public function __set( $key, $value ) {
614 $this->setAttribute( $key, $value );
615 }
616
617 /**
618 * Determine if the given attribute exists.
619 *
620 * @param mixed $offset Name.
621 * @return bool
622 */
623 public function offsetExists( $offset ): bool {
624 return ! is_null( $this->getAttribute( $offset ) );
625 }
626
627 /**
628 * Get the value for a given offset.
629 *
630 * @param mixed $offset Name.
631 * @return mixed
632 */
633 #[\ReturnTypeWillChange]
634 public function offsetGet( $offset ) {
635 return $this->getAttribute( $offset );
636 }
637
638 /**
639 * Set the value for a given offset.
640 *
641 * @param mixed $offset Name.
642 * @param mixed $value Value.
643 * @return void
644 */
645 public function offsetSet( $offset, $value ): void {
646 $this->setAttribute( $offset, $value );
647 }
648
649 /**
650 * Unset the value for a given offset.
651 *
652 * @param mixed $offset Name.
653 * @return void
654 */
655 public function offsetUnset( $offset ): void {
656 $this->user->$offset = null;
657 }
658
659 /**
660 * Get the user object.
661 *
662 * @return \WP_User|null
663 */
664 public function getUser() {
665 return $this->user;
666 }
667
668 /**
669 * Determine if an attribute or relation exists on the model.
670 *
671 * @param string $key Name.
672 * @return bool
673 */
674 public function __isset( $key ) {
675 return $this->offsetExists( $key );
676 }
677
678 /**
679 * Unset an attribute on the model.
680 *
681 * @param string $key Name.
682 * @return void
683 */
684 public function __unset( $key ) {
685 $this->offsetUnset( $key );
686 }
687
688 /**
689 * Forward call to method
690 *
691 * @param string $method Method to call.
692 * @param mixed $params Method params.
693 */
694 public function __call( $method, $params ) {
695 return call_user_func_array( [ $this, $method ], $params );
696 }
697
698 /**
699 * Static Facade Accessor
700 *
701 * @param string $method Method to call.
702 * @param mixed $params Method params.
703 *
704 * @return mixed
705 */
706 public static function __callStatic( $method, $params ) {
707 return call_user_func_array( [ new static(), $method ], $params );
708 }
709 }
710