HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: /var/www/html/ielts-store/wp-content/plugins/automatewoo/includes/Customer.php
<?php
// phpcs:ignoreFile

namespace AutomateWoo;

if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * @class Customer
 * @since 3.0.0
 */
class Customer extends Abstract_Model_With_Meta_Table {

	/** @var string */
	public $table_id = 'customers';

	/** @var string  */
	public $object_type = 'customer';

	/**
	 * @param bool|int $id
	 */
	function __construct( $id = false ) {
		if ( $id ) {
			$this->get_by( 'id', $id );
		}
	}

	/**
	 * Returns the ID of the model's meta table.
	 *
	 * @return string
	 */
	public function get_meta_table_id() {
		return 'customer-meta';
	}

	/**
	 * @return int
	 */
	function get_user_id() {
		return (int) $this->get_prop( 'user_id' );
	}


	/**
	 * @param $user_id
	 */
	function set_user_id( $user_id ) {
		$this->set_prop( 'user_id', (int) $user_id );
	}


	/**
	 * @return int
	 */
	function get_guest_id() {
		return (int) $this->get_prop( 'guest_id' );
	}


	/**
	 * @param $guest_id
	 */
	function set_guest_id( $guest_id ) {
		$this->set_prop( 'guest_id', (int) $guest_id );
	}


	/**
	 * Returns a unique key that can ID the customer. This is added to the customer upon creation.
	 *
	 * @return string
	 */
	function get_key() {
		return Clean::string( $this->get_prop( 'id_key' ) );
	}


	/**
	 * @param string $key
	 */
	function set_key( $key ) {
		$this->set_prop( 'id_key', Clean::string( $key ) );
	}


	/**
	 * Generates a new key for registered users that don't have one.
	 *
	 * @deprecated tracking keys are replaced with $this->get_key()
	 *
	 * @since 4.0
	 * @return string
	 */
	function get_tracking_key() {
		wc_deprecated_function( __METHOD__, '5.2.0', 'get_key' );

		return Clean::string( $this->get_linked_prop( 'tracking_key' ) );
	}


	/**
	 * Get the creation date of the customer's last paid order.
	 *
	 * @return bool|DateTime
	 */
	public function get_date_last_purchased() {
		return $this->get_date_column( 'last_purchased' );
	}


	/**
	 * @param DateTime|string $date
	 */
	function set_date_last_purchased( $date ) {
		$this->set_date_column( 'last_purchased', $date );
	}


	/**
	 * Get the creation date of the customer's first paid order.
	 *
	 * @since 4.4
	 *
	 * @return DateTime|bool
	 */
	function get_date_first_purchased() {
		if ( $this->is_registered() ) {
			$first_order = aw_get_customer_first_order( $this->get_user_id() );
		} else {
			$first_order = aw_get_customer_first_order( $this->get_email() );
		}

		if ( $first_order ) {
			return aw_normalize_date( $first_order->get_date_created() );
		}

		return false;
	}

	/**
	 * Takes into account the global optin_mode option.
	 *
	 * If the customer is unsubd then all workflows will still run but any emails sending to
	 * this customer will be rejected and marked in the logs.
	 *
	 * @return string
	 */
	function is_unsubscribed() {
		if ( Options::optin_enabled() ) {
			$is_unsubscribed = ! $this->get_is_subscribed();
		}
		else { // opt-out
			$is_unsubscribed = $this->get_is_unsubscribed();
		}

		return apply_filters( 'automatewoo/customer/is_unsubscribed', $is_unsubscribed, $this );
	}


	/**
	 * @return bool
	 */
	function is_opted_in() {
		return ! $this->is_unsubscribed();
	}


	/**
	 * @return string
	 */
	function is_opted_out() {
		return $this->is_unsubscribed();
	}


	/**
	 * Mark a customer as subscribed
	 */
	function opt_in() {
		if ( $this->get_is_subscribed() ) {
			return; // already subscribed
		}

		$this->set_is_subscribed( true );
		$this->set_date_subscribed( new DateTime() );
		$this->set_is_unsubscribed( false );
		$this->save();
		do_action( 'automatewoo/customer/opted_in', $this );
	}


	/**
	 * Mark a customer as unsubscribed
	 */
	function opt_out() {
		if ( $this->get_is_unsubscribed() ) {
			return; // already unsubscribed
		}

		$this->set_is_unsubscribed( true );
		$this->set_date_unsubscribed( new DateTime() );
		$this->set_is_subscribed( false );
		$this->save();
		do_action( 'automatewoo/customer/opted_out', $this );
	}


	/**
	 * @param bool $unsubscribed
	 */
	function set_is_unsubscribed( $unsubscribed ) {
		$this->set_prop( 'unsubscribed', aw_bool_int( $unsubscribed ) );
	}


	/**
	 * @return bool
	 */
	function get_is_unsubscribed() {
		return (bool) $this->get_prop( 'unsubscribed' );
	}


	/**
	 * @return bool|DateTime
	 */
	function get_date_unsubscribed() {
		return $this->get_date_column( 'unsubscribed_date' );
	}


	/**
	 * @param DateTime|string $date
	 */
	function set_date_unsubscribed( $date ) {
		$this->set_date_column( 'unsubscribed_date', $date );
	}


	/**
	 * @param bool $subscribed
	 */
	function set_is_subscribed( $subscribed ) {
		$this->set_prop( 'subscribed', aw_bool_int( $subscribed ) );
	}


	/**
	 * @return bool
	 */
	function get_is_subscribed() {
		return (bool) $this->get_prop( 'subscribed' );
	}


	/**
	 * @return bool|DateTime
	 */
	function get_date_subscribed() {
		return $this->get_date_column( 'subscribed_date' );
	}


	/**
	 * @param DateTime|string $date
	 */
	function set_date_subscribed( $date ) {
		$this->set_date_column( 'subscribed_date', $date );
	}


	/**
	 * @return Guest|false
	 */
	function get_guest() {
		if ( $this->is_registered() ) {
			return false;
		}
		return Guest_Factory::get( $this->get_guest_id() );
	}


	/**
	 * @return \WP_User
	 */
	function get_user() {
		return get_userdata( $this->get_user_id() );
	}


	/**
	 * @return Cart
	 */
	function get_cart() {
		if ( $this->is_registered() ) {
			return Cart_Factory::get_by_user_id( $this->get_user_id() );
		}
		else {
			return Cart_Factory::get_by_guest_id( $this->get_guest_id() );
		}
	}


	/**
	 * Deletes the customer's stored cart.
	 *
	 * @since 4.3.0
	 */
	function delete_cart() {
		if ( $cart = $this->get_cart() ) {
			$cart->delete();
		}
	}


	/**
	 * @return bool
	 */
	function is_registered() {
		return $this->get_user_id() !== 0;
	}


	/**
	 * @return string
	 */
	function get_email() {
		return Clean::email( $this->get_linked_prop( 'email' ) );
	}


	/**
	 * @return string
	 */
	function get_first_name() {
		return $this->get_linked_prop( 'first_name' );
	}


	/**
	 * @return string
	 */
	function get_last_name() {
		return $this->get_linked_prop( 'last_name' );
	}


	/**
	 * @return string
	 */
	function get_full_name() {
		return trim( sprintf( _x( '%1$s %2$s', 'full name', 'automatewoo' ), $this->get_first_name(), $this->get_last_name() ) );
	}


	/**
	 * @return string
	 */
	function get_billing_country() {
		return $this->get_linked_prop( 'billing_country' );
	}


	/**
	 * @return string
	 */
	function get_billing_state() {
		return $this->get_linked_prop( 'billing_state' );
	}


	/**
	 * @return string
	 */
	function get_billing_phone() {
		return $this->get_linked_prop( 'billing_phone' );
	}


	/**
	 * @return string
	 */
	function get_billing_postcode() {
		return $this->get_linked_prop( 'billing_postcode' );
	}


	/**
	 * @return string
	 */
	function get_billing_city() {
		return $this->get_linked_prop( 'billing_city' );
	}


	/**
	 * @return string
	 */
	function get_billing_address_1() {
		return $this->get_linked_prop( 'billing_address_1' );
	}


	/**
	 * @return string
	 */
	function get_billing_address_2() {
		return $this->get_linked_prop( 'billing_address_2' );
	}


	/**
	 * @return string
	 */
	function get_billing_company() {
		return $this->get_linked_prop( 'billing_company' );
	}


	/**
	 * @param bool $include_name
	 * @return array
	 */
	function get_address( $include_name = true ) {
		$args = [];

		if ( $include_name ) {
			$args['first_name'] = $this->get_first_name();
			$args['last_name'] = $this->get_last_name();
		}

		$args['company'] = $this->get_billing_company();
		$args['address_1'] = $this->get_billing_address_1();
		$args['address_2' ] = $this->get_billing_address_2();
		$args['city'] = $this->get_billing_city();
		$args['state'] = $this->get_billing_state();
		$args['postcode'] = $this->get_billing_postcode();
		$args['country'] = $this->get_billing_country();

		return $args;
	}

	/**
	 * @param bool $include_name
	 * @return string
	 */
	function get_formatted_billing_address( $include_name = true ) {
		return WC()->countries->get_formatted_address( $this->get_address( $include_name ) );
	}


	/**
	 * Get meta value using legacy meta system.
	 *
	 * The legacy meta system stored data in the WP user meta table when the customer was registered
	 * and in the AW guest meta table when the customer was a guest.
	 *
	 * It's worth noting that guest meta does not become user meta when a guest creates an account.
	 *
	 * @param string $key
	 * @return mixed
	 */
	function get_legacy_meta( $key ) {

		if ( ! $key ) return false;

		if ( $this->is_registered() ) {
			return get_user_meta( $this->get_user_id(), $key, true );
		}
		elseif ( $guest = $this->get_guest() ) {
			return $guest->get_meta( $key );
		}
	}

	/**
	 * Update meta value using legacy meta system.
	 *
	 * @see \AutomateWoo\Customer::get_legacy_meta()
	 *
	 * @param string $key
	 * @param $value
	 * @return mixed
	 */
	function update_legacy_meta( $key, $value ) {

		if ( ! $key ) return false;

		if ( $this->is_registered() ) {
			update_user_meta( $this->get_user_id(), $key, $value );
		}
		elseif ( $guest = $this->get_guest() ) {
			$guest->update_meta( $key, $value );
		}
	}


	/**
	 * Get count of customer's orders.
	 *
	 * Includes orders that match user ID OR billing email.
	 *
	 * @return int
	 */
	function get_order_count() {
		$count = $this->get_meta( 'order_count' );

		if ( '' !== $count ) {
			return (int) $count;
		}

		global $wpdb;

		$statuses = array_map( 'esc_sql', aw_get_counted_order_statuses( true ) );

		$query = "
			SELECT COUNT(DISTINCT ID)
			FROM $wpdb->posts as posts
			LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
			WHERE posts.post_type = 'shop_order'
			AND posts.post_status IN ('" . implode( "','", $statuses ) . "')
			AND {$this->get_customer_order_meta_sql()}
		";

		$count = (int) $wpdb->get_var( $wpdb->prepare( $query, $this->get_customer_order_meta_sql_args() ) );

		$this->update_meta( 'order_count', $count );

		return $count;
	}

	/**
	 * Get total spent by the customer.
	 *
	 * Includes orders that match user ID OR billing email.
	 *
	 * @return float
	 */
	function get_total_spent() {
		$total = $this->get_meta( 'total_spent' );

		if ( '' !== $total ) {
			return (float) $total;
		}

		global $wpdb;

		$statuses = array_map( 'aw_add_order_status_prefix', wc_get_is_paid_statuses() );
		$statuses = array_map( 'esc_sql', $statuses );

		$query = "
			SELECT SUM(order_total) FROM (
				SELECT posts.ID as order_id, meta2.meta_value as order_total 
				FROM $wpdb->posts as posts
				LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
				LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id
				WHERE posts.post_type = 'shop_order'
				AND posts.post_status IN ('" . implode( "','", $statuses ) . "')
				AND {$this->get_customer_order_meta_sql()}
				AND meta2.meta_key = '_order_total'
				GROUP BY order_id
			) AS orders_table
		";

		// Use formatting function to round the total
		$total = (float) Format::decimal( $wpdb->get_var( $wpdb->prepare( $query, $this->get_customer_order_meta_sql_args() ) ) );

		$this->update_meta( 'total_spent', $total );

		return $total;
	}

	/**
	 * Get SQL used for customer order meta queries.
	 *
	 * Used to get orders that match user ID OR email.
	 *
	 * @since 4.6.0
	 *
	 * @return string
	 */
	protected function get_customer_order_meta_sql() {
		$sql = '';

		if ( $this->is_registered() ) {
			$sql .= '((';
		}

		$sql .= "( meta.meta_key = '_billing_email' AND meta.meta_value = %s )";

		if ( $this->is_registered() ) {
			$sql .= "OR ( meta.meta_key = '_customer_user' AND meta.meta_value = %s )";
		}

		if ( $this->is_registered() ) {
			$sql .= '))';
		}

		return $sql;
	}

	/**
	 * Get SQL query args used for customer order meta queries.
	 *
	 * @since 4.6.0
	 *
	 * @return array
	 */
	protected function get_customer_order_meta_sql_args() {
		$args = [
			$this->get_email()
		];

		if ( $this->is_registered() ) {
			$args[] = $this->get_user_id();
		}

		return $args;
	}


	/**
	 * @return string
	 */
	function get_role() {
		if ( $this->is_registered() && $user = $this->get_user() ) {
			return current( $user->roles );
		}
		else {
			return 'guest';
		}
	}


	/**
	 * Get the customer's language if site is multilingual.
	 * 
	 * @return string
	 */
	public function get_language() {
		$lang = '';

		if ( Language::is_multilingual() ) {
			if ( $this->is_registered() ) {
				$lang = Language::get_user_language( $this->get_user() );
			} else {
				$lang = Language::get_guest_language( $this->get_guest() );
			}
		}

		return apply_filters( 'automatewoo/customer/get_language', $lang, $this );
	}

	/**
	 * Gets the user registered date, if the user is registered.
	 *
	 * @since 4.4
	 *
	 * @return DateTime|bool
	 */
	function get_date_registered() {
		$user = $this->get_user();

		if ( $user ) {
			// user_registered is saved in UTC
			return aw_normalize_date( $user->user_registered );
		}

		return false;
	}


	/**
	 * No need to save after using this method
	 * @param $language
	 */
	function update_language( $language ) {

		if ( ! Language::is_multilingual() || ! $language ) {
			return;
		}

		if ( $this->is_registered() ) {
			$user_lang = get_user_meta( $this->get_user_id(), '_aw_persistent_language', true );

			if ( $user_lang != $language ) {
				Language::set_user_language( $this->get_user_id(), $language );
			}
		}
		else {
			if ( $guest = $this->get_guest() ) {
				if ( $guest->get_language() != $language ) {
					$guest->set_language( $language );
					$guest->save();
				}
			}
		}
	}


	/**
	 * Get product and variation ids of all the customers purchased products
	 * @return array
	 */
	function get_purchased_products() {
		global $wpdb;

		$transient_name = 'aw_cpp_' . md5( $this->get_id() . \WC_Cache_Helper::get_transient_version( 'orders' ) );
		$products = get_transient( $transient_name );

		if ( $products === false ) {

			$customer_data = [ $this->get_email() ];

			if ( $this->is_registered() ) {
				$customer_data[] = $this->get_user_id();
			}

			$customer_data = array_map( 'esc_sql', array_filter( $customer_data ) );
			$statuses = array_map( 'esc_sql', aw_get_counted_order_statuses( true ) );

			$result = $wpdb->get_col( "
				SELECT im.meta_value FROM {$wpdb->posts} AS p
				INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
				INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
				INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
				WHERE p.post_status IN ( '" . implode( "','", $statuses ) . "' )
				AND pm.meta_key IN ( '_billing_email', '_customer_user' )
				AND im.meta_key IN ( '_product_id', '_variation_id' )
				AND im.meta_value != 0
				AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )
			" );

			$products = array_unique( array_map( 'absint', $result ) );

			set_transient( $transient_name, $result, DAY_IN_SECONDS * 30 );
		}

		return $products;
	}



	/**
	 * @param $prop
	 * @return mixed
	 */
	function get_linked_prop( $prop ) {

		$guest = false;
		$user = false;

		if ( $this->is_registered() ) {
			if ( ! $user = $this->get_user() ) {
				return false;
			}
		}
		else {
			if ( ! $guest = $this->get_guest() ) {
				return false;
			}
		}

		switch ( $prop ) {
			case 'email':
				return $this->is_registered() ? $user->user_email : $guest->get_email();
				break;
			case 'first_name':
				return $this->is_registered() ? $user->first_name : $guest->get_first_name();
				break;
			case 'last_name':
				return $this->is_registered() ? $user->last_name : $guest->get_last_name();
				break;
			case 'billing_country':
				return $this->is_registered() ? $user->billing_country : $guest->get_country();
				break;
			case 'billing_state':
				return $this->is_registered() ? $user->billing_state : $guest->get_state();
				break;
			case 'billing_phone':
				return $this->is_registered() ? $user->billing_phone : $guest->get_phone();
				break;
			case 'billing_company':
				return $this->is_registered() ? $user->billing_company : $guest->get_company();
				break;
			case 'billing_address_1':
				return $this->is_registered() ? $user->billing_address_1 : $guest->get_address_1();
				break;
			case 'billing_address_2':
				return $this->is_registered() ? $user->billing_address_2 : $guest->get_address_2();
				break;
			case 'billing_postcode':
				return $this->is_registered() ? $user->billing_postcode : $guest->get_postcode();
				break;
			case 'billing_city':
				return $this->is_registered() ? $user->billing_city : $guest->get_city();
				break;
			case 'tracking_key':
				if ( $this->is_registered() ) {
					// not every registered user will have a key
					if ( ! $key = get_user_meta( $this->get_user_id(), 'automatewoo_visitor_key', true ) ) {
						$key = aw_generate_key( 32 );
						update_user_meta( $this->get_user_id(), 'automatewoo_visitor_key', $key );
					}
					return $key;
				}
				else {
					// guests are always created with a key
					return $guest->get_key();
				}
				break;
		}

	}

	/**
	 * Get reviews for our customer.
	 *
	 * @since 4.4
	 *
	 * @param array $args Arguments array.
	 *
	 * @return array|int
	 */
	function get_reviews( $args = [] ) {
		$query_args = wp_parse_args( $args, [
			'status' => 'approve',
			'count'  => false,
			'type'   => 'review',
			'parent' => 0,
		] );

		if ( $this->is_registered() ) {
			$query_args['user_id'] = $this->get_user_id();
		} else {
			$query_args['author_email'] = $this->get_email();
		}

		return get_comments( $query_args );
	}

	/**
	 * Get the customer's review count.
	 *
	 * NOTE: This excludes multiple reviews of the same product.
	 *
	 * @return int
	 */
	function get_review_count() {
		$cache_group = 'customer_review_count';

		if ( Cache::exists( $this->get_id(), $cache_group ) ) {
			$count = (int) Cache::get( $this->get_id(), $cache_group );
		} else {
			$count = $this->calculate_unique_product_review_count();
			Cache::set( $this->get_id(), $count, $cache_group );
		}

		return $count;
	}

	/**
	 * Calculate the customer's review count excluding multiple reviews on the same product.
	 *
	 * @since 4.5
	 *
	 * @return int
	 */
	function calculate_unique_product_review_count() {
		global $wpdb;
		$sql = "SELECT COUNT(DISTINCT comment_post_ID) FROM {$wpdb->comments} 
				WHERE comment_parent = 0
				AND comment_approved = 1
				AND comment_type = 'review'
				AND (user_ID = %d OR comment_author_email = %s)";

		return (int) $wpdb->get_var( $wpdb->prepare( $sql, [ $this->get_user_id(), $this->get_email() ] ) );
	}

	/**
	 * Clear customer review count cache.
	 *
	 * @since 4.5
	 */
	function clear_review_count_cache() {
		Cache::delete( $this->get_id(), 'customer_review_count' );
	}

	/**
	 * Gets the last review date for the user.
	 *
	 * @since 4.4
	 *
	 * @return DateTime|bool
	 */
	function get_last_review_date() {
		$cache_key         = "customer_last_review_date";
		$last_comment_date = false;

		if ( Temporary_Data::exists( $cache_key, $this->get_id() ) ) {
			$last_comment_date = Temporary_Data::get( $cache_key, $this->get_id() );
		} else {
			$comments = $this->get_reviews( [ 'number' => 1 ] );

			if ( ! empty( $comments ) ) {
				$last_comment_date = $comments[0]->comment_date_gmt;
				Temporary_Data::set( $cache_key, $this->get_id(), $last_comment_date );
			}
		}

		if ( ! $last_comment_date ) {
			return false;
		}

		return new DateTime( $last_comment_date );
	}

	/**
	 * Get the date that a workflow last run for the customer.
	 *
	 * @since 4.4
	 *
	 * @param int|array|Workflow $workflow Workflow object, ID or array of IDs.
	 *
	 * @return DateTime|bool
	 */
	function get_workflow_last_run_date( $workflow ) {
		if ( ! $workflow ) {
			return false;
		}

		$query = new Log_Query();
		$query->where_workflow( $workflow );
		$query->where_customer_or_legacy_user( $this, true );
		$query->set_limit( 1 );
		$query->set_ordering( 'date', 'DESC' );
		$results = $query->get_results();

		if ( $results ) {
			return current( $results )->get_date();
		}

		return false;
	}

	/**
	 * Get customer's nth last paid order.
	 *
	 * @param int $n
	 *
	 * @return \WC_Order|bool
	 *
	 * @since 4.8.0
	 */
	public function get_nth_last_paid_order( $n ) {
		$query_args = [
			'type'    => 'shop_order',
			'limit'   => 1,
			'offset'  => $n - 1,
			'status'  => wc_get_is_paid_statuses(),
			'orderby' => 'date',
			'order'   => 'DESC',
		];

		if ( $this->is_registered() ) {
			$query_args['customer'] = [ $this->get_user_id(), $this->get_email() ];
		} else {
			$query_args['customer'] = $this->get_email();
		}

		return current( wc_get_orders( $query_args ) );
	}

}