Automattic\WooCommerce\Internal\PushNotifications\Validators
PushTokenValidator{} │ WC 10.6.0
Validator class for push tokens.
Хуков нет.
Использование
$PushTokenValidator = new PushTokenValidator(); // use class methods
Методы
- public static validate( array $data, ?array $fields = array() )
- private static validate_device_locale( $value, ?array $context = array() )
- private static validate_device_uuid( $value, ?array $context = array() )
- private static validate_id( $value, ?array $context = array() )
- private static validate_metadata( $value, ?array $context = array() )
- private static validate_origin( $value, ?array $context = array() )
- private static validate_platform( $value, ?array $context = array() )
- private static validate_token( $value, ?array $context = array() )
- private static validate_user_id( $value, ?array $context = array() )
Список изменений
| С версии 10.6.0 | Введена. |
Код PushTokenValidator{} PushTokenValidator{} WC 10.8.1
class PushTokenValidator {
const VALIDATABLE_FIELDS = array(
'id',
'user_id',
'origin',
'device_uuid',
'device_locale',
'platform',
'token',
'metadata',
);
/**
* The error code to return in WP_Errors.
*
* @since 10.6.0
*/
const ERROR_CODE = 'woocommerce_invalid_data';
/**
* Validates device locale format:
* - language code (2–3 lowercase letters)
* - optionally followed by underscore and region code (2 uppercase letters)
*
* We don't have access to a locale dictionary to check valid locales, but
* it's important to note that we do not support country codes here, ONLY
* valid locales. Some languages have valid 2 and 3 character locales (like
* Japanese - ja, Arabic - ar, Asturian - ast), which is what this regex
* reflects.
*/
const DEVICE_LOCALE_FORMAT = '/^(?<language>[a-z]{2,3})(?:_(?<region>[A-Z]{2}))?$/';
/**
* The regex to use when validating device UUID format.
*
* @since 10.6.0
*/
const DEVICE_UUID_FORMAT = '/^[A-Za-z0-9._:-]+$/';
/**
* The length to validate the device UUID against.
*
* @since 10.6.0
*/
const DEVICE_UUID_MAXIMUM_LENGTH = 255;
/**
* The length to validate the token against.
*
* @since 10.6.0
*/
const TOKEN_MAXIMUM_LENGTH = 4096;
/**
* The regex to use when validating Apple token format.
*
* @since 10.6.0
*/
const TOKEN_FORMAT_APPLE = '/^[A-Fa-f0-9]{64}$/';
/**
* The regex to use when validating Android token format.
*
* @since 10.6.0
*/
const TOKEN_FORMAT_ANDROID = '/^[A-Za-z0-9=:_\-+\/]+$/';
/**
* Validates the fields defined in `$fields`, or all the list of known
* fields if `$fields` is empty.
*
* @since 10.6.0
*
* @param array $data The data to be validated.
* @param array $fields The fields to validate.
* @return bool|WP_Error
*/
public static function validate( array $data, ?array $fields = array() ) {
$fields = empty( $fields ) ? self::VALIDATABLE_FIELDS : $fields;
foreach ( $fields as $field ) {
$method = 'validate_' . $field;
if ( ! method_exists( self::class, $method ) ) {
return new WP_Error(
'woocommerce_invalid_data',
sprintf( 'Can\'t validate param \'%s\' as a validator does not exist for it.', $field )
);
}
$result = self::$method( $data[ $field ] ?? null, $data );
if ( is_wp_error( $result ) ) {
return $result;
}
}
return true;
}
/**
* Validates ID.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_id( $value, ?array $context = array() ) {
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'ID is required.' );
}
if ( ! is_numeric( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'ID must be numeric.' );
}
if ( $value <= 0 ) {
return new WP_Error( self::ERROR_CODE, 'ID must be a positive integer.' );
}
return true;
}
/**
* Validates user ID.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_user_id( $value, ?array $context = array() ) {
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'User ID is required.' );
}
if ( ! is_numeric( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'User ID must be numeric.' );
}
if ( $value <= 0 ) {
return new WP_Error( self::ERROR_CODE, 'User ID must be a positive integer.' );
}
return true;
}
/**
* Validates origin.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_origin( $value, ?array $context = array() ) {
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Origin is required.' );
}
if ( ! is_string( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Origin must be a string.' );
}
$value = trim( $value );
if ( '' === $value ) {
return new WP_Error( self::ERROR_CODE, 'Origin cannot be empty.' );
}
if ( ! in_array( $value, PushToken::ORIGINS, true ) ) {
return new WP_Error(
self::ERROR_CODE,
sprintf( 'Origin must be one of: %s.', implode( ', ', PushToken::ORIGINS ) )
);
}
return true;
}
/**
* Validates device UUID.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_device_uuid( $value, ?array $context = array() ) {
/**
* We may or may not have platform; if we don't have it, we can skip the
* platform-specific checks and allow the platform validation to trigger
* the failure.
*/
$maybe_platform = $context['platform'] ?? null;
if (
PushToken::PLATFORM_APPLE === $maybe_platform
|| PushToken::PLATFORM_ANDROID === $maybe_platform
) {
/**
* The browser platform doesn't use a device UUID, so we don't need
* to check truthiness or format unless the platform is not browser.
*/
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device UUID is required.' );
}
if ( ! is_string( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device UUID must be a string.' );
}
$value = trim( $value );
if ( '' === $value ) {
return new WP_Error( self::ERROR_CODE, 'Device UUID cannot be empty.' );
}
if ( ! preg_match( self::DEVICE_UUID_FORMAT, $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device UUID is an invalid format.' );
}
}
if (
is_string( $value )
&& strlen( $value ) > self::DEVICE_UUID_MAXIMUM_LENGTH ) {
/**
* Check maximum length for all device UUIDs sent, regardless of
* platform. We don't know for sure the value is a string as the
* check above isn't guaranteed to have run, so ensure it is a
* string before evaluating this validation rule.
*/
return new WP_Error(
self::ERROR_CODE,
sprintf( 'Device UUID exceeds maximum length of %s.', self::DEVICE_UUID_MAXIMUM_LENGTH )
);
}
return true;
}
/**
* Validates device locale.
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*
* @since 10.6.0
*/
private static function validate_device_locale( $value, ?array $context = array() ) {
if ( ! isset( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device locale is required.' );
}
if ( ! is_string( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device locale must be a string.' );
}
$value = trim( $value );
if ( '' === $value ) {
return new WP_Error( self::ERROR_CODE, 'Device locale cannot be empty.' );
}
if ( ! preg_match( self::DEVICE_LOCALE_FORMAT, $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Device locale is an invalid format.' );
}
return true;
}
/**
* Validates platform.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_platform( $value, ?array $context = array() ) {
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Platform is required.' );
}
if ( ! is_string( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Platform must be a string.' );
}
$value = trim( $value );
if ( '' === $value ) {
return new WP_Error( self::ERROR_CODE, 'Platform cannot be empty.' );
}
if ( ! in_array( $value, PushToken::PLATFORMS, true ) ) {
return new WP_Error(
self::ERROR_CODE,
sprintf( 'Platform must be one of: %s.', implode( ', ', PushToken::PLATFORMS ) )
);
}
return true;
}
/**
* Validates token value.
*
* @since 10.6.0
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*/
private static function validate_token( $value, ?array $context = array() ) {
if ( is_null( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Token is required.' );
}
if ( ! is_string( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Token must be a string.' );
}
$value = trim( $value );
if ( '' === $value ) {
return new WP_Error( self::ERROR_CODE, 'Token cannot be empty.' );
}
if ( strlen( $value ) > self::TOKEN_MAXIMUM_LENGTH ) {
return new WP_Error(
self::ERROR_CODE,
sprintf( 'Token exceeds maximum length of %s.', self::TOKEN_MAXIMUM_LENGTH )
);
}
if ( ! isset( $context['platform'] ) ) {
/**
* We don't know how to validate the format as we don't know the
* platform, so let the platform validation handle the failure.
*/
return true;
}
if (
PushToken::PLATFORM_APPLE === $context['platform']
&& ! preg_match( self::TOKEN_FORMAT_APPLE, $value )
) {
return new WP_Error( self::ERROR_CODE, 'Token is an invalid format.' );
}
if (
PushToken::PLATFORM_ANDROID === $context['platform']
&& ! preg_match( self::TOKEN_FORMAT_ANDROID, $value )
) {
return new WP_Error( self::ERROR_CODE, 'Token is an invalid format.' );
}
if ( PushToken::PLATFORM_BROWSER === $context['platform'] ) {
$token_object = json_decode( $value, true );
$endpoint = $token_object['endpoint'] ?? null;
if (
is_null( $token_object )
|| json_last_error()
|| ! isset( $token_object['keys']['auth'] )
|| ! isset( $token_object['keys']['p256dh'] )
|| ! $endpoint
|| ! wp_http_validate_url( (string) $endpoint )
|| ( wp_parse_url( (string) $endpoint, PHP_URL_SCHEME ) !== 'https' )
) {
return new WP_Error( self::ERROR_CODE, 'Token is an invalid format.' );
}
}
return true;
}
/**
* Validates metadata.
*
* @param mixed $value The value to validate.
* @param array|null $context An array of other values included as context for the validation.
* @return bool|WP_Error
*
* @since 10.6.0
*/
private static function validate_metadata( $value, ?array $context = array() ) {
if ( ! isset( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Metadata is required.' );
}
if ( ! is_array( $value ) ) {
return new WP_Error( self::ERROR_CODE, 'Metadata must be an array.' );
}
foreach ( $value as $key => $item ) {
if ( ! is_scalar( $item ) ) {
return new WP_Error( self::ERROR_CODE, 'Metadata items must be scalar values.' );
}
}
return true;
}
}