Commit ace57fa8556 for woocommerce

commit ace57fa85561c72fdc55fdebb91385a0933b63d8
Author: Peter Petrov <peter.petrov89@gmail.com>
Date:   Fri Mar 27 13:25:27 2026 +0200

    Fix: Don't print quantity output symbol if there's no quantity (#63878)

    * Fix empty quantity output handling in email order item templates

    * Add changelog

    * Fix phpcs errors

    * Fix changelog type

    * Fix incorrect docblock comment

    * Remove space from quantity prefix

    * Bump versions for updated templates

    * Bump template versions correctly

    * Update quantity symbol to match the rest

diff --git a/plugins/woocommerce/changelog/fix-email-empty-quantity-output b/plugins/woocommerce/changelog/fix-email-empty-quantity-output
new file mode 100644
index 00000000000..045b5a77d57
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-email-empty-quantity-output
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent empty quantity output markers in order email templates.
diff --git a/plugins/woocommerce/templates/emails/email-fulfillment-items.php b/plugins/woocommerce/templates/emails/email-fulfillment-items.php
index ca91ebd76c8..460374dd7c1 100644
--- a/plugins/woocommerce/templates/emails/email-fulfillment-items.php
+++ b/plugins/woocommerce/templates/emails/email-fulfillment-items.php
@@ -12,7 +12,7 @@
  *
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates\Emails
- * @version 10.1.0
+ * @version 10.8.0
  */

 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -145,7 +145,6 @@ foreach ( $items as $item_id => $item ) :
 		</td>
 		<td class="td font-family text-align-<?php echo esc_attr( $price_text_align ); ?>" style="vertical-align:middle;">
 			<?php
-			echo '&times;';
 			$qty         = $item->qty;
 			$qty_display = esc_html( $qty );
 			/**
@@ -153,7 +152,10 @@ foreach ( $items as $item_id => $item ) :
 			 *
 			 * @since 2.4.0
 			 */
-			echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item->item ) );
+			$quantity = apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item->item );
+			if ( '' !== $quantity ) {
+				echo '&times;' . wp_kses_post( $quantity );
+			}
 			?>
 		</td>
 		<td class="td font-family text-align-<?php echo esc_attr( $price_text_align ); ?>" style="vertical-align:middle;">
diff --git a/plugins/woocommerce/templates/emails/email-order-items.php b/plugins/woocommerce/templates/emails/email-order-items.php
index 1f44a1613e2..209f1ccc125 100644
--- a/plugins/woocommerce/templates/emails/email-order-items.php
+++ b/plugins/woocommerce/templates/emails/email-order-items.php
@@ -12,7 +12,7 @@
  *
  * @see     https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates\Emails
- * @version 10.7.0
+ * @version 10.8.0
  */

 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -193,7 +193,6 @@ foreach ( $items as $item_id => $item ) :
 		</td>
 		<td class="td font-family text-align-<?php echo esc_attr( $price_text_align ); ?>" style="vertical-align:middle;">
 			<?php
-			echo $email_improvements_enabled ? '&times;' : '';
 			$qty          = $item->get_quantity();
 			$refunded_qty = $order->get_qty_refunded_for_item( $item_id );

@@ -202,7 +201,19 @@ foreach ( $items as $item_id => $item ) :
 			} else {
 				$qty_display = esc_html( $qty );
 			}
-			echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item ) );
+			/**
+			 * Email Order Item Quantity hook.
+			 *
+			 * @since 2.4.0
+			 * @param string                $qty_display Item quantity.
+			 * @param WC_Order_Item_Product $item        Item object.
+			 * @return string
+			 */
+			$quantity = apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item );
+			if ( '' !== $quantity ) {
+				$quantity_prefix = $email_improvements_enabled ? '&times;' : '';
+				echo wp_kses_post( $quantity_prefix . $quantity );
+			}
 			?>
 		</td>
 		<td class="td font-family text-align-<?php echo esc_attr( $price_text_align ); ?>" style="vertical-align:middle;">
diff --git a/plugins/woocommerce/templates/emails/plain/email-fulfillment-items.php b/plugins/woocommerce/templates/emails/plain/email-fulfillment-items.php
index 49ddb6eef34..78e9b8ab765 100644
--- a/plugins/woocommerce/templates/emails/plain/email-fulfillment-items.php
+++ b/plugins/woocommerce/templates/emails/plain/email-fulfillment-items.php
@@ -12,7 +12,7 @@
  *
  * @see         https://woocommerce.com/document/template-structure/
  * @package     WooCommerce\Templates\Emails\Plain
- * @version     10.1.0
+ * @version     10.8.0
  */

 if ( ! defined( 'ABSPATH' ) ) {
@@ -60,7 +60,10 @@ foreach ( $items as $item_id => $item ) :
 		 * @param int           $quantity Item quantity.
 		 * @param WC_Order_Item $item     Item object.
 		 */
-		$product_name .= ' × ' . apply_filters( 'woocommerce_email_order_item_quantity', $item->qty, $item->item );
+		$quantity = apply_filters( 'woocommerce_email_order_item_quantity', $item->qty, $item->item );
+		if ( '' !== $quantity ) {
+			$product_name .= ' × ' . $quantity;
+		}
 		echo wp_kses_post( str_pad( wp_kses_post( $product_name ), 40 ) );
 		echo ' ';
 		echo esc_html( str_pad( wp_kses( $order->get_formatted_line_subtotal( $item->item ), array() ), 20, ' ', STR_PAD_LEFT ) ) . "\n";
diff --git a/plugins/woocommerce/templates/emails/plain/email-order-items.php b/plugins/woocommerce/templates/emails/plain/email-order-items.php
index e7235437c9a..a7d40325584 100644
--- a/plugins/woocommerce/templates/emails/plain/email-order-items.php
+++ b/plugins/woocommerce/templates/emails/plain/email-order-items.php
@@ -12,7 +12,7 @@
  *
  * @see         https://woocommerce.com/document/template-structure/
  * @package     WooCommerce\Templates\Emails\Plain
- * @version     9.8.0
+ * @version     10.8.0
  */

 use Automattic\WooCommerce\Utilities\FeaturesUtil;
@@ -52,7 +52,10 @@ foreach ( $items as $item_id => $item ) :
 			 * @param int           $quantity Item quantity.
 			 * @param WC_Order_Item $item     Item object.
 			 */
-			$product_name .= ' × ' . apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item );
+			$quantity = apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item );
+			if ( '' !== $quantity ) {
+				$product_name .= ' × ' . $quantity;
+			}
 			echo wp_kses_post( str_pad( wp_kses_post( $product_name ), 40 ) );
 			echo ' ';
 			echo esc_html( str_pad( wp_kses( $order->get_formatted_line_subtotal( $item ), array() ), 20, ' ', STR_PAD_LEFT ) ) . "\n";
@@ -81,7 +84,10 @@ foreach ( $items as $item_id => $item ) :
 			 * @param int           $quantity Item quantity.
 			 * @param WC_Order_Item $item     Item object.
 			 */
-			echo ' X ' . apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item );
+			$quantity = apply_filters( 'woocommerce_email_order_item_quantity', $item->get_quantity(), $item );
+			if ( '' !== $quantity ) {
+				echo ' × ' . $quantity;
+			}
 			echo ' = ' . $order->get_formatted_line_subtotal( $item ) . "\n";
 			// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
 		}