Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
woocommerce
/
src
/
Internal
/
ProductCustoms
:
CustomsDataValidator.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php declare( strict_types = 1 ); namespace Automattic\WooCommerce\Internal\ProductCustoms; use WC_Countries; use WC_Data_Exception; /** * Normalizes customs data shared by product entry points. */ final class CustomsDataValidator { /** * Customs product props, in the order normalize_fields() validates them. */ public const FIELDS = array( 'customs_commodity_code', 'customs_country_of_origin', 'customs_description' ); /** * Normalizes every supplied customs field before callers apply changes. * * @since 11.3.0 * * @param array $data Product properties, including any customs fields to update. * @return array<string, string|null> Normalized customs fields only. * @throws WC_Data_Exception When any supplied customs field is invalid. */ public static function normalize_fields( array $data ): array { $normalized = array(); foreach ( self::FIELDS as $field ) { if ( array_key_exists( $field, $data ) ) { $normalized[ $field ] = self::{ str_replace( 'customs_', 'normalize_', $field ) }( $data[ $field ] ); } } return $normalized; } /** * Removes commodity code punctuation and spacing while preserving leading zeroes. * * @since 11.3.0 * * @param mixed $value Commodity code, or null to clear it. * @return string|null * @throws WC_Data_Exception When the code does not contain six to fourteen ASCII digits. */ public static function normalize_commodity_code( $value ): ?string { if ( null === $value || ( is_string( $value ) && preg_match( '/\A\s*\z/u', $value ) ) ) { return null; } if ( is_string( $value ) && preg_match( '/\A[0-9\p{P}\s]+\z/u', $value ) ) { $normalized = preg_replace( '/[\p{P}\s]+/u', '', $value ) ?? ''; if ( preg_match( '/\A[0-9]{6,14}\z/', $normalized ) ) { return $normalized; } } throw new WC_Data_Exception( 'woocommerce_product_invalid_customs_commodity_code', __( 'The customs commodity code must contain 6 to 14 digits, with optional punctuation or spaces.', 'woocommerce' ), // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Escaped when output. 400 ); } /** * Normalizes an origin country against the full WooCommerce country list. * * @since 11.3.0 * * @param mixed $value Country code, or null to clear it. * @return string|null * @throws WC_Data_Exception When the country code is not recognized. */ public static function normalize_country_of_origin( $value ): ?string { if ( null === $value ) { return null; } // Unicode-aware trim, so a pasted non-breaking space counts as empty. Null means invalid UTF-8. $trimmed = is_string( $value ) ? preg_replace( '/^\s+|\s+$/u', '', $value ) : null; if ( null !== $trimmed ) { $code = strtoupper( $trimmed ); if ( '' === $code ) { return null; } // WC()->countries is only set once WooCommerce has initialized. $countries = WC()->countries instanceof WC_Countries ? WC()->countries : new WC_Countries(); if ( $countries->country_exists( $code ) ) { return $code; } } throw new WC_Data_Exception( 'woocommerce_product_invalid_customs_country_of_origin', __( 'Select a valid country of origin.', 'woocommerce' ), // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Escaped when output. 400 ); } /** * Normalizes a plain text customs description of up to thirty-five Unicode code points. * Only letters, digits, spaces, punctuation and printable ASCII symbols are allowed, so emoji (including keycap and variation-selector sequences) and symbols such as ™ are rejected. * Angle brackets are rejected rather than stripped as markup, so text such as "Size<M shirt" isn't silently cut. * * @since 11.3.0 * * @param mixed $value Plain text description, or null to clear it. * @return string|null * @throws WC_Data_Exception When the description is not a string, has disallowed characters, or exceeds the length limit. */ public static function normalize_description( $value ): ?string { if ( null === $value || '' === $value ) { return null; } if ( is_string( $value ) && '' !== wp_check_invalid_utf8( $value ) ) { $text = trim( (string) preg_replace( '/[\r\n\t ]+/', ' ', $value ) ); if ( '' === $text ) { return null; } if ( mb_strlen( $text ) <= 35 && ! preg_match( '/[<>]|[^\x20-\x7E\p{L}\p{Mn}\p{Mc}\p{N}\p{P}\s]|[\x{FE00}-\x{FE0F}\x{E0100}-\x{E01EF}]/u', $text ) ) { return $text; } } throw new WC_Data_Exception( 'woocommerce_product_invalid_customs_description', __( 'The customs description must be plain text with no more than 35 characters, without emoji or special symbols.', 'woocommerce' ), // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Escaped when output. 400 ); } /** * Lightly normalizes a stored customs value read from the database, without validating it. * * @since 11.3.0 * * @param string $prop Customs prop name, such as customs_country_of_origin. * @param mixed $value Stored value. * @return string|null */ public static function normalize_stored_value( string $prop, $value ): ?string { if ( ! is_string( $value ) ) { return null; } $value = trim( $value ); if ( '' === $value ) { return null; } return 'customs_country_of_origin' === $prop ? strtoupper( $value ) : $value; } }