Commit 3e2ea932815 for woocommerce

commit 3e2ea932815aa6d06e6d816c7e311d11b818f501
Author: Asim Sulehria <de.asimhabib@gmail.com>
Date:   Sun Oct 25 07:30:23 2026 +0500

    Fix fatal errors from unchecked null returns in cart, coupon, and download functions (#65374)


    ---------

    Co-authored-by: Darren Ethier <darren@roughsmootheng.in>

diff --git a/plugins/woocommerce/changelog/65374-null-safety-guards b/plugins/woocommerce/changelog/65374-null-safety-guards
new file mode 100644
index 00000000000..deacaed96ae
--- /dev/null
+++ b/plugins/woocommerce/changelog/65374-null-safety-guards
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent fatal errors in the cart, coupons, and download permissions when a product, parent product, or category was deleted.
diff --git a/plugins/woocommerce/includes/class-wc-cart.php b/plugins/woocommerce/includes/class-wc-cart.php
index e846548bbc9..bff0e8d4dce 100644
--- a/plugins/woocommerce/includes/class-wc-cart.php
+++ b/plugins/woocommerce/includes/class-wc-cart.php
@@ -1232,6 +1232,10 @@ class WC_Cart extends WC_Legacy_Cart {
 				$missing_attributes = array();
 				$parent_data        = wc_get_product( $product_data->get_parent_id() );

+				if ( ! $parent_data instanceof WC_Product ) {
+					throw new Exception( __( 'Sorry, this product cannot be added to the cart.', 'woocommerce' ) );
+				}
+
 				$variation_attributes = $product_data->get_variation_attributes();
 				// Filter out 'any' variations, which are empty, as they need to be explicitly specified while adding to cart.
 				$variation_attributes = array_filter( $variation_attributes, 'wc_array_filter_default_attributes' );
diff --git a/plugins/woocommerce/includes/class-wc-coupon.php b/plugins/woocommerce/includes/class-wc-coupon.php
index 153c1260670..0408645ce1c 100644
--- a/plugins/woocommerce/includes/class-wc-coupon.php
+++ b/plugins/woocommerce/includes/class-wc-coupon.php
@@ -1267,8 +1267,10 @@ class WC_Coupon extends WC_Legacy_Coupon {

 						if ( count( $intersect ) > 0 ) {
 							foreach ( $intersect as $cat_id ) {
-								$cat          = get_term( $cat_id, 'product_cat' );
-								$categories[] = $cat->name;
+								$cat = get_term( $cat_id, 'product_cat' );
+								if ( $cat instanceof WP_Term ) {
+									$categories[] = $cat->name;
+								}
 							}
 						}
 					}
diff --git a/plugins/woocommerce/includes/wc-order-functions.php b/plugins/woocommerce/includes/wc-order-functions.php
index 84192d84177..3e0b5a6ace7 100644
--- a/plugins/woocommerce/includes/wc-order-functions.php
+++ b/plugins/woocommerce/includes/wc-order-functions.php
@@ -434,6 +434,9 @@ function wc_downloadable_file_permission( $download_id, $product, $order, $qty =
 	if ( is_numeric( $product ) ) {
 		$product = wc_get_product( $product );
 	}
+	if ( ! $product instanceof WC_Product ) {
+		return false;
+	}
 	$download = new WC_Customer_Download();
 	$download->set_download_id( $download_id );
 	$download->set_product_id( $product->get_id() );
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 56a1883b23b..1a9f9d68b36 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -10098,12 +10098,6 @@ parameters:
 			count: 1
 			path: includes/class-wc-cart.php

-		-
-			message: '#^Cannot call method get_attributes\(\) on WC_Product\|false\|null\.$#'
-			identifier: method.nonObject
-			count: 2
-			path: includes/class-wc-cart.php
-
 		-
 			message: '#^Method WC_Cart\:\:check_cart_items\(\) has no return type specified\.$#'
 			identifier: missingType.return
@@ -10626,12 +10620,6 @@ parameters:
 			count: 1
 			path: includes/class-wc-coupon.php

-		-
-			message: '#^Cannot access property \$name on WP_Error\|WP_Term\|null\.$#'
-			identifier: property.nonObject
-			count: 1
-			path: includes/class-wc-coupon.php
-
 		-
 			message: '#^Method WC_Coupon\:\:get_short_info\(\) should return string but returns string\|false\.$#'
 			identifier: return.type
@@ -33891,24 +33879,6 @@ parameters:
 			count: 1
 			path: includes/wc-order-functions.php

-		-
-			message: '#^Cannot call method get_download_expiry\(\) on WC_Product\|false\|null\.$#'
-			identifier: method.nonObject
-			count: 1
-			path: includes/wc-order-functions.php
-
-		-
-			message: '#^Cannot call method get_download_limit\(\) on WC_Product\|false\|null\.$#'
-			identifier: method.nonObject
-			count: 2
-			path: includes/wc-order-functions.php
-
-		-
-			message: '#^Cannot call method get_id\(\) on WC_Product\|false\|null\.$#'
-			identifier: method.nonObject
-			count: 1
-			path: includes/wc-order-functions.php
-
 		-
 			message: '#^Cannot call method get_total\(\) on WC_Order\|WC_Order_Refund\|false\.$#'
 			identifier: method.nonObject