diff --git a/plugins/woocommerce/changelog/trunk b/plugins/woocommerce/changelog/trunk index 96319e14d80..2e7f0ab896e 100644 --- a/plugins/woocommerce/changelog/trunk +++ b/plugins/woocommerce/changelog/trunk @@ -1,4 +1,4 @@ Significance: patch Type: fix -Fixes PHP error when WooCommerce menus are created without a position and when users view the WordPress admin dashboard without having WooCommerce admin permissions. +Use correct object reference when cloning a cart diff --git a/plugins/woocommerce/includes/class-wc-cart-fees.php b/plugins/woocommerce/includes/class-wc-cart-fees.php index f80376f996a..ed1b98cacfc 100644 --- a/plugins/woocommerce/includes/class-wc-cart-fees.php +++ b/plugins/woocommerce/includes/class-wc-cart-fees.php @@ -26,14 +26,6 @@ final class WC_Cart_Fees { */ private $fees = array(); - /** - * Reference to cart object. - * - * @since 3.2.0 - * @var WC_Cart - */ - private $cart; - /** * New fees are made out of these props. * @@ -51,16 +43,18 @@ final class WC_Cart_Fees { /** * Constructor. Reference to the cart. * + * @param null $deprecated Deprecated since WooCommerce 8.2.0. + * * @since 3.2.0 - * @throws Exception If missing WC_Cart object. - * @param WC_Cart $cart Cart object. */ - public function __construct( &$cart ) { - if ( ! is_a( $cart, 'WC_Cart' ) ) { - throw new Exception( 'A valid WC_Cart object is required' ); + public function __construct( $deprecated = null ) { + if ( isset( $deprecated ) ) { + wc_doing_it_wrong( + 'new WC_Cart_Fees', + 'You don\'t need to pass a cart parameter to the WC_Cart_Fees constructor anymore', + '8.2.0' + ); } - - $this->cart = $cart; } /** diff --git a/plugins/woocommerce/includes/class-wc-cart-session.php b/plugins/woocommerce/includes/class-wc-cart-session.php index cfc6bc24284..478e9fdabfa 100644 --- a/plugins/woocommerce/includes/class-wc-cart-session.php +++ b/plugins/woocommerce/includes/class-wc-cart-session.php @@ -33,14 +33,24 @@ final class WC_Cart_Session { * * @param WC_Cart $cart Cart object to calculate totals for. */ - public function __construct( &$cart ) { + public function __construct( $cart ) { if ( ! is_a( $cart, 'WC_Cart' ) ) { throw new Exception( 'A valid WC_Cart object is required' ); } + $this->set_cart( $cart ); + } + + /** + * Sets the cart instance. + * + * @param WC_Cart $cart Cart object. + */ + public function set_cart( WC_Cart $cart ) { $this->cart = $cart; } + /** * Register methods for this object on the appropriate WordPress hooks. */ diff --git a/plugins/woocommerce/includes/class-wc-cart.php b/plugins/woocommerce/includes/class-wc-cart.php index 085f9cfbcb5..84410f9c505 100644 --- a/plugins/woocommerce/includes/class-wc-cart.php +++ b/plugins/woocommerce/includes/class-wc-cart.php @@ -98,7 +98,7 @@ class WC_Cart extends WC_Legacy_Cart { */ public function __construct() { $this->session = new WC_Cart_Session( $this ); - $this->fees_api = new WC_Cart_Fees( $this ); + $this->fees_api = new WC_Cart_Fees(); // Register hooks for the objects. $this->session->init(); @@ -120,6 +120,8 @@ class WC_Cart extends WC_Legacy_Cart { public function __clone() { $this->session = clone $this->session; $this->fees_api = clone $this->fees_api; + + $this->session->set_cart( $this ); } /* @@ -667,7 +669,7 @@ class WC_Cart extends WC_Legacy_Cart { public function get_cart_contents_weight() { $weight = 0.0; - foreach ( $this->get_cart() as $cart_item_key => $values ) { + foreach ( $this->get_cart() as $values ) { if ( $values['data']->has_weight() ) { $weight += (float) $values['data']->get_weight() * $values['quantity']; } @@ -684,7 +686,7 @@ class WC_Cart extends WC_Legacy_Cart { public function get_cart_item_quantities() { $quantities = array(); - foreach ( $this->get_cart() as $cart_item_key => $values ) { + foreach ( $this->get_cart() as $values ) { $product = $values['data']; $quantities[ $product->get_stock_managed_by_id() ] = isset( $quantities[ $product->get_stock_managed_by_id() ] ) ? $quantities[ $product->get_stock_managed_by_id() ] + $values['quantity'] : $values['quantity']; } @@ -759,7 +761,7 @@ class WC_Cart extends WC_Legacy_Cart { $product_qty_in_cart = $this->get_cart_item_quantities(); $current_session_order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0; - foreach ( $this->get_cart() as $cart_item_key => $values ) { + foreach ( $this->get_cart() as $values ) { $product = $values['data']; // Check stock based on stock-status. @@ -818,7 +820,7 @@ class WC_Cart extends WC_Legacy_Cart { $cross_sells = array(); $in_cart = array(); if ( ! $this->is_empty() ) { - foreach ( $this->get_cart() as $cart_item_key => $values ) { + foreach ( $this->get_cart() as $values ) { if ( $values['quantity'] > 0 ) { $cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells ); $in_cart[] = $values['product_id']; @@ -908,7 +910,7 @@ class WC_Cart extends WC_Legacy_Cart { public function get_cart_item_tax_classes() { $found_tax_classes = array(); - foreach ( WC()->cart->get_cart() as $item ) { + foreach ( $this->get_cart() as $item ) { if ( $item['data'] && ( $item['data']->is_taxable() || $item['data']->is_shipping_taxable() ) ) { $found_tax_classes[] = $item['data']->get_tax_class(); } @@ -925,7 +927,7 @@ class WC_Cart extends WC_Legacy_Cart { public function get_cart_item_tax_classes_for_shipping() { $found_tax_classes = array(); - foreach ( WC()->cart->get_cart() as $item ) { + foreach ( $this->get_cart() as $item ) { if ( $item['data'] && ( $item['data']->is_shipping_taxable() ) ) { $found_tax_classes[] = $item['data']->get_tax_class(); } @@ -1536,7 +1538,7 @@ class WC_Cart extends WC_Legacy_Cart { } $needs_shipping = false; - foreach ( $this->get_cart_contents() as $cart_item_key => $values ) { + foreach ( $this->get_cart_contents() as $values ) { if ( $values['data']->needs_shipping() ) { $needs_shipping = true; break; diff --git a/plugins/woocommerce/tests/legacy/unit-tests/cart/cart-fees.php b/plugins/woocommerce/tests/legacy/unit-tests/cart/cart-fees.php index 20721913ccc..1b59e03808e 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/cart/cart-fees.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/cart/cart-fees.php @@ -14,7 +14,7 @@ class WC_Tests_WC_Cart_Fees extends WC_Unit_Test_Case { */ public function test_set_get_remove_fees() { - $cart_fees = new WC_Cart_Fees( wc()->cart ); + $cart_fees = new WC_Cart_Fees(); // Test add_fee. $args = array( diff --git a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php index bf6a85bbd9c..57835fc52d3 100644 --- a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php +++ b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php @@ -96,6 +96,37 @@ class WC_Cart_Test extends \WC_Unit_Test_Case { $variable_product->delete( true ); } + /** + * Test cloning cart holds no references in session + */ + public function test_cloning_cart_session() { + $product = WC_Helper_Product::create_simple_product(); + + // Initialize $cart1 and $cart2 as empty carts. + $cart1 = WC()->cart; + $cart1->empty_cart(); + $cart2 = clone $cart1; + + // Create a cart in session. + $cart1->add_to_cart( $product->get_id(), 1 ); + $cart1->set_session(); + + // Empty the cart without clearing the session. + $cart1->set_cart_contents( array() ); + + // Both carts are empty at that point. + $this->assertTrue( $cart2->is_empty() ); + $this->assertTrue( $cart1->is_empty() ); + + $cart2->get_cart_from_session(); + + // We retrieved $cart2 from the previously set session so it should not be empty. + $this->assertFalse( $cart2->is_empty() ); + + // We didn't touch $cart1 so it should still be empty. + $this->assertTrue( $cart1->is_empty() ); + } + /** * Test show shipping. */