Commit a08bfc792eb for woocommerce
commit a08bfc792ebbda7cc5be51ac46a410278d2f72a6
Author: Parsa Kafi <parselearn@gmail.com>
Date: Tue Jun 30 16:10:57 2026 +0330
Add woocommerce_validate_phone filter for customizing of is_phone values (#65817)
diff --git a/plugins/woocommerce/changelog/add-woocommerce_validate_phone-filter-phone-validation b/plugins/woocommerce/changelog/add-woocommerce_validate_phone-filter-phone-validation
new file mode 100644
index 00000000000..99d3864fc1c
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-woocommerce_validate_phone-filter-phone-validation
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add woocommerce_validate_phone filter to allow customizing phone number validation
diff --git a/plugins/woocommerce/includes/class-wc-checkout.php b/plugins/woocommerce/includes/class-wc-checkout.php
index 3fd0f65557c..15c50e5ee71 100644
--- a/plugins/woocommerce/includes/class-wc-checkout.php
+++ b/plugins/woocommerce/includes/class-wc-checkout.php
@@ -917,10 +917,11 @@ class WC_Checkout {
}
if ( in_array( 'phone', $format, true ) ) {
+ $country = $data[ $fieldset_key . '_country' ] ?? WC()->customer->{"get_{$fieldset_key}_country"}();
// This is a safe sanitize to prevent copy-paste issues with invisible chars. Won't ensure validation.
$data[ $key ] = wc_remove_non_displayable_chars( $data[ $key ] );
- if ( $validate_fieldset && '' !== $data[ $key ] && ! WC_Validation::is_phone( $data[ $key ] ) ) {
+ if ( $validate_fieldset && '' !== $data[ $key ] && ! WC_Validation::is_phone( $data[ $key ], $country ) ) {
/* translators: %s: phone number */
$errors->add( $key . '_validation', sprintf( __( '%s is not a valid phone number.', 'woocommerce' ), '<strong>' . esc_html( $field_label ) . '</strong>' ), array( 'id' => $key ) );
}
diff --git a/plugins/woocommerce/includes/class-wc-form-handler.php b/plugins/woocommerce/includes/class-wc-form-handler.php
index 72169baf096..daea46ee9ca 100644
--- a/plugins/woocommerce/includes/class-wc-form-handler.php
+++ b/plugins/woocommerce/includes/class-wc-form-handler.php
@@ -157,7 +157,9 @@ class WC_Form_Handler {
}
break;
case 'phone':
- if ( '' !== $value && ! WC_Validation::is_phone( $value ) ) {
+ $country = wc_clean( wp_unslash( $_POST[ $address_type . '_country' ] ) );
+ $country = is_string( $country ) ? $country : '';
+ if ( '' !== $value && ! WC_Validation::is_phone( $value, $country ) ) {
/* translators: %s: Phone number. */
wc_add_notice( sprintf( __( '%s is not a valid phone number.', 'woocommerce' ), '<strong>' . $field['label'] . '</strong>' ), 'error' );
}
diff --git a/plugins/woocommerce/includes/class-wc-validation.php b/plugins/woocommerce/includes/class-wc-validation.php
index 576f97ac53a..c5cad488180 100644
--- a/plugins/woocommerce/includes/class-wc-validation.php
+++ b/plugins/woocommerce/includes/class-wc-validation.php
@@ -26,15 +26,23 @@ class WC_Validation {
/**
* Validates a phone number using a regular expression.
*
- * @param string $phone Phone number to validate.
+ * @param string $phone Phone number to validate.
+ * @param string|null $country The country code the phone is being validated for, or null if unknown.
* @return bool
*/
- public static function is_phone( $phone ) {
- if ( 0 < strlen( trim( preg_replace( '/[\s\#0-9_\-\+\/\(\)\.]/', '', $phone ) ) ) ) {
- return false;
- }
-
- return true;
+ public static function is_phone( $phone, $country = null ) {
+ $valid = 0 === strlen( trim( preg_replace( '/[\s\#0-9_\-\+\/\(\)\.]/', '', $phone ) ) );
+
+ /**
+ * Filters whether a phone number is considered valid.
+ *
+ * @since 11.0.0
+ *
+ * @param bool $valid Whether the phone number passed the default validation.
+ * @param string $phone The phone number being validated.
+ * @param string|null $country The country code the phone is being validated for, or null if unknown.
+ */
+ return (bool) apply_filters( 'woocommerce_validate_phone', $valid, $phone, $country );
}
/**
diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AbstractAddressSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AbstractAddressSchema.php
index 419e2c0a8a5..16ae7ce1d58 100644
--- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AbstractAddressSchema.php
+++ b/plugins/woocommerce/src/StoreApi/Schemas/V1/AbstractAddressSchema.php
@@ -236,7 +236,7 @@ abstract class AbstractAddressSchema extends AbstractSchema {
// This is a safe sanitize to prevent copy-paste issues with invisible chars. Won't ensure validation.
$address['phone'] = wc_remove_non_displayable_chars( $address['phone'] );
- if ( ! \WC_Validation::is_phone( $address['phone'] ) ) {
+ if ( ! \WC_Validation::is_phone( $address['phone'], $address['country'] ?? null ) ) {
$errors->add(
'invalid_phone',
__( 'The provided phone number is not valid', 'woocommerce' )
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php b/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
index a72350997f5..5f8798a56e1 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-validation-test.php
@@ -9,6 +9,60 @@
* Class WC_Validation_Test.
*/
class WC_Validation_Test extends \WC_Unit_Test_Case {
+ /**
+ * Data provider for test_is_phone().
+ */
+ public function data_provider_test_is_phone(): array {
+ return array(
+ array( true, '+00 000 00 00 000', null ),
+ array( true, '+00-000-00-00-000', null ),
+ array( true, '(000) 00 00 000', null ),
+ array( true, '+00.000.00.00.000', null ),
+ array( false, '+00 aaa dd ee fff', null ),
+ );
+ }
+
+ /**
+ * Test phone validation (default behaviour).
+ *
+ * @dataProvider data_provider_test_is_phone
+ *
+ * @param bool $expected Expected result.
+ * @param string $phone Phone number to validate.
+ * @param string|null $country Country code.
+ */
+ public function test_is_phone( bool $expected, string $phone, ?string $country ): void {
+ $this->assertSame( $expected, WC_Validation::is_phone( $phone, $country ) );
+ }
+
+ /**
+ * The woocommerce_validate_phone filter can override the validation result.
+ */
+ public function test_is_phone_filter_can_override_result(): void {
+ $callback = function ( $valid, $phone, $country ) {
+ if ( 'IR' === $country ) {
+ $phone = str_replace(
+ array( '۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹' ),
+ range( 0, 9 ),
+ $phone
+ );
+
+ return (bool) preg_match( '/^(0|0098|\+98)?(9\d{9}|[1-8]\d{9,10})$/', $phone );
+ }
+
+ return $valid;
+ };
+
+ add_filter( 'woocommerce_validate_phone', $callback, 10, 3 );
+ try {
+ $this->assertTrue( WC_Validation::is_phone( '+۹۸۹۱۵۱۱۱۲۲۳۳', 'IR' ) );
+ $this->assertTrue( WC_Validation::is_phone( '۰۰۹۸۹۱۵۱۱۱۲۲۳۳', 'IR' ) );
+ $this->assertTrue( WC_Validation::is_phone( '۰۹۱۵۱۱۱۲۲۳۳', 'IR' ) );
+ } finally {
+ remove_filter( 'woocommerce_validate_phone', $callback, 10 );
+ }
+ }
+
/**
* Data provider for test_is_postcode().
*/