diff --git a/plugins/woocommerce/changelog/tweak-gtin-structured-data b/plugins/woocommerce/changelog/tweak-gtin-structured-data new file mode 100644 index 00000000000..69fc8c38c2e --- /dev/null +++ b/plugins/woocommerce/changelog/tweak-gtin-structured-data @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Validate and prepare GTIN in structured data diff --git a/plugins/woocommerce/includes/class-wc-structured-data.php b/plugins/woocommerce/includes/class-wc-structured-data.php index 98acb513224..12f1861ad9e 100644 --- a/plugins/woocommerce/includes/class-wc-structured-data.php +++ b/plugins/woocommerce/includes/class-wc-structured-data.php @@ -214,9 +214,9 @@ class WC_Structured_Data { $markup['sku'] = $product->get_id(); } - // Add GTIN only if it's a valid number. - $gtin = $product->get_global_unique_id(); - if ( $gtin && is_numeric( $gtin ) ) { + // Prepare GTIN and load it if it's valid. + $gtin = $this->prepare_gtin( $product->get_global_unique_id() ); + if ( $this->is_valid_gtin( $gtin ) ) { $markup['gtin'] = $gtin; } @@ -577,4 +577,30 @@ class WC_Structured_Data { $this->set_data( apply_filters( 'woocommerce_structured_data_order', $markup, $sent_to_admin, $order ), true ); } + + /** + * Check if a GTIN is valid. + * A valid GTIN is a string containing 8,12,13 or 14 digits. + * + * @see https://schema.org/gtin + * @param string $gtin The GTIN to check. + * @return bool True if valid. False otherwise. + */ + public function is_valid_gtin( $gtin ) { + return is_string( $gtin ) && preg_match( '/^(\d{8}|\d{12,14})$/', $gtin ); + } + + /** + * Prepare a GTIN input removing everything except numbers. + * + * @param string $gtin The GTIN to prepare. + * @return string Empty string if no GTIN is provided or the string with the replacements. + */ + public function prepare_gtin( $gtin ) { + if ( ! $gtin || ! is_string( $gtin ) ) { + return ''; + } + + return preg_replace( '/[^0-9]/', '', $gtin ); + } } diff --git a/plugins/woocommerce/tests/php/includes/class-wc-structured-data-test.php b/plugins/woocommerce/tests/php/includes/class-wc-structured-data-test.php new file mode 100644 index 00000000000..652d1199e92 --- /dev/null +++ b/plugins/woocommerce/tests/php/includes/class-wc-structured-data-test.php @@ -0,0 +1,83 @@ +structured_data = new WC_Structured_Data(); + parent::setUp(); + } + + /** + * Test is_valid_gtin function + * + * @return void + */ + public function test_is_valid_gtin(): void { + + $valid_gtins = array( + '12345678', + '123456789012', + '1234567890123', + '12345678901234', + ); + + $invalid_gtins = array( + '', + null, + false, + 12345678, + 123.4e-5, + +1234567, + 'abcdefgh', + '-9999999', + '12-45-66', + '123', + '123456789012345', + '123456789', + '1234567890', + '12 34 56 78', + '12 34 56', + '+12345678', + '123.4e-5', + ); + + foreach ( $valid_gtins as $valid_gtin ) { + $this->assertTrue( $this->structured_data->is_valid_gtin( $valid_gtin ) ); + } + + foreach ( $invalid_gtins as $invalid_gtin ) { + $this->assertFalse( $this->structured_data->is_valid_gtin( $invalid_gtin ) ); + } + } + + /** + * Test prepare_gtin function + * + * @return void + */ + public function test_prepare_gtin(): void { + $this->assertEquals( $this->structured_data->prepare_gtin( '123-456-78' ), '12345678' ); + $this->assertEquals( $this->structured_data->prepare_gtin( '-123-456-78' ), '12345678' ); + $this->assertEquals( $this->structured_data->prepare_gtin( 'GTIN: 123-456-78' ), '12345678' ); + $this->assertEquals( $this->structured_data->prepare_gtin( '123 456 78' ), '12345678' ); + $this->assertEquals( $this->structured_data->prepare_gtin( null ), '' ); + $this->assertEquals( $this->structured_data->prepare_gtin( 'GTIN' ), '' ); + $this->assertEquals( $this->structured_data->prepare_gtin( 123 ), '' ); + $this->assertEquals( $this->structured_data->prepare_gtin( array( '123-456-78', '123-456-78' ) ), '' ); + $this->assertEquals( $this->structured_data->prepare_gtin( '+12345678' ), '12345678' ); + $this->assertEquals( $this->structured_data->prepare_gtin( '123.4e-5' ), '12345' ); + } +}