Commit 4206f87a3d0 for woocommerce

commit 4206f87a3d0b922b92e5cb715b52308e9d1cf46a
Author: Marin Atanasov <8436925+tyxla@users.noreply.github.com>
Date:   Thu Mar 26 00:20:54 2026 +0900

    Fix `get_item_downloads()` not always returning an `array` (#63816)

    * Fix get_item_downloads() not always returning an array

    After #61518, get_item_downloads() could implicitly return null when
    the product or order does not exist. Ensure the method always returns
    the initialized empty array when the early conditions are not met.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    * Add changefile(s) from automation for the following project(s): woocommerce

    * Update phpstan baseline

    * Early return when no downloads

    * Add extra defensive check for filtered value

    ---------

    Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/63816-fix-get-item-downloads-return-array b/plugins/woocommerce/changelog/63816-fix-get-item-downloads-return-array
new file mode 100644
index 00000000000..e12810e9552
--- /dev/null
+++ b/plugins/woocommerce/changelog/63816-fix-get-item-downloads-return-array
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+Comment: Fix `get_item_downloads()` not always returning an `array`
+
diff --git a/plugins/woocommerce/includes/class-wc-order-item-product.php b/plugins/woocommerce/includes/class-wc-order-item-product.php
index 4de34de8e3b..917e0edc4c6 100644
--- a/plugins/woocommerce/includes/class-wc-order-item-product.php
+++ b/plugins/woocommerce/includes/class-wc-order-item-product.php
@@ -431,47 +431,55 @@ class WC_Order_Item_Product extends WC_Order_Item {
 		$order      = $this->get_order();
 		$product_id = $this->get_variation_id() ? $this->get_variation_id() : $this->get_product_id();

-		if ( $product && $order && $product->is_downloadable() && $order->is_download_permitted() ) {
-			$email_hash         = function_exists( 'hash' ) ? hash( 'sha256', $order->get_billing_email() ) : sha1( $order->get_billing_email() );
-			$data_store         = WC_Data_Store::load( 'customer-download' );
-			$customer_downloads = $data_store->get_downloads(
-				array(
-					'user_email' => $order->get_billing_email(),
-					'order_id'   => $order->get_id(),
-					'product_id' => $product_id,
-				)
-			);
-			foreach ( $customer_downloads as $customer_download ) {
-				$download_id = $customer_download->get_download_id();
-
-				if ( $product->has_file( $download_id ) ) {
-					$file                  = $product->get_file( $download_id );
-					$files[ $download_id ] = $file->get_data();
-					$files[ $download_id ]['downloads_remaining'] = $customer_download->get_downloads_remaining();
-					$files[ $download_id ]['access_expires']      = $customer_download->get_access_expires();
-					$files[ $download_id ]['download_url']        = add_query_arg(
-						array(
-							'download_file' => $product_id,
-							'order'         => $order->get_order_key(),
-							'uid'           => $email_hash,
-							'key'           => $download_id,
-						),
-						trailingslashit( home_url() )
-					);
-				}
+		if ( ! $product || ! $order || ! $product->is_downloadable() || ! $order->is_download_permitted() ) {
+			return array();
+		}
+
+		$email_hash         = function_exists( 'hash' ) ? hash( 'sha256', $order->get_billing_email() ) : sha1( $order->get_billing_email() );
+		$data_store         = WC_Data_Store::load( 'customer-download' );
+		$customer_downloads = $data_store->get_downloads(
+			array(
+				'user_email' => $order->get_billing_email(),
+				'order_id'   => $order->get_id(),
+				'product_id' => $product_id,
+			)
+		);
+		foreach ( $customer_downloads as $customer_download ) {
+			$download_id = $customer_download->get_download_id();
+
+			if ( $product->has_file( $download_id ) ) {
+				$file                  = $product->get_file( $download_id );
+				$files[ $download_id ] = $file->get_data();
+				$files[ $download_id ]['downloads_remaining'] = $customer_download->get_downloads_remaining();
+				$files[ $download_id ]['access_expires']      = $customer_download->get_access_expires();
+				$files[ $download_id ]['download_url']        = add_query_arg(
+					array(
+						'download_file' => $product_id,
+						'order'         => $order->get_order_key(),
+						'uid'           => $email_hash,
+						'key'           => $download_id,
+					),
+					trailingslashit( home_url() )
+				);
 			}
+		}

-			/**
-			 * Filters the list of downloadable files for an order item.
-			 *
-			 * @since 2.7.0
-			 *
-			 * @param array                 $files Array of downloadable file data.
-			 * @param WC_Order_Item_Product $item  The order item product object.
-			 * @param WC_Order              $order The order object.
-			 */
-			return apply_filters( 'woocommerce_get_item_downloads', $files, $this, $order );
+		/**
+		 * Filters the list of downloadable files for an order item.
+		 *
+		 * @since 2.7.0
+		 *
+		 * @param array                 $files Array of downloadable file data.
+		 * @param WC_Order_Item_Product $item  The order item product object.
+		 * @param WC_Order              $order The order object.
+		 */
+		$files = apply_filters( 'woocommerce_get_item_downloads', $files, $this, $order );
+
+		if ( ! is_array( $files ) ) {
+			return array();
 		}
+
+		return $files;
 	}

 	/**
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 9f56a27909d..a6cfa0d0613 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -12840,12 +12840,6 @@ parameters:
 			count: 1
 			path: includes/class-wc-order-item-product.php

-		-
-			message: '#^Method WC_Order_Item_Product\:\:get_item_downloads\(\) should return array but return statement is missing\.$#'
-			identifier: return.missing
-			count: 1
-			path: includes/class-wc-order-item-product.php
-
 		-
 			message: '#^Method WC_Order_Item_Product\:\:set_backorder_meta\(\) has no return type specified\.$#'
 			identifier: missingType.return