Commit f118d351d5f for woocommerce

commit f118d351d5fbc764bf6f7057032e37041ae69855
Author: Ján Mikláš <neosinner@gmail.com>
Date:   Wed Apr 8 13:24:16 2026 +0200

    Harden email footer placeholder replacements against null input (#64056)

    * Harden email footer placeholder replacements against null input.

    Prevent PHP 8 TypeErrors when third-party filters return non-string footer text, and add WC_Emails tests covering null handling and site title replacement.

    Made-with: Cursor

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

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/64056-wooplug-6479-public-function-replace_placeholders-error b/plugins/woocommerce/changelog/64056-wooplug-6479-public-function-replace_placeholders-error
new file mode 100644
index 00000000000..e0000ff2ac9
--- /dev/null
+++ b/plugins/woocommerce/changelog/64056-wooplug-6479-public-function-replace_placeholders-error
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent fatal errors in email footer placeholder replacement when third-party filters return non-string values.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/class-wc-emails.php b/plugins/woocommerce/includes/class-wc-emails.php
index 48f173482cd..a41c109b0bf 100644
--- a/plugins/woocommerce/includes/class-wc-emails.php
+++ b/plugins/woocommerce/includes/class-wc-emails.php
@@ -393,6 +393,10 @@ class WC_Emails {
 	 * @return string       Email footer text with any replacements done.
 	 */
 	public function replace_placeholders( $text ) {
+		if ( ! is_string( $text ) ) {
+			$text = is_scalar( $text ) ? (string) $text : '';
+		}
+
 		$domain = wp_parse_url( home_url(), PHP_URL_HOST );

 		return str_replace(
diff --git a/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-completed-order.php b/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-completed-order.php
index e9f38834385..aa1c329fd0b 100644
--- a/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-completed-order.php
+++ b/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-completed-order.php
@@ -506,6 +506,10 @@ if ( ! class_exists( 'WC_Email_Customer_POS_Completed_Order', false ) ) :
 				return $footer_text;
 			}

+			if ( ! is_string( $footer_text ) ) {
+				$footer_text = is_scalar( $footer_text ) ? (string) $footer_text : '';
+			}
+
 			return str_replace(
 				array(
 					'{site_title}',
diff --git a/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-refunded-order.php b/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-refunded-order.php
index 9df5da7b226..7797583fd21 100644
--- a/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-refunded-order.php
+++ b/plugins/woocommerce/includes/emails/class-wc-email-customer-pos-refunded-order.php
@@ -620,6 +620,10 @@ if ( ! class_exists( 'WC_Email_Customer_POS_Refunded_Order', false ) ) :
 				return $footer_text;
 			}

+			if ( ! is_string( $footer_text ) ) {
+				$footer_text = is_scalar( $footer_text ) ? (string) $footer_text : '';
+			}
+
 			return str_replace(
 				array(
 					'{site_title}',
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php b/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
index 755c314cfda..39bceac5f07 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
@@ -41,6 +41,26 @@ class WC_Emails_Tests extends \WC_Unit_Test_Case {
 		$this->assertFalse( empty( $content ) );
 	}

+	/**
+	 * Test that replace_placeholders safely handles null values.
+	 */
+	public function test_replace_placeholders_handles_null_value() {
+		$email_object = new WC_Emails();
+		$this->assertSame( '', $email_object->replace_placeholders( null ) );
+	}
+
+	/**
+	 * Test that replace_placeholders replaces known placeholders.
+	 */
+	public function test_replace_placeholders_replaces_site_title() {
+		$email_object = new WC_Emails();
+		$placeholder  = '{site_title}';
+		$actual       = $email_object->replace_placeholders( $placeholder );
+		$expected     = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
+
+		$this->assertSame( $expected, $actual );
+	}
+
 	/**
 	 * Test that order meta function outputs linked meta.
 	 */