File: /var/www/html/ielts-store/wp-content/plugins/automatewoo/includes/Session_Tracker.php
<?php
// phpcs:ignoreFile
namespace AutomateWoo;
/**
* Tracks logged out customers via cookies.
*
* @class Session_Tracker
*
* @since 4.3.0 Class was essentially rewritten.
*/
class Session_Tracker {
/** @var int (days) */
private static $tracking_cookie_expiry;
/** cookie name */
private static $tracking_key_cookie_name;
/** @var string - This key WILL BE saved */
private static $tracking_key_to_set = '';
/**
* Init session tracker, add hooks.
*/
static function init() {
$self = 'AutomateWoo\Session_Tracker'; /** @var $self Session_Tracker */
if ( ! Options::session_tracking_enabled() ) {
return;
}
self::$tracking_key_cookie_name = apply_filters( 'automatewoo/session_tracker/cookie_name', 'wp_automatewoo_visitor_' . COOKIEHASH );
self::$tracking_cookie_expiry = apply_filters( 'automatewoo_visitor_tracking_cookie_expiry', 365 ); // in days
add_action( 'wp', [ $self, 'maybe_set_session_cookies' ], 99 );
add_action( 'shutdown', [ $self, 'maybe_set_session_cookies' ], 0 );
add_action( 'automatewoo/ajax/before_send_json', [ $self, 'maybe_set_session_cookies' ] );
add_action( 'set_logged_in_cookie', [ $self, 'update_session_on_user_login' ], 10, 4 );
add_action( 'comment_post', [ $self, 'capture_from_comment' ], 10, 2 );
add_action( 'automatewoo_capture_guest_email', [ $self, 'set_session_by_captured_email' ] ); // for third-party
add_action( 'woocommerce_checkout_order_processed', [ $self, 'maybe_track_guest_customer_after_order_placed' ], 20 );
}
/**
* Returns true if a session tracking cookie has been set.
*
* Note: Includes any changes to the cookie in the current request.
*
* @since 4.0
*
* @return bool
*/
static function is_tracking_cookie_set() {
return (bool) Cookies::get( self::$tracking_key_cookie_name );
}
/**
* Returns true if a session tracking cookie has been set.
*
* Note: Includes any changes to the cookie in the current request.
*
* @since 4.2
*
* @return bool
*/
static function is_session_started_cookie_set() {
return (bool) Cookies::get( 'wp_automatewoo_session_started' );
}
/**
* Returns the tracking key as currently stored in the cookie.
*
* @since 4.3
*
* @return string
*/
static function get_tracking_cookie() {
return Clean::string( Cookies::get( self::$tracking_key_cookie_name ) );
}
/**
* This method doesn't actually set the cookie, rather it initiates the cookie setting.
* Cookies are set only on 'wp', 'shutdown' or 'automatewoo/ajax/before_send_json'.
*
* @since 4.3
*
* @param string $tracking_key
*
* @return bool
*/
static function set_tracking_key_to_be_set( $tracking_key ) {
if ( headers_sent() ) {
return false; // cookies can't be set
}
self::$tracking_key_to_set = $tracking_key;
return true;
}
/**
* If session cookies aren't permitted session tracking is basically disabled.
*
* @since 4.0
* @return bool
*/
static function cookies_permitted() {
if ( ! Options::session_tracking_enabled() ) {
return false;
}
$permitted = true;
if ( Options::session_tracking_requires_cookie_consent() ) {
$permitted = false;
$consent_cookie = Options::session_tracking_consent_cookie_name();
// if consent cookie name is set and that cookie exists then permit cookies
if ( $consent_cookie && Cookies::get( $consent_cookie ) ) {
$permitted = true;
}
}
return apply_filters( 'automatewoo/session_tracking/cookies_permitted', $permitted );
}
/**
* Clear session cookies
*
* @since 4.3
*/
static function clear_tracking_cookies() {
self::$tracking_key_to_set = '';
Cookies::clear( self::$tracking_key_cookie_name );
Cookies::clear( 'wp_automatewoo_session_started' );
}
/**
* New browser session initiated
*/
static function new_session_initiated() {
if ( $guest = self::get_current_guest() ) {
$guest->do_check_in();
}
do_action( 'automatewoo_new_session_initiated' );
}
/**
* Sets a new session cookie for the logged in customer.
* Clears any stored guest cart before their cookie key is updated.
*
* @param $logged_in_cookie
* @param $expire
* @param $expiration
* @param int $user_id
*/
static function update_session_on_user_login( $logged_in_cookie, $expire, $expiration, $user_id ) {
$new_customer = Customer_Factory::get_by_user_id( $user_id );
if ( ! $new_customer ) {
return; // $user_id is not always set, as in #48
}
self::maybe_clear_previous_session_customers_cart( $new_customer );
self::set_session_customer( $new_customer );
}
/**
* Attempt to set session tracking cookies.
*
* Doesn't set cookies in the admin area.
*/
static function maybe_set_session_cookies() {
if ( headers_sent() || is_admin() ) {
return;
}
// if cookies are not permitted clear cookies and bail
if ( ! self::cookies_permitted() ) {
self::clear_tracking_cookies();
return;
}
/**
* Tracking cookie needs updating when:
* - it's a new session
* - the cookie doesn't match the logged in user
*/
$cookie_needs_updating = false;
$logged_in_customer = aw_get_logged_in_customer();
if ( ! self::is_session_started_cookie_set() ) {
$cookie_needs_updating = true;
}
if ( $logged_in_customer ) {
self::maybe_clear_previous_session_customers_cart( $logged_in_customer );
if ( $logged_in_customer->get_key() !== self::get_tracking_cookie() ) {
$cookie_needs_updating = true;
}
}
if ( $cookie_needs_updating && $logged_in_customer ) {
self::$tracking_key_to_set = $logged_in_customer->get_key();
}
// Set the tracking cookie if one needs setting
if ( self::$tracking_key_to_set ) {
Cookies::set( self::$tracking_key_cookie_name, self::$tracking_key_to_set, time() + DAY_IN_SECONDS * self::$tracking_cookie_expiry );
self::$tracking_key_to_set = false; // Don't need to set the tracking cookie again
}
// If a tracking cookie is set but no session started cookie, init the session now.
// MUST not set the session cookie until we have a tracking key.
if ( self::is_tracking_cookie_set() && ! self::is_session_started_cookie_set() ) {
// check the tracking is valid before starting a session
if ( $customer = Customer_Factory::get_by_key( self::get_tracking_cookie() ) ) {
Cookies::set( 'wp_automatewoo_session_started', 1 );
self::new_session_initiated();
}
else {
// invalid or legacy session key so clear the cookie
self::clear_tracking_cookies();
}
}
}
/**
* To avoid duplicate carts this method can be used to clear the cart when switching session customers.
*
* $new_customer is the customer that will be set.
* The current session customer is retrieved from the current cookie value.
*
* @param Customer $new_customer
*
* @since 4.3.0
*/
static function maybe_clear_previous_session_customers_cart( $new_customer ) {
if ( $new_customer->get_key() === self::get_tracking_cookie() ) {
return; // don't clear if the new key is the same as the current one
}
if ( $tracked_customer = Customer_Factory::get_by_key( self::get_tracking_cookie() ) ) {
if ( $cart = $tracked_customer->get_cart() ) {
$cart->delete();
}
}
}
/**
* @return string|false
*/
static function get_current_tracking_key() {
if ( ! Options::session_tracking_enabled() ) {
return false;
}
// If a new tracking key will be set in the request, use that in favour of current cookie value
if ( self::$tracking_key_to_set && ! headers_sent() ) {
return self::$tracking_key_to_set;
}
return self::get_tracking_cookie();
}
/**
* Returns the current user ID factoring in any session cookies.
*
* @return int
*/
static function get_detected_user_id() {
if ( is_user_logged_in() ) {
return get_current_user_id();
}
if ( ! Options::session_tracking_enabled() ) {
return 0; // only return the real user id
}
$customer = self::get_session_customer();
if ( $customer && $customer->is_registered() ) {
return $customer->get_user_id();
}
return 0;
}
/**
* Returns the current guest from tracking cookie.
*
* @return Guest|bool
*/
static function get_current_guest() {
if ( ! Options::session_tracking_enabled() ) {
return false;
}
if ( is_user_logged_in() ) {
return false;
}
$customer = self::get_session_customer();
if ( $customer && ! $customer->is_registered() ) {
return $customer->get_guest();
}
return false;
}
/**
* Updates the current session based on the customer's email.
*
* Create the customer for the email if needed and contains logic to handle when a customers email changes.
*
* Cases to handle:
*
* - Registered user is logged in or remembered via cookie = bail
* - Email matches existing customer
* - Cookie customer exists
* - Cookie and matched customer are the same = do nothing
* - Cookie and matched customer are different = cookie must be changed, clear cart from previous key to avoid duplicates
* - No cookie customer = Set new cookie to matched customer key
* - Email is new
* - Cookie customer exists
* - Customer data is locked = create new customer, change cookie, clear cart from previous key to avoid duplicates
* - Customer data is not locked = update customer email
* - No cookie customer = Set new cookie to matched customer key
*
* @param string $new_email
* @param string $language
*
* @return Customer|false
*/
static function set_session_by_captured_email( $new_email, $language = '' ) {
if ( ! is_email( $new_email ) || headers_sent() || ! Options::session_tracking_enabled() ) {
// must have a valid email, be able to set cookies, have session tracking enabled
return false;
}
$new_email = Clean::email( $new_email );
$existing_session_customer = self::get_session_customer(); // existing session customer from cookie
$customer_matching_email = Customer_Factory::get_by_email( $new_email, false ); // important! don't create new customer
$email_is_new = $customer_matching_email === false;
if ( $existing_session_customer && $existing_session_customer->is_registered() ) {
return $existing_session_customer; // bail if a registered user is already being tracked
}
// Check if a customer already exists matching the supplied email
if ( $customer_matching_email ) {
// Is the matched email the same as the customer of the current session?
if ( $existing_session_customer && $new_email === $existing_session_customer->get_email() ) {
// Customer has probably re-entered their email at checkout
}
else {
// Customer has changed so delete the cart for the existing customer
// To avoid duplicate abandoned cart emails
if ( $existing_session_customer ) {
$existing_session_customer->delete_cart();
}
}
// Set the matched customer as the new customer
$new_customer = $customer_matching_email;
}
else {
// Is there an existing session customer
if ( $existing_session_customer ) {
// Check if existing and new emails are the same
// This is actually impossible considering the previous logic but it's probably more confusing to omit this
if ( $existing_session_customer->get_email() === $new_email ) {
// Nothing to do
$new_customer = $existing_session_customer;
}
else {
$guest = $existing_session_customer->get_guest(); // customer can not be a registered user at this point
if ( $guest->is_locked() ) {
// email has changed and guest is locked so we must create a new guest
// first clear the old guests cart, to avoid duplicate abandoned cart emails
$guest->delete_cart();
$new_customer = Customer_Factory::get_by_email( $new_email );
}
else {
// Guest is not locked so we can simply update guest email
$guest->set_email( $new_email );
$guest->save();
// Set the new customer to the existing session customer
$new_customer = $existing_session_customer;
}
}
}
else {
// There is no session customer, so create one
$new_customer = Customer_Factory::get_by_email( $new_email );
}
}
// init the new customer tracking, also saves/updates the language
if ( $new_customer ) {
self::set_session_customer( $new_customer, $language );
}
// the new customer could be a user, if the email address matched a user
if ( $guest = $new_customer->get_guest() ) {
$guest->do_check_in();
}
// update the stored cart
if ( Options::abandoned_cart_enabled() ) {
Carts::update_stored_customer_cart( $new_customer );
}
if ( $email_is_new && $guest ) {
// fire hook after new email is stored
do_action( 'automatewoo/session_tracker/new_stored_guest', $guest );
}
return $new_customer;
}
/**
* Store guest info if they place a comment
* @param $comment_ID
*/
static function capture_from_comment( $comment_ID ) {
if ( is_user_logged_in() ) {
return;
}
$comment = get_comment( $comment_ID );
if ( $comment && ! $comment->user_id ) {
self::set_session_by_captured_email( $comment->comment_author_email );
}
}
/**
* Attempt to set a tracking key for guests when they place an order.
* Otherwise, if presubmit tracking is disabled, guests won't have session tracking.
*
* @since 4.0
* @param int $order_id
*/
static function maybe_track_guest_customer_after_order_placed( $order_id ) {
if ( ! self::cookies_permitted() ) {
return; // cookies blocked
}
$order = wc_get_order( $order_id );
if ( ! $order || is_user_logged_in() || ! $customer = Customer_Factory::get_by_order( $order ) ) {
return;
}
self::set_session_customer( $customer );
}
/**
* Attempts to set the $customer as the current session customer.
* Should only be used before headers are sent.
* Fails silently if session cookies or session tracking is disabled.
*
* Allows the session to be set even if the same customer is already set.
* Doing this will extend the cookie expiry date.
*
* @param Customer $customer
* @param string $language
*
* @since 4.0
*/
static function set_session_customer( $customer, $language = '' ) {
if ( headers_sent() || ! self::cookies_permitted() || ! $customer ) {
return;
}
if ( is_user_logged_in() ) {
return; // session for logged in user will already be set
}
$key = $customer->get_key();
$customer->update_language( $language ? $language : Language::get_current() );
self::set_tracking_key_to_be_set( $key );
}
/**
* Returns the current session customer and takes into account session tracking cookies.
*
* @return Customer|false
*/
static function get_session_customer() {
if ( is_user_logged_in() ) {
return aw_get_logged_in_customer();
}
if ( ! Options::session_tracking_enabled() ) {
return false;
}
// uses the newly set key if it exists and can be set
if ( $cookie_key = self::get_current_tracking_key() ) {
return Customer_Factory::get_by_key( $cookie_key );
}
return false;
}
/**
* Returns true if the supplied $customer arg matches the currently tracked session customer.
*
* @since 4.3.0
*
* @param Customer|int $input_customer
*
* @return bool
*/
static function is_session_customer( $input_customer ) {
if ( ! $session_customer = self::get_session_customer() ) {
return false;
}
if ( is_a( $input_customer, 'AutomateWoo\Customer' ) ) {
return $session_customer->get_id() == $input_customer->get_id();
}
elseif ( is_numeric( $input_customer ) ) {
return $session_customer->get_id() == Clean::id( $input_customer );
}
return false;
}
}