2017-08-18 09:54:41 +00:00
< ? php
/**
* Cart session handling class .
*
* @ package WooCommerce / Classes
2018-03-16 17:45:28 +00:00
* @ version 3.2 . 0
2017-08-18 09:54:41 +00:00
*/
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* WC_Cart_Session class .
*
* @ since 3.2 . 0
*/
final class WC_Cart_Session {
/**
* Reference to cart object .
*
* @ since 3.2 . 0
2017-09-27 16:12:45 +00:00
* @ var WC_Cart
2017-08-18 09:54:41 +00:00
*/
protected $cart ;
/**
* Sets up the items provided , and calculate totals .
*
* @ since 3.2 . 0
2017-09-27 16:12:45 +00:00
* @ throws Exception If missing WC_Cart object .
2019-01-21 15:54:49 +00:00
*
2017-09-27 16:16:33 +00:00
* @ param WC_Cart $cart Cart object to calculate totals for .
2017-08-18 09:54:41 +00:00
*/
2017-09-27 16:12:45 +00:00
public function __construct ( & $cart ) {
if ( ! is_a ( $cart , 'WC_Cart' ) ) {
throw new Exception ( 'A valid WC_Cart object is required' );
}
2017-08-18 09:54:41 +00:00
$this -> cart = $cart ;
2017-11-03 19:49:45 +00:00
}
2017-08-18 09:54:41 +00:00
2017-11-03 19:49:45 +00:00
/**
* Register methods for this object on the appropriate WordPress hooks .
*/
2017-11-08 15:17:52 +00:00
public function init () {
2017-08-18 09:54:41 +00:00
add_action ( 'wp_loaded' , array ( $this , 'get_cart_from_session' ) );
add_action ( 'woocommerce_cart_emptied' , array ( $this , 'destroy_cart_session' ) );
add_action ( 'woocommerce_after_calculate_totals' , array ( $this , 'set_session' ) );
add_action ( 'woocommerce_cart_loaded_from_session' , array ( $this , 'set_session' ) );
add_action ( 'woocommerce_removed_coupon' , array ( $this , 'set_session' ) );
add_action ( 'woocommerce_cart_updated' , array ( $this , 'persistent_cart_update' ) );
2019-02-05 13:03:16 +00:00
// Cookie events - cart cookies need to be set before headers are sent.
2019-04-17 22:50:42 +00:00
add_action ( 'woocommerce_add_to_cart' , array ( $this , 'maybe_set_cart_cookies' ) );
add_action ( 'wp' , array ( $this , 'maybe_set_cart_cookies' ), 99 );
add_action ( 'shutdown' , array ( $this , 'maybe_set_cart_cookies' ), 0 );
2017-08-18 09:54:41 +00:00
}
/**
* Get the cart data from the PHP session and store it in class variables .
*
* @ since 3.2 . 0
*/
public function get_cart_from_session () {
2018-10-30 09:58:39 +00:00
do_action ( 'woocommerce_load_cart_from_session' );
2018-06-22 12:32:45 +00:00
$this -> cart -> set_totals ( WC () -> session -> get ( 'cart_totals' , null ) );
2017-08-18 09:54:41 +00:00
$this -> cart -> set_applied_coupons ( WC () -> session -> get ( 'applied_coupons' , array () ) );
$this -> cart -> set_coupon_discount_totals ( WC () -> session -> get ( 'coupon_discount_totals' , array () ) );
$this -> cart -> set_coupon_discount_tax_totals ( WC () -> session -> get ( 'coupon_discount_tax_totals' , array () ) );
$this -> cart -> set_removed_cart_contents ( WC () -> session -> get ( 'removed_cart_contents' , array () ) );
2018-06-22 12:32:45 +00:00
$update_cart_session = false ; // Flag to indicate the stored cart should be updated.
2018-09-26 07:39:48 +00:00
$order_again = false ; // Flag to indicate whether this is a re-order.
2018-06-22 12:32:45 +00:00
$cart = WC () -> session -> get ( 'cart' , null );
$merge_saved_cart = ( bool ) get_user_meta ( get_current_user_id (), '_woocommerce_load_saved_cart_after_login' , true );
2018-02-19 16:36:26 +00:00
2018-06-22 12:32:45 +00:00
// Merge saved cart with current cart.
if ( is_null ( $cart ) || $merge_saved_cart ) {
$saved_cart = $this -> get_saved_cart ();
$cart = is_null ( $cart ) ? array () : $cart ;
$cart = array_merge ( $saved_cart , $cart );
2017-12-05 06:45:52 +00:00
$update_cart_session = true ;
2018-06-22 12:32:45 +00:00
delete_user_meta ( get_current_user_id (), '_woocommerce_load_saved_cart_after_login' );
}
// Populate cart from order.
if ( isset ( $_GET [ 'order_again' ], $_GET [ '_wpnonce' ] ) && is_user_logged_in () && wp_verify_nonce ( wp_unslash ( $_GET [ '_wpnonce' ] ), 'woocommerce-order_again' ) ) { // WPCS: input var ok, sanitization ok.
2018-09-26 07:39:48 +00:00
$cart = $this -> populate_cart_from_order ( absint ( $_GET [ 'order_again' ] ), $cart ); // WPCS: input var ok.
$order_again = true ;
2018-06-22 12:32:45 +00:00
}
// Prime caches to reduce future queries.
if ( is_callable ( '_prime_post_caches' ) ) {
_prime_post_caches ( wp_list_pluck ( $cart , 'product_id' ) );
2017-08-18 09:54:41 +00:00
}
2018-06-22 12:32:45 +00:00
$cart_contents = array ();
foreach ( $cart as $key => $values ) {
2019-01-22 16:01:17 +00:00
if ( ! is_customize_preview () && 'customize-preview' === $key ) {
continue ;
}
2018-06-22 12:32:45 +00:00
$product = wc_get_product ( $values [ 'variation_id' ] ? $values [ 'variation_id' ] : $values [ 'product_id' ] );
2017-08-18 09:54:41 +00:00
2019-01-22 16:01:17 +00:00
if ( empty ( $product ) || ! $product -> exists () || 0 >= $values [ 'quantity' ] ) {
2018-06-22 12:32:45 +00:00
continue ;
2018-05-01 11:39:18 +00:00
}
2017-08-18 09:54:41 +00:00
2019-01-22 16:01:17 +00:00
/**
* Allow 3 rd parties to validate this item before it ' s added to cart and add their own notices .
*
* @ since 3.6 . 0
*
* @ param bool $remove_cart_item_from_session If true , the item will not be added to the cart . Default : false .
* @ param string $key Cart item key .
* @ param array $values Cart item values e . g . quantity and product_id .
*/
if ( apply_filters ( 'woocommerce_pre_remove_cart_item_from_session' , false , $key , $values ) ) {
$update_cart_session = true ;
do_action ( 'woocommerce_remove_cart_item_from_session' , $key , $values );
} elseif ( ! $product -> is_purchasable () ) {
$update_cart_session = true ;
/* translators: %s: product name */
wc_add_notice ( sprintf ( __ ( '%s has been removed from your cart because it can no longer be purchased. Please contact us if you need assistance.' , 'woocommerce' ), $product -> get_name () ), 'error' );
do_action ( 'woocommerce_remove_cart_item_from_session' , $key , $values );
} elseif ( ! empty ( $values [ 'data_hash' ] ) && ! hash_equals ( $values [ 'data_hash' ], wc_get_cart_item_data_hash ( $product ) ) ) { // phpcs:ignore PHPCompatibility.PHP.NewFunctions.hash_equalsFound
$update_cart_session = true ;
/* translators: %1$s: product name. %2$s product permalink */
wc_add_notice ( sprintf ( __ ( '%1$s has been removed from your cart because it has since been modified. You can add it back to your cart <a href="%2$s">here</a>.' , 'woocommerce' ), $product -> get_name (), $product -> get_permalink () ), 'notice' );
do_action ( 'woocommerce_remove_cart_item_from_session' , $key , $values );
} else {
// Put session data into array. Run through filter so other plugins can load their own session data.
$session_data = array_merge (
$values ,
array (
'data' => $product ,
)
);
$cart_contents [ $key ] = apply_filters ( 'woocommerce_get_cart_item_from_session' , $session_data , $values , $key );
// Add to cart right away so the product is visible in woocommerce_get_cart_item_from_session hook.
$this -> cart -> set_cart_contents ( $cart_contents );
2017-08-18 09:54:41 +00:00
}
}
2018-08-14 19:30:02 +00:00
// If it's not empty, it's been already populated by the loop above.
2018-08-14 16:34:52 +00:00
if ( ! empty ( $cart_contents ) ) {
$this -> cart -> set_cart_contents ( apply_filters ( 'woocommerce_cart_contents_changed' , $cart_contents ) );
}
2017-08-18 09:54:41 +00:00
do_action ( 'woocommerce_cart_loaded_from_session' , $this -> cart );
2017-11-07 20:10:13 +00:00
if ( $update_cart_session || is_null ( WC () -> session -> get ( 'cart_totals' , null ) ) ) {
2017-08-18 09:54:41 +00:00
WC () -> session -> set ( 'cart' , $this -> get_cart_for_session () );
$this -> cart -> calculate_totals ();
}
2018-09-26 07:39:48 +00:00
// If this is a re-order, redirect to the cart page to get rid of the `order_again` query string.
if ( $order_again ) {
2019-01-03 15:30:21 +00:00
wp_safe_redirect ( wc_get_page_permalink ( 'cart' ) );
2018-09-26 07:39:48 +00:00
exit ;
}
2017-08-18 09:54:41 +00:00
}
/**
* Destroy cart session data .
*
* @ since 3.2 . 0
*/
public function destroy_cart_session () {
WC () -> session -> set ( 'cart' , null );
WC () -> session -> set ( 'cart_totals' , null );
WC () -> session -> set ( 'applied_coupons' , null );
WC () -> session -> set ( 'coupon_discount_totals' , null );
WC () -> session -> set ( 'coupon_discount_tax_totals' , null );
WC () -> session -> set ( 'removed_cart_contents' , null );
WC () -> session -> set ( 'order_awaiting_payment' , null );
}
/**
* Will set cart cookies if needed and when possible .
*
* @ since 3.2 . 0
*/
public function maybe_set_cart_cookies () {
if ( ! headers_sent () && did_action ( 'wp_loaded' ) ) {
if ( ! $this -> cart -> is_empty () ) {
$this -> set_cart_cookies ( true );
2018-03-16 17:45:28 +00:00
} elseif ( isset ( $_COOKIE [ 'woocommerce_items_in_cart' ] ) ) { // WPCS: input var ok.
2017-08-18 09:54:41 +00:00
$this -> set_cart_cookies ( false );
}
}
}
/**
* Sets the php session data for the cart and coupons .
*/
public function set_session () {
WC () -> session -> set ( 'cart' , $this -> get_cart_for_session () );
WC () -> session -> set ( 'cart_totals' , $this -> cart -> get_totals () );
WC () -> session -> set ( 'applied_coupons' , $this -> cart -> get_applied_coupons () );
WC () -> session -> set ( 'coupon_discount_totals' , $this -> cart -> get_coupon_discount_totals () );
WC () -> session -> set ( 'coupon_discount_tax_totals' , $this -> cart -> get_coupon_discount_tax_totals () );
WC () -> session -> set ( 'removed_cart_contents' , $this -> cart -> get_removed_cart_contents () );
do_action ( 'woocommerce_cart_updated' );
}
/**
* Returns the contents of the cart in an array without the 'data' element .
*
* @ return array contents of the cart
*/
public function get_cart_for_session () {
$cart_session = array ();
foreach ( $this -> cart -> get_cart () as $key => $values ) {
$cart_session [ $key ] = $values ;
unset ( $cart_session [ $key ][ 'data' ] ); // Unset product object.
}
return $cart_session ;
}
/**
* Save the persistent cart when the cart is updated .
*/
public function persistent_cart_update () {
2018-02-19 16:36:26 +00:00
if ( get_current_user_id () && apply_filters ( 'woocommerce_persistent_cart_enabled' , true ) ) {
2018-03-16 17:45:28 +00:00
update_user_meta (
2019-01-03 15:30:21 +00:00
get_current_user_id (),
'_woocommerce_persistent_cart_' . get_current_blog_id (),
array (
2018-03-16 17:45:28 +00:00
'cart' => $this -> get_cart_for_session (),
)
);
2017-08-18 09:54:41 +00:00
}
}
/**
* Delete the persistent cart permanently .
*/
public function persistent_cart_destroy () {
2018-06-22 12:32:45 +00:00
if ( get_current_user_id () ) {
2017-08-18 09:54:41 +00:00
delete_user_meta ( get_current_user_id (), '_woocommerce_persistent_cart_' . get_current_blog_id () );
}
}
/**
2019-02-05 13:02:52 +00:00
* Set cart hash cookie and items in cart if not already set .
2017-08-18 09:54:41 +00:00
*
* @ param bool $set Should cookies be set ( true ) or unset .
*/
private function set_cart_cookies ( $set = true ) {
if ( $set ) {
2019-02-05 13:02:52 +00:00
$setcookies = array (
'woocommerce_items_in_cart' => '1' ,
'woocommerce_cart_hash' => WC () -> cart -> get_cart_hash (),
);
foreach ( $setcookies as $name => $value ) {
if ( ! isset ( $_COOKIE [ $name ] ) || $_COOKIE [ $name ] !== $value ) {
wc_setcookie ( $name , $value );
}
}
} else {
$unsetcookies = array (
'woocommerce_items_in_cart' ,
'woocommerce_cart_hash' ,
);
foreach ( $unsetcookies as $name ) {
if ( isset ( $_COOKIE [ $name ] ) ) {
wc_setcookie ( $name , 0 , time () - HOUR_IN_SECONDS );
unset ( $_COOKIE [ $name ] );
}
}
2017-08-18 09:54:41 +00:00
}
2019-02-05 13:02:52 +00:00
2017-08-18 09:54:41 +00:00
do_action ( 'woocommerce_set_cart_cookies' , $set );
}
2018-06-22 12:32:45 +00:00
/**
* Get the persistent cart from the database .
*
* @ since 3.5 . 0
* @ return array
*/
private function get_saved_cart () {
$saved_cart = array ();
if ( apply_filters ( 'woocommerce_persistent_cart_enabled' , true ) ) {
$saved_cart_meta = get_user_meta ( get_current_user_id (), '_woocommerce_persistent_cart_' . get_current_blog_id (), true );
if ( isset ( $saved_cart_meta [ 'cart' ] ) ) {
$saved_cart = array_filter ( ( array ) $saved_cart_meta [ 'cart' ] );
}
}
return $saved_cart ;
}
/**
* Get a cart from an order , if user has permission .
*
* @ since 3.5 . 0
2019-01-21 15:54:49 +00:00
*
2018-06-22 12:32:45 +00:00
* @ param int $order_id Order ID to try to load .
* @ param array $cart Current cart array .
2019-01-21 15:54:49 +00:00
*
2018-06-22 12:32:45 +00:00
* @ return array
*/
private function populate_cart_from_order ( $order_id , $cart ) {
$order = wc_get_order ( $order_id );
if ( ! $order -> get_id () || ! $order -> has_status ( apply_filters ( 'woocommerce_valid_order_statuses_for_order_again' , array ( 'completed' ) ) ) || ! current_user_can ( 'order_again' , $order -> get_id () ) ) {
return ;
}
if ( apply_filters ( 'woocommerce_empty_cart_when_order_again' , true ) ) {
$cart = array ();
}
$inital_cart_size = count ( $cart );
$order_items = $order -> get_items ();
foreach ( $order_items as $item ) {
$product_id = ( int ) apply_filters ( 'woocommerce_add_to_cart_product_id' , $item -> get_product_id () );
$quantity = $item -> get_quantity ();
$variation_id = ( int ) $item -> get_variation_id ();
$variations = array ();
$cart_item_data = apply_filters ( 'woocommerce_order_again_cart_item_data' , array (), $item , $order );
$product = $item -> get_product ();
if ( ! $product ) {
continue ;
}
// Prevent reordering variable products if no selected variation.
if ( ! $variation_id && $product -> is_type ( 'variable' ) ) {
continue ;
}
2019-01-03 15:30:26 +00:00
// Prevent reordering items specifically out of stock.
if ( ! $product -> is_in_stock () ) {
continue ;
}
2018-06-22 12:32:45 +00:00
foreach ( $item -> get_meta_data () as $meta ) {
if ( taxonomy_is_product_attribute ( $meta -> key ) ) {
$term = get_term_by ( 'slug' , $meta -> value , $meta -> key );
$variations [ $meta -> key ] = $term ? $term -> name : $meta -> value ;
} elseif ( meta_is_product_attribute ( $meta -> key , $meta -> value , $product_id ) ) {
$variations [ $meta -> key ] = $meta -> value ;
}
}
if ( ! apply_filters ( 'woocommerce_add_to_cart_validation' , true , $product_id , $quantity , $variation_id , $variations , $cart_item_data ) ) {
continue ;
}
// Add to cart directly.
$cart_id = WC () -> cart -> generate_cart_id ( $product_id , $variation_id , $variations , $cart_item_data );
$product_data = wc_get_product ( $variation_id ? $variation_id : $product_id );
$cart [ $cart_id ] = apply_filters (
2019-01-03 15:30:21 +00:00
'woocommerce_add_order_again_cart_item' ,
array_merge (
$cart_item_data ,
array (
2018-06-22 12:32:45 +00:00
'key' => $cart_id ,
'product_id' => $product_id ,
'variation_id' => $variation_id ,
'variation' => $variations ,
'quantity' => $quantity ,
'data' => $product_data ,
'data_hash' => wc_get_cart_item_data_hash ( $product_data ),
)
2019-01-03 15:30:21 +00:00
),
$cart_id
2018-06-22 12:32:45 +00:00
);
}
2019-01-11 17:14:29 +00:00
do_action_ref_array ( 'woocommerce_ordered_again' , array ( $order -> get_id (), $order_items , & $cart ) );
2018-06-22 12:32:45 +00:00
$num_items_in_cart = count ( $cart );
$num_items_in_original_order = count ( $order_items );
$num_items_added = $num_items_in_cart - $inital_cart_size ;
if ( $num_items_in_original_order > $num_items_added ) {
wc_add_notice (
sprintf (
2018-06-26 12:49:57 +00:00
/* translators: %d item count */
2018-06-22 12:32:45 +00:00
_n (
'%d item from your previous order is currently unavailable and could not be added to your cart.' ,
'%d items from your previous order are currently unavailable and could not be added to your cart.' ,
2019-01-07 19:44:13 +00:00
$num_items_in_original_order - $num_items_added ,
2018-06-22 12:32:45 +00:00
'woocommerce'
),
2019-01-07 19:44:13 +00:00
$num_items_in_original_order - $num_items_added
2018-06-22 12:32:45 +00:00
),
'error'
);
}
if ( 0 < $num_items_added ) {
wc_add_notice ( __ ( 'The cart has been filled with the items from your previous order.' , 'woocommerce' ) );
}
return $cart ;
}
2017-08-18 09:54:41 +00:00
}