Fix cloning cart keeps wrong reference in session and fee (#39282)

This commit is contained in:
Ron Rennick 2023-09-07 14:00:29 -03:00 committed by GitHub
commit 60fa13e21b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 26 deletions

View File

@ -1,4 +1,4 @@
Significance: patch Significance: patch
Type: fix 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

View File

@ -26,14 +26,6 @@ final class WC_Cart_Fees {
*/ */
private $fees = array(); private $fees = array();
/**
* Reference to cart object.
*
* @since 3.2.0
* @var WC_Cart
*/
private $cart;
/** /**
* New fees are made out of these props. * New fees are made out of these props.
* *
@ -51,16 +43,18 @@ final class WC_Cart_Fees {
/** /**
* Constructor. Reference to the cart. * Constructor. Reference to the cart.
* *
* @param null $deprecated Deprecated since WooCommerce 8.2.0.
*
* @since 3.2.0 * @since 3.2.0
* @throws Exception If missing WC_Cart object.
* @param WC_Cart $cart Cart object.
*/ */
public function __construct( &$cart ) { public function __construct( $deprecated = null ) {
if ( ! is_a( $cart, 'WC_Cart' ) ) { if ( isset( $deprecated ) ) {
throw new Exception( 'A valid WC_Cart object is required' ); 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;
} }
/** /**

View File

@ -33,14 +33,24 @@ final class WC_Cart_Session {
* *
* @param WC_Cart $cart Cart object to calculate totals for. * @param WC_Cart $cart Cart object to calculate totals for.
*/ */
public function __construct( &$cart ) { public function __construct( $cart ) {
if ( ! is_a( $cart, 'WC_Cart' ) ) { if ( ! is_a( $cart, 'WC_Cart' ) ) {
throw new Exception( 'A valid WC_Cart object is required' ); 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; $this->cart = $cart;
} }
/** /**
* Register methods for this object on the appropriate WordPress hooks. * Register methods for this object on the appropriate WordPress hooks.
*/ */

View File

@ -98,7 +98,7 @@ class WC_Cart extends WC_Legacy_Cart {
*/ */
public function __construct() { public function __construct() {
$this->session = new WC_Cart_Session( $this ); $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. // Register hooks for the objects.
$this->session->init(); $this->session->init();
@ -120,6 +120,8 @@ class WC_Cart extends WC_Legacy_Cart {
public function __clone() { public function __clone() {
$this->session = clone $this->session; $this->session = clone $this->session;
$this->fees_api = clone $this->fees_api; $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() { public function get_cart_contents_weight() {
$weight = 0.0; $weight = 0.0;
foreach ( $this->get_cart() as $cart_item_key => $values ) { foreach ( $this->get_cart() as $values ) {
if ( $values['data']->has_weight() ) { if ( $values['data']->has_weight() ) {
$weight += (float) $values['data']->get_weight() * $values['quantity']; $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() { public function get_cart_item_quantities() {
$quantities = array(); $quantities = array();
foreach ( $this->get_cart() as $cart_item_key => $values ) { foreach ( $this->get_cart() as $values ) {
$product = $values['data']; $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']; $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(); $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; $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']; $product = $values['data'];
// Check stock based on stock-status. // Check stock based on stock-status.
@ -818,7 +820,7 @@ class WC_Cart extends WC_Legacy_Cart {
$cross_sells = array(); $cross_sells = array();
$in_cart = array(); $in_cart = array();
if ( ! $this->is_empty() ) { if ( ! $this->is_empty() ) {
foreach ( $this->get_cart() as $cart_item_key => $values ) { foreach ( $this->get_cart() as $values ) {
if ( $values['quantity'] > 0 ) { if ( $values['quantity'] > 0 ) {
$cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells ); $cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells );
$in_cart[] = $values['product_id']; $in_cart[] = $values['product_id'];
@ -908,7 +910,7 @@ class WC_Cart extends WC_Legacy_Cart {
public function get_cart_item_tax_classes() { public function get_cart_item_tax_classes() {
$found_tax_classes = array(); $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() ) ) { if ( $item['data'] && ( $item['data']->is_taxable() || $item['data']->is_shipping_taxable() ) ) {
$found_tax_classes[] = $item['data']->get_tax_class(); $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() { public function get_cart_item_tax_classes_for_shipping() {
$found_tax_classes = array(); $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() ) ) { if ( $item['data'] && ( $item['data']->is_shipping_taxable() ) ) {
$found_tax_classes[] = $item['data']->get_tax_class(); $found_tax_classes[] = $item['data']->get_tax_class();
} }
@ -1536,7 +1538,7 @@ class WC_Cart extends WC_Legacy_Cart {
} }
$needs_shipping = false; $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() ) { if ( $values['data']->needs_shipping() ) {
$needs_shipping = true; $needs_shipping = true;
break; break;

View File

@ -14,7 +14,7 @@ class WC_Tests_WC_Cart_Fees extends WC_Unit_Test_Case {
*/ */
public function test_set_get_remove_fees() { public function test_set_get_remove_fees() {
$cart_fees = new WC_Cart_Fees( wc()->cart ); $cart_fees = new WC_Cart_Fees();
// Test add_fee. // Test add_fee.
$args = array( $args = array(

View File

@ -96,6 +96,37 @@ class WC_Cart_Test extends \WC_Unit_Test_Case {
$variable_product->delete( true ); $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. * Test show shipping.
*/ */