Commit 4b8ad3641a8 for woocommerce

commit 4b8ad3641a827c2eb46fe1f99e69f33dee8fd5d3
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Fri Nov 6 16:56:46 2026 +0100

    CartController test and types enhancements (#70711)

    * Improve CartController types

    * Update PHPStan

    * Test correct 'Invalid value posted' is shown when trying to add variations to cart with incorrect values

    * Add changelog file

    * Improve comments

diff --git a/plugins/woocommerce/changelog/update-CartController-fixes b/plugins/woocommerce/changelog/update-CartController-fixes
new file mode 100644
index 00000000000..2e524c67e98
--- /dev/null
+++ b/plugins/woocommerce/changelog/update-CartController-fixes
@@ -0,0 +1,5 @@
+Significance: patch
+Type: dev
+Comment: CartController test and types enhancements
+
+
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 9c4f5683eaf..6378bb66407 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -12757,13 +12757,13 @@ parameters:
 			path: includes/class-wc-order.php

 		-
-			message: '#^PHPDoc tag @param has invalid value \(WC_Order \$this          Order object\.\)\: Unexpected token "\$this", expected variable at offset 192 on line 6$#'
+			message: '#^PHPDoc tag @param has invalid value \(WC_Order \$this          Order object\.\)\: Unexpected token "\$this", expected variable at offset 187 on line 6$#'
 			identifier: phpDoc.parseError
 			count: 1
 			path: includes/class-wc-order.php

 		-
-			message: '#^PHPDoc tag @param has invalid value \(WC_Order \$this          Order object\.\)\: Unexpected token "\$this", expected variable at offset 187 on line 6$#'
+			message: '#^PHPDoc tag @param has invalid value \(WC_Order \$this          Order object\.\)\: Unexpected token "\$this", expected variable at offset 192 on line 6$#'
 			identifier: phpDoc.parseError
 			count: 1
 			path: includes/class-wc-order.php
@@ -66882,12 +66882,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Utilities/CartController.php

-		-
-			message: '#^Call to method is_type\(\) on an unknown class Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\.$#'
-			identifier: class.notFound
-			count: 2
-			path: src/StoreApi/Utilities/CartController.php
-
 		-
 			message: '#^Cannot call method get_id\(\) on WC_Product\|false\|null\.$#'
 			identifier: method.nonObject
@@ -66954,12 +66948,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Utilities/CartController.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_product_for_cart\(\) has invalid return type Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\.$#'
-			identifier: class.notFound
-			count: 1
-			path: src/StoreApi/Utilities/CartController.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_remaining_stock_for_product\(\) should return int but returns float\|int\.$#'
 			identifier: return.type
@@ -67062,42 +67050,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Utilities/CartController.php

-		-
-			message: '#^Parameter \#1 \$product of function wc_get_cart_item_data_hash expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/StoreApi/Utilities/CartController.php
-
-		-
-			message: '#^Parameter \#1 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_product_id\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 3
-			path: src/StoreApi/Utilities/CartController.php
-
-		-
-			message: '#^Parameter \#1 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_variable_product_attributes\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/StoreApi/Utilities/CartController.php
-
-		-
-			message: '#^Parameter \#1 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_variation_id\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 3
-			path: src/StoreApi/Utilities/CartController.php
-
-		-
-			message: '#^Parameter \#1 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:validate_add_to_cart\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/StoreApi/Utilities/CartController.php
-
-		-
-			message: '#^Parameter \#1 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\QuantityLimits\:\:get_add_to_cart_limits\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 2
-			path: src/StoreApi/Utilities/CartController.php
-
 		-
 			message: '#^Parameter \#1 \$str of function md5 expects string, string\|false given\.$#'
 			identifier: argument.type
@@ -67110,12 +67062,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Utilities/CartController.php

-		-
-			message: '#^Parameter \#2 \$product of method Automattic\\WooCommerce\\StoreApi\\Utilities\\CartController\:\:get_variation_id_from_variation_data\(\) expects WC_Product, Automattic\\WooCommerce\\StoreApi\\Utilities\\Error\|WC_Product given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/StoreApi/Utilities/CartController.php
-
 		-
 			message: '#^Parameter \#2 \$quantity of method WC_Cart\:\:set_quantity\(\) expects int, float\|int given\.$#'
 			identifier: argument.type
diff --git a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
index 79ddd787e79..3a80f89eb34 100644
--- a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
+++ b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
@@ -1138,12 +1138,12 @@ class CartController {
 	 * @throws RouteException Exception if invalid data is detected.
 	 *
 	 * @param array $request Add to cart request params.
-	 * @return \WC_Product|Error Returns a product object if purchasable.
+	 * @return \WC_Product Product to add. Throws if it does not exist or is trashed.
 	 */
 	protected function get_product_for_cart( $request ) {
 		$product = wc_get_product( $request['id'] );

-		if ( ! $product || ProductStatus::TRASH === $product->get_status() ) {
+		if ( ! $product instanceof \WC_Product || ProductStatus::TRASH === $product->get_status() ) {
 			throw new RouteException(
 				'woocommerce_rest_cart_invalid_product',
 				sprintf(
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
index e274fd059e3..ebf01549546 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
@@ -3,6 +3,7 @@ declare( strict_types = 1 );

 namespace Automattic\WooCommerce\Tests\Blocks\StoreApi\Utilities;

+use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
 use Automattic\WooCommerce\StoreApi\Utilities\CartController;
 use Automattic\WooCommerce\Tests\Blocks\Helpers\FixtureData;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
@@ -389,4 +390,48 @@ class CartControllerTests extends \WC_Unit_Test_Case {
 		$this->assertSame( array( 1, $product->get_id(), (int) $variation['variation_id'] ), $captured );
 		$this->assertSame( 4, wc()->cart->get_cart_item( $cart_id )['quantity'] );
 	}
+
+	/**
+	 * @testdox Add to cart rejects an attribute value outside the configured options, including when an Any variation exists.
+	 */
+	public function test_add_to_cart_rejects_attribute_value_outside_options_when_any_variation_exists(): void {
+		$product   = new \WC_Product_Variable();
+		$attribute = new \WC_Product_Attribute();
+		$attribute->set_name( 'Color' );
+		$attribute->set_options( array( 'Red', 'Blue' ) );
+		$attribute->set_variation( true );
+		$product->set_attributes( array( $attribute ) );
+		$product->save();
+
+		$red = new \WC_Product_Variation();
+		$red->set_parent_id( $product->get_id() );
+		$red->set_attributes( array( 'color' => 'Red' ) );
+		$red->set_regular_price( '5' );
+		$red->save();
+
+		$any = new \WC_Product_Variation();
+		$any->set_parent_id( $product->get_id() );
+		$any->set_attributes( array( 'color' => '' ) );
+		$any->set_regular_price( '4' );
+		$any->save();
+
+		try {
+			( new CartController() )->add_to_cart(
+				array(
+					'id'        => $product->get_id(),
+					'quantity'  => 1,
+					'variation' => array(
+						array(
+							'attribute' => 'Color',
+							'value'     => 'Green',
+						),
+					),
+				)
+			);
+			$this->fail( 'Posting an attribute value that is not an option should be rejected, including when an Any variation exists.' );
+		} catch ( RouteException $exception ) {
+			$this->assertSame( 'woocommerce_rest_invalid_variation_data', $exception->getErrorCode() );
+			$this->assertStringContainsString( 'Invalid value posted for Color', $exception->getMessage() );
+		}
+	}
 }