From 1eb90c1d538a7828bfe1166a9295b7cf1433f815 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Tue, 16 Nov 2021 11:19:56 +0000 Subject: [PATCH] Fix state validation to compare state codes, and only validate if a country is given (#5132) * Only get valid states from wc if there is a country * Shared validation logic which uses keys --- .../Schemas/AbstractAddressSchema.php | 48 +++++++++++++++---- src/StoreApi/Schemas/BillingAddressSchema.php | 3 +- .../Schemas/ShippingAddressSchema.php | 3 +- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/StoreApi/Schemas/AbstractAddressSchema.php b/src/StoreApi/Schemas/AbstractAddressSchema.php index 5b70759902e..c17f497ced5 100644 --- a/src/StoreApi/Schemas/AbstractAddressSchema.php +++ b/src/StoreApi/Schemas/AbstractAddressSchema.php @@ -107,18 +107,47 @@ public function sanitize_callback( $address, $request, $param ) { } /** - * Format a state based on the country. If country has defined states, will return an upper case state code. + * Get list of states for a country. + * + * @param string $country Country code. + * @return array Array of state names indexed by state keys. + */ + protected function get_states_for_country( $country ) { + return $country ? array_filter( (array) \wc()->countries->get_states( $country ) ) : []; + } + + /** + * Validate provided state against a countries list of defined states. + * + * If there are no defined states for a country, any given state is valid. + * + * @param string $state State name or code (sanitized). + * @param string $country Country code. + * @return boolean Valid or not valid. + */ + protected function validate_state( $state, $country ) { + $states = $this->get_states_for_country( $country ); + + if ( count( $states ) && ! in_array( \wc_strtoupper( $state ), array_map( '\wc_strtoupper', array_keys( $states ) ), true ) ) { + return false; + } + + return true; + } + + /** + * Format a state based on the country. If country has defined states, will return a valid upper case state code. * * @param string $state State name or code (sanitized). * @param string $country Country code. * @return string */ protected function format_state( $state, $country ) { - $states = $country ? array_filter( (array) wc()->countries->get_states( $country ) ) : []; + $states = $this->get_states_for_country( $country ); if ( count( $states ) ) { - $state = wc_strtoupper( $state ); - $state_values = array_map( 'wc_strtoupper', array_flip( array_map( 'wc_strtoupper', $states ) ) ); + $state = \wc_strtoupper( $state ); + $state_values = array_map( 'wc_strtoupper', array_flip( array_map( '\wc_strtoupper', $states ) ) ); if ( isset( $state_values[ $state ] ) ) { // Convert to state code if a state name was provided. @@ -163,15 +192,14 @@ public function validate_callback( $address, $request, $param ) { return $errors; } - $states = array_filter( array_keys( (array) wc()->countries->get_states( $address['country'] ) ) ); - - if ( ! empty( $address['state'] ) && count( $states ) && ! in_array( $address['state'], $states, true ) ) { + if ( ! empty( $address['state'] ) && ! $this->validate_state( $address['state'], $address['country'] ) ) { $errors->add( 'invalid_state', sprintf( - /* translators: %s valid states */ - __( 'The provided state is not valid. Must be one of: %s', 'woo-gutenberg-products-block' ), - implode( ', ', $states ) + /* translators: %1$s given state, %2$s valid states */ + __( 'The provided state (%1$s) is not valid. Must be one of: %2$s', 'woo-gutenberg-products-block' ), + esc_html( $address['state'] ), + implode( ', ', array_keys( $this->get_states_for_country( $address['country'] ) ) ) ) ); } diff --git a/src/StoreApi/Schemas/BillingAddressSchema.php b/src/StoreApi/Schemas/BillingAddressSchema.php index d8c899e381d..89ab1dd12df 100644 --- a/src/StoreApi/Schemas/BillingAddressSchema.php +++ b/src/StoreApi/Schemas/BillingAddressSchema.php @@ -95,9 +95,8 @@ public function get_item_response( $address ) { if ( ( $address instanceof \WC_Customer || $address instanceof \WC_Order ) ) { $billing_country = $address->get_billing_country(); $billing_state = $address->get_billing_state(); - $valid_states = array_filter( (array) wc()->countries->get_states( $billing_country ) ); - if ( ! empty( $billing_state ) && count( $valid_states ) && ! in_array( $billing_state, $valid_states, true ) ) { + if ( ! $this->validate_state( $billing_state, $billing_country ) ) { $billing_state = ''; } diff --git a/src/StoreApi/Schemas/ShippingAddressSchema.php b/src/StoreApi/Schemas/ShippingAddressSchema.php index fb3175afd0b..af3f2c1618e 100644 --- a/src/StoreApi/Schemas/ShippingAddressSchema.php +++ b/src/StoreApi/Schemas/ShippingAddressSchema.php @@ -44,9 +44,8 @@ public function get_item_response( $address ) { $shipping_country = $address->get_shipping_country(); $shipping_state = $address->get_shipping_state(); - $valid_states = array_filter( (array) wc()->countries->get_states( $shipping_country ) ); - if ( ! empty( $shipping_state ) && count( $valid_states ) && ! in_array( $shipping_state, $valid_states, true ) ) { + if ( ! $this->validate_state( $shipping_state, $shipping_country ) ) { $shipping_state = ''; }