Skip to content

Commit

Permalink
Validate missing fields in Store API (#45840)
Browse files Browse the repository at this point in the history
* fix field validation

* add changelog

* fix tests

* add more checks

* fix arrays
  • Loading branch information
senadir committed Mar 27, 2024
1 parent 7d6d2c9 commit 3513af5
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Always validate missing additional fields
2 changes: 1 addition & 1 deletion plugins/woocommerce/phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
</rule>

<!-- Temporary -->
<rule ref="Generic.Arrays.DisallowShortArraySyntax.Found">
<rule ref="Universal.Arrays.DisallowShortArraySyntax.Found">
<exclude-pattern>src/Blocks/</exclude-pattern>
<exclude-pattern>src/StoreApi/</exclude-pattern>
</rule>
Expand Down
128 changes: 65 additions & 63 deletions plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CheckoutFields {
*
* @var array
*/
private $additional_fields = array();
private $additional_fields = [];

/**
* Fields locations.
Expand Down Expand Up @@ -75,8 +75,8 @@ class CheckoutFields {
*/
public function __construct( AssetDataRegistry $asset_data_registry ) {
$this->asset_data_registry = $asset_data_registry;
$this->core_fields = array(
'email' => array(
$this->core_fields = [
'email' => [
'label' => __( 'Email address', 'woocommerce' ),
'optionalLabel' => __(
'Email address (optional)',
Expand All @@ -87,8 +87,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'email',
'autocapitalize' => 'none',
'index' => 0,
),
'first_name' => array(
],
'first_name' => [
'label' => __( 'First name', 'woocommerce' ),
'optionalLabel' => __(
'First name (optional)',
Expand All @@ -99,8 +99,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'given-name',
'autocapitalize' => 'sentences',
'index' => 10,
),
'last_name' => array(
],
'last_name' => [
'label' => __( 'Last name', 'woocommerce' ),
'optionalLabel' => __(
'Last name (optional)',
Expand All @@ -111,8 +111,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'family-name',
'autocapitalize' => 'sentences',
'index' => 20,
),
'company' => array(
],
'company' => [
'label' => __( 'Company', 'woocommerce' ),
'optionalLabel' => __(
'Company (optional)',
Expand All @@ -123,8 +123,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'organization',
'autocapitalize' => 'sentences',
'index' => 30,
),
'address_1' => array(
],
'address_1' => [
'label' => __( 'Address', 'woocommerce' ),
'optionalLabel' => __(
'Address (optional)',
Expand All @@ -135,8 +135,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'address-line1',
'autocapitalize' => 'sentences',
'index' => 40,
),
'address_2' => array(
],
'address_2' => [
'label' => __( 'Apartment, suite, etc.', 'woocommerce' ),
'optionalLabel' => __(
'Apartment, suite, etc. (optional)',
Expand All @@ -147,8 +147,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'address-line2',
'autocapitalize' => 'sentences',
'index' => 50,
),
'country' => array(
],
'country' => [
'label' => __( 'Country/Region', 'woocommerce' ),
'optionalLabel' => __(
'Country/Region (optional)',
Expand All @@ -158,8 +158,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'hidden' => false,
'autocomplete' => 'country',
'index' => 50,
),
'city' => array(
],
'city' => [
'label' => __( 'City', 'woocommerce' ),
'optionalLabel' => __(
'City (optional)',
Expand All @@ -170,8 +170,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'address-level2',
'autocapitalize' => 'sentences',
'index' => 70,
),
'state' => array(
],
'state' => [
'label' => __( 'State/County', 'woocommerce' ),
'optionalLabel' => __(
'State/County (optional)',
Expand All @@ -182,8 +182,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'address-level1',
'autocapitalize' => 'sentences',
'index' => 80,
),
'postcode' => array(
],
'postcode' => [
'label' => __( 'Postal code', 'woocommerce' ),
'optionalLabel' => __(
'Postal code (optional)',
Expand All @@ -194,8 +194,8 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'postal-code',
'autocapitalize' => 'characters',
'index' => 90,
),
'phone' => array(
],
'phone' => [
'label' => __( 'Phone', 'woocommerce' ),
'optionalLabel' => __(
'Phone (optional)',
Expand All @@ -207,15 +207,15 @@ public function __construct( AssetDataRegistry $asset_data_registry ) {
'autocomplete' => 'tel',
'autocapitalize' => 'characters',
'index' => 100,
),
);
],
];

$this->fields_locations = array(
$this->fields_locations = [
// omit email from shipping and billing fields.
'address' => array_merge( \array_diff_key( array_keys( $this->core_fields ), array( 'email' ) ) ),
'contact' => array( 'email' ),
'additional' => array(),
);
'additional' => [],
];

add_filter( 'woocommerce_get_country_locale_default', array( $this, 'update_default_locale_with_fields' ) );
}
Expand Down Expand Up @@ -296,7 +296,7 @@ public function register_checkout_field( $options ) {
// The above validate_options function ensures these options are valid. Type might not be supplied but then it defaults to text.
$field_data = wp_parse_args(
$options,
array(
[
'id' => '',
'label' => '',
'optionalLabel' => sprintf(
Expand All @@ -308,11 +308,11 @@ public function register_checkout_field( $options ) {
'type' => 'text',
'hidden' => false,
'required' => false,
'attributes' => array(),
'attributes' => [],
'show_in_order_confirmation' => true,
'sanitize_callback' => array( $this, 'default_sanitize_callback' ),
'validate_callback' => array( $this, 'default_validate_callback' ),
)
]
);

$field_data['attributes'] = $this->register_field_attributes( $field_data['id'], $field_data['attributes'] );
Expand Down Expand Up @@ -452,8 +452,8 @@ private function process_select_field( $field_data, $options ) {
_doing_it_wrong( '__experimental_woocommerce_blocks_register_checkout_field', esc_html( $message ), '8.6.0' );
return false;
}
$cleaned_options = array();
$added_values = array();
$cleaned_options = [];
$added_values = [];

// Check all entries in $options['options'] has a key and value member.
foreach ( $options['options'] as $option ) {
Expand All @@ -474,23 +474,23 @@ private function process_select_field( $field_data, $options ) {

$added_values[] = $sanitized_value;

$cleaned_options[] = array(
$cleaned_options[] = [
'value' => $sanitized_value,
'label' => $sanitized_label,
);
];
}

$field_data['options'] = $cleaned_options;

// If the field is not required, inject an empty option at the start.
if ( isset( $field_data['required'] ) && false === $field_data['required'] && ! in_array( '', $added_values, true ) ) {
$field_data['options'] = array_merge(
array(
array(
[
[
'value' => '',
'label' => '',
),
),
],
],
$field_data['options']
);
}
Expand Down Expand Up @@ -542,18 +542,18 @@ private function register_field_attributes( $id, $attributes ) {
}

// These are formatted in camelCase because React components expect them that way.
$allowed_attributes = array(
$allowed_attributes = [
'maxLength',
'readOnly',
'pattern',
'autocomplete',
'autocapitalize',
'title',
);
];

$valid_attributes = array_filter(
$attributes,
function( $_, $key ) use ( $allowed_attributes ) {
function ( $_, $key ) use ( $allowed_attributes ) {
return in_array( $key, $allowed_attributes, true ) || strpos( $key, 'aria-' ) === 0 || strpos( $key, 'data-' ) === 0;
},
ARRAY_FILTER_USE_BOTH
Expand All @@ -568,7 +568,7 @@ function( $_, $key ) use ( $allowed_attributes ) {

// Escape attributes to remove any malicious code and return them.
return array_map(
function( $value ) {
function ( $value ) {
return esc_attr( $value );
},
$valid_attributes
Expand Down Expand Up @@ -756,7 +756,7 @@ public function get_fields_for_location( $location ) {

return array_filter(
$this->get_additional_fields(),
function( $key ) use ( $order_fields_keys ) {
function ( $key ) use ( $order_fields_keys ) {
return in_array( $key, $order_fields_keys, true );
},
ARRAY_FILTER_USE_KEY
Expand Down Expand Up @@ -923,7 +923,7 @@ private function set_array_meta( $key, $value, $object ) {
$meta_data = $object->get_meta( $meta_key, true );

if ( ! is_array( $meta_data ) ) {
$meta_data = array();
$meta_data = [];
}

$meta_data[ $key ] = $value;
Expand Down Expand Up @@ -1001,11 +1001,12 @@ private function get_field_from_object( $key, $object, $group = '' ) {
* @return array An array of fields.
*/
public function get_all_fields_from_customer( $customer, $all = false ) {
$meta_data = array(
'billing' => array(),
'shipping' => array(),
'additional' => array(),
);
$meta_data = [
'billing' => [],
'shipping' => [],
'additional' => [],
];

if ( $customer instanceof WC_Customer ) {
$meta_data['billing'] = $customer->get_meta( self::BILLING_FIELDS_KEY, true );
$meta_data['shipping'] = $customer->get_meta( self::SHIPPING_FIELDS_KEY, true );
Expand All @@ -1023,11 +1024,12 @@ public function get_all_fields_from_customer( $customer, $all = false ) {
* @return array An array of fields.
*/
public function get_all_fields_from_order( $order, $all = false ) {
$meta_data = array(
'billing' => array(),
'shipping' => array(),
'additional' => array(),
);
$meta_data = [
'billing' => [],
'shipping' => [],
'additional' => [],
];

if ( $order instanceof WC_Order ) {
$meta_data['billing'] = $order->get_meta( self::BILLING_FIELDS_KEY, true );
$meta_data['shipping'] = $order->get_meta( self::SHIPPING_FIELDS_KEY, true );
Expand All @@ -1045,11 +1047,11 @@ public function get_all_fields_from_order( $order, $all = false ) {
* @return array An array of fields.
*/
private function format_meta_data( $meta, $all = false ) {
$billing_fields = $meta['billing'] ?? array();
$shipping_fields = $meta['shipping'] ?? array();
$additional_fields = $meta['additional'] ?? array();
$billing_fields = $meta['billing'] ?? [];
$shipping_fields = $meta['shipping'] ?? [];
$additional_fields = $meta['additional'] ?? [];

$fields = array();
$fields = [];

if ( is_array( $billing_fields ) ) {
foreach ( $billing_fields as $key => $value ) {
Expand Down Expand Up @@ -1095,7 +1097,7 @@ public function filter_fields_for_customer( $fields ) {
);
return array_filter(
$fields,
function( $key ) use ( $customer_fields_keys ) {
function ( $key ) use ( $customer_fields_keys ) {
if ( 0 === strpos( $key, '/billing/' ) ) {
$key = str_replace( '/billing/', '', $key );
} elseif ( 0 === strpos( $key, '/shipping/' ) ) {
Expand All @@ -1117,7 +1119,7 @@ function( $key ) use ( $customer_fields_keys ) {
public function filter_fields_for_location( $fields, $location ) {
return array_filter(
$fields,
function( $key ) use ( $location ) {
function ( $key ) use ( $location ) {
if ( 0 === strpos( $key, '/billing/' ) ) {
$key = str_replace( '/billing/', '', $key );
} elseif ( 0 === strpos( $key, '/shipping/' ) ) {
Expand All @@ -1138,7 +1140,7 @@ function( $key ) use ( $location ) {
public function filter_fields_for_order_confirmation( $fields ) {
return array_filter(
$fields,
function( $field ) {
function ( $field ) {
return ! empty( $field['show_in_order_confirmation'] );
}
);
Expand All @@ -1155,7 +1157,7 @@ function( $field ) {
*/
public function get_order_additional_fields_with_values( $order, $location, $group = '', $context = 'edit' ) {
$fields = $this->get_fields_for_location( $location );
$fields_with_values = array();
$fields_with_values = [];

foreach ( $fields as $field_key => $field ) {
$value = $this->get_field_from_order( $field_key, $order, $group );
Expand Down

0 comments on commit 3513af5

Please sign in to comment.