From ef8f122bb75f000a43a0ffa02b14088a1545f0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lys=C3=BD?= Date: Fri, 1 Nov 2024 21:11:11 +0100 Subject: [PATCH] Migrate email editor Validator to WP Coding Standard [MAILPOET-6240] --- .../Templates/class-template-preview.php | 2 +- .../src/Engine/class-email-api-controller.php | 2 +- .../src/Engine/class-email-styles-schema.php | 2 +- .../Validator/Schema/class-any-of-schema.php | 52 ++++-- .../Validator/Schema/class-array-schema.php | 44 ++++- .../Validator/Schema/class-boolean-schema.php | 18 +- .../Validator/Schema/class-integer-schema.php | 61 +++++-- .../Validator/Schema/class-null-schema.php | 18 +- .../Validator/Schema/class-number-schema.php | 61 +++++-- .../Validator/Schema/class-object-schema.php | 64 +++++-- .../Validator/Schema/class-one-of-schema.php | 52 ++++-- .../Validator/Schema/class-string-schema.php | 68 ++++++-- .../src/Validator/class-builder.php | 57 +++++- .../src/Validator/class-schema.php | 155 ++++++++++++---- .../Validator/class-validation-exception.php | 39 ++++- .../src/Validator/class-validator.php | 165 ++++++++++-------- 16 files changed, 641 insertions(+), 219 deletions(-) diff --git a/packages/php/email-editor/src/Engine/Templates/class-template-preview.php b/packages/php/email-editor/src/Engine/Templates/class-template-preview.php index 6148726a2b..9648f712dd 100644 --- a/packages/php/email-editor/src/Engine/Templates/class-template-preview.php +++ b/packages/php/email-editor/src/Engine/Templates/class-template-preview.php @@ -63,7 +63,7 @@ class Template_Preview { array( 'get_callback' => array( $this, 'get_email_theme_preview_css' ), 'update_callback' => null, - 'schema' => Builder::string()->toArray(), + 'schema' => Builder::string()->to_array(), ) ); } diff --git a/packages/php/email-editor/src/Engine/class-email-api-controller.php b/packages/php/email-editor/src/Engine/class-email-api-controller.php index 2a1bb5fcfa..c9202b7778 100644 --- a/packages/php/email-editor/src/Engine/class-email-api-controller.php +++ b/packages/php/email-editor/src/Engine/class-email-api-controller.php @@ -41,6 +41,6 @@ class Email_Api_Controller { * @return array */ public function get_email_data_schema(): array { - return Builder::object()->toArray(); + return Builder::object()->to_array(); } } diff --git a/packages/php/email-editor/src/Engine/class-email-styles-schema.php b/packages/php/email-editor/src/Engine/class-email-styles-schema.php index 6421dfac72..655e9b3b1f 100644 --- a/packages/php/email-editor/src/Engine/class-email-styles-schema.php +++ b/packages/php/email-editor/src/Engine/class-email-styles-schema.php @@ -109,6 +109,6 @@ class Email_Styles_Schema { ) )->nullable(), ) - )->toArray(); + )->to_array(); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php b/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php index fca9e7ea06..338011d4ea 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-any-of-schema.php @@ -1,40 +1,64 @@ - array(), ); - /** @param Schema[] $schemas */ + /** + * Any_Of_Schema constructor. + * + * @param Schema[] $schemas List of schemas. + */ public function __construct( array $schemas ) { foreach ( $schemas as $schema ) { - $this->schema['anyOf'][] = $schema->toArray(); + $this->schema['anyOf'][] = $schema->to_array(); } } + /** + * Returns the schema as an array. + */ public function nullable(): self { - $null = array( 'type' => 'null' ); - $anyOf = $this->schema['anyOf']; - $value = in_array( $null, $anyOf, true ) ? $anyOf : array_merge( $anyOf, array( $null ) ); - return $this->updateSchemaProperty( 'anyOf', $value ); + $null = array( 'type' => 'null' ); + $any_of = $this->schema['anyOf']; + $value = in_array( $null, $any_of, true ) ? $any_of : array_merge( $any_of, array( $null ) ); + return $this->update_schema_property( 'anyOf', $value ); } - public function nonNullable(): self { - $null = array( 'type' => 'null' ); - $anyOf = $this->schema['anyOf']; - $value = array_filter( - $anyOf, + /** + * Returns the schema as an array. + */ + public function non_nullable(): self { + $null = array( 'type' => 'null' ); + $any_of = $this->schema['any_of']; + $value = array_filter( + $any_of, function ( $item ) use ( $null ) { return $item !== $null; } ); - return $this->updateSchemaProperty( 'anyOf', $value ); + return $this->update_schema_property( 'any_of', $value ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-array-schema.php b/packages/php/email-editor/src/Validator/Schema/class-array-schema.php index 7d3500950a..e864bf277a 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-array-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-array-schema.php @@ -1,28 +1,60 @@ - 'array', ); + /** + * Sets the schema for the items in the array. + * + * @param Schema $schema Schema for the items in the array. + */ public function items( Schema $schema ): self { - return $this->updateSchemaProperty( 'items', $schema->toArray() ); + return $this->update_schema_property( 'items', $schema->to_array() ); } + /** + * Sets the minimum number of items in the array. + * + * @param int $value Minimum number of items in the array. + */ public function minItems( int $value ): self { - return $this->updateSchemaProperty( 'minItems', $value ); + return $this->update_schema_property( 'minItems', $value ); } + /** + * Sets the maximum number of items in the array. + * + * @param int $value Maximum number of items in the array. + */ public function maxItems( int $value ): self { - return $this->updateSchemaProperty( 'maxItems', $value ); + return $this->update_schema_property( 'maxItems', $value ); } + /** + * Sets the uniqueItems property to true. + */ public function uniqueItems(): self { - return $this->updateSchemaProperty( 'uniqueItems', true ); + return $this->update_schema_property( 'uniqueItems', true ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php b/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php index e314e1567d..3407ecb4e5 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-boolean-schema.php @@ -1,11 +1,25 @@ - 'boolean', ); diff --git a/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php b/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php index 14bcf32359..1fb82a6a8e 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-integer-schema.php @@ -1,36 +1,75 @@ - 'integer', ); + /** + * Sets the minimum value of the integer. + * + * @param int $value Minimum value of the integer. + */ public function minimum( int $value ): self { - return $this->updateSchemaProperty( 'minimum', $value ) - ->unsetSchemaProperty( 'exclusiveMinimum' ); + return $this->update_schema_property( 'minimum', $value ) + ->unset_schema_property( 'exclusiveMinimum' ); } + /** + * Sets the exclusiveMinimum property to true. + * + * @param int $value Minimum value of the integer. + */ public function exclusiveMinimum( int $value ): self { - return $this->updateSchemaProperty( 'minimum', $value ) - ->updateSchemaProperty( 'exclusiveMinimum', true ); + return $this->update_schema_property( 'minimum', $value ) + ->update_schema_property( 'exclusiveMinimum', true ); } + /** + * Sets the maximum value of the integer. + * + * @param int $value Maximum value of the integer. + */ public function maximum( int $value ): self { - return $this->updateSchemaProperty( 'maximum', $value ) - ->unsetSchemaProperty( 'exclusiveMaximum' ); + return $this->update_schema_property( 'maximum', $value ) + ->unset_schema_property( 'exclusiveMaximum' ); } + /** + * Sets the exclusiveMaximum property to true. + * + * @param int $value Maximum value of the integer. + */ public function exclusiveMaximum( int $value ): self { - return $this->updateSchemaProperty( 'maximum', $value ) - ->updateSchemaProperty( 'exclusiveMaximum', true ); + return $this->update_schema_property( 'maximum', $value ) + ->update_schema_property( 'exclusiveMaximum', true ); } + /** + * Sets the multipleOf property. + * + * @param int $value Multiple of the integer. + */ public function multipleOf( int $value ): self { - return $this->updateSchemaProperty( 'multipleOf', $value ); + return $this->update_schema_property( 'multipleOf', $value ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-null-schema.php b/packages/php/email-editor/src/Validator/Schema/class-null-schema.php index 4d5bf7e84e..722c27e81f 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-null-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-null-schema.php @@ -1,11 +1,25 @@ - 'null', ); diff --git a/packages/php/email-editor/src/Validator/Schema/class-number-schema.php b/packages/php/email-editor/src/Validator/Schema/class-number-schema.php index 69423a2bd8..20fad74e7a 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-number-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-number-schema.php @@ -1,36 +1,75 @@ - 'number', ); + /** + * Sets the minimum value of the number. + * + * @param float $value Minimum value of the number. + */ public function minimum( float $value ): self { - return $this->updateSchemaProperty( 'minimum', $value ) - ->unsetSchemaProperty( 'exclusiveMinimum' ); + return $this->update_schema_property( 'minimum', $value ) + ->unset_schema_property( 'exclusiveMinimum' ); } + /** + * Sets the exclusiveMinimum property to true. + * + * @param float $value Minimum value of the number. + */ public function exclusiveMinimum( float $value ): self { - return $this->updateSchemaProperty( 'minimum', $value ) - ->updateSchemaProperty( 'exclusiveMinimum', true ); + return $this->update_schema_property( 'minimum', $value ) + ->update_schema_property( 'exclusiveMinimum', true ); } + /** + * Sets the maximum value of the number. + * + * @param float $value Maximum value of the number. + */ public function maximum( float $value ): self { - return $this->updateSchemaProperty( 'maximum', $value ) - ->unsetSchemaProperty( 'exclusiveMaximum' ); + return $this->update_schema_property( 'maximum', $value ) + ->unset_schema_property( 'exclusiveMaximum' ); } + /** + * Sets the exclusiveMaximum property to true. + * + * @param float $value Maximum value of the number. + */ public function exclusiveMaximum( float $value ): self { - return $this->updateSchemaProperty( 'maximum', $value ) - ->updateSchemaProperty( 'exclusiveMaximum', true ); + return $this->update_schema_property( 'maximum', $value ) + ->update_schema_property( 'exclusiveMaximum', true ); } + /** + * Sets the multipleOf property. + * + * @param float $value Multiple of the number. + */ public function multipleOf( float $value ): self { - return $this->updateSchemaProperty( 'multipleOf', $value ); + return $this->update_schema_property( 'multipleOf', $value ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-object-schema.php b/packages/php/email-editor/src/Validator/Schema/class-object-schema.php index 6e47da9f69..10d7915263 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-object-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-object-schema.php @@ -1,56 +1,92 @@ - 'object', ); - /** @param array $properties */ + /** + * Set the required properties of the object. + * + * @param array $properties Required properties. + */ public function properties( array $properties ): self { - return $this->updateSchemaProperty( + return $this->update_schema_property( 'properties', array_map( function ( Schema $property ) { - return $property->toArray(); + return $property->to_array(); }, $properties ) ); } + /** + * Set the required properties of the object. + * + * @param Schema $schema Schema of the additional properties. + */ public function additionalProperties( Schema $schema ): self { - return $this->updateSchemaProperty( 'additionalProperties', $schema->toArray() ); + return $this->update_schema_property( 'additionalProperties', $schema->to_array() ); } + /** + * Disables additional properties. + */ public function disableAdditionalProperties(): self { - return $this->updateSchemaProperty( 'additionalProperties', false ); + return $this->update_schema_property( 'additionalProperties', false ); } /** * Keys of $properties are regular expressions without leading/trailing delimiters. * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#patternproperties * - * @param array $properties + * @param array $properties Regular expressions and their schemas. */ public function patternProperties( array $properties ): self { - $patternProperties = array(); + $pattern_properties = array(); foreach ( $properties as $key => $value ) { - $this->validatePattern( $key ); - $patternProperties[ $key ] = $value->toArray(); + $this->validate_pattern( $key ); + $pattern_properties[ $key ] = $value->to_array(); } - return $this->updateSchemaProperty( 'patternProperties', $patternProperties ); + return $this->update_schema_property( 'patternProperties', $pattern_properties ); } + /** + * Sets the minimum number of properties in the object. + * + * @param int $value Minimum number of properties in the object. + */ public function minProperties( int $value ): self { - return $this->updateSchemaProperty( 'minProperties', $value ); + return $this->update_schema_property( 'minProperties', $value ); } + /** + * Sets the maximum number of properties in the object. + * + * @param int $value Maximum number of properties in the object. + */ public function maxProperties( int $value ): self { - return $this->updateSchemaProperty( 'maxProperties', $value ); + return $this->update_schema_property( 'maxProperties', $value ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php b/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php index 4162375394..eab8c192a8 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-one-of-schema.php @@ -1,40 +1,64 @@ - array(), ); - /** @param Schema[] $schemas */ + /** + * One_Of_Schema constructor. + * + * @param Schema[] $schemas List of schemas. + */ public function __construct( array $schemas ) { foreach ( $schemas as $schema ) { - $this->schema['oneOf'][] = $schema->toArray(); + $this->schema['oneOf'][] = $schema->to_array(); } } + /** + * Sets the schema as nullable. + */ public function nullable(): self { - $null = array( 'type' => 'null' ); - $oneOf = $this->schema['oneOf']; - $value = in_array( $null, $oneOf, true ) ? $oneOf : array_merge( $oneOf, array( $null ) ); - return $this->updateSchemaProperty( 'oneOf', $value ); + $null = array( 'type' => 'null' ); + $one_of = $this->schema['oneOf']; + $value = in_array( $null, $one_of, true ) ? $one_of : array_merge( $one_of, array( $null ) ); + return $this->update_schema_property( 'oneOf', $value ); } - public function nonNullable(): self { - $null = array( 'type' => 'null' ); - $oneOf = $this->schema['oneOf']; - $value = array_filter( - $oneOf, + /** + * Sets the schema as non-nullable. + */ + public function non_nullable(): self { + $null = array( 'type' => 'null' ); + $one_of = $this->schema['one_of']; + $value = array_filter( + $one_of, function ( $item ) use ( $null ) { return $item !== $null; } ); - return $this->updateSchemaProperty( 'oneOf', $value ); + return $this->update_schema_property( 'one_of', $value ); } } diff --git a/packages/php/email-editor/src/Validator/Schema/class-string-schema.php b/packages/php/email-editor/src/Validator/Schema/class-string-schema.php index dd444bd4d0..c1e6a8b559 100644 --- a/packages/php/email-editor/src/Validator/Schema/class-string-schema.php +++ b/packages/php/email-editor/src/Validator/Schema/class-string-schema.php @@ -1,53 +1,97 @@ - 'string', ); + /** + * Set minimum length of the string. + * + * @param int $value Minimum length. + */ public function minLength( int $value ): self { - return $this->updateSchemaProperty( 'minLength', $value ); + return $this->update_schema_property( 'minLength', $value ); } + /** + * Set maximum length of the string. + * + * @param int $value Maximum length. + */ public function maxLength( int $value ): self { - return $this->updateSchemaProperty( 'maxLength', $value ); + return $this->update_schema_property( 'maxLength', $value ); } /** * Parameter $pattern is a regular expression without leading/trailing delimiters. * See: https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#pattern + * + * @param string $pattern Regular expression pattern. */ public function pattern( string $pattern ): self { - $this->validatePattern( $pattern ); - return $this->updateSchemaProperty( 'pattern', $pattern ); + $this->validate_pattern( $pattern ); + return $this->update_schema_property( 'pattern', $pattern ); } + /** + * Set the format of the string according to DateTime. + */ public function formatDateTime(): self { - return $this->updateSchemaProperty( 'format', 'date-time' ); + return $this->update_schema_property( 'format', 'date-time' ); } + /** + * Set the format of the string according to email. + */ public function formatEmail(): self { - return $this->updateSchemaProperty( 'format', 'email' ); + return $this->update_schema_property( 'format', 'email' ); } + /** + * Set the format of the string according to Hex color. + */ public function formatHexColor(): self { - return $this->updateSchemaProperty( 'format', 'hex-color' ); + return $this->update_schema_property( 'format', 'hex-color' ); } + /** + * Set the format of the string according to IP address. + */ public function formatIp(): self { - return $this->updateSchemaProperty( 'format', 'ip' ); + return $this->update_schema_property( 'format', 'ip' ); } + /** + * Set the format of the string according to uri. + */ public function formatUri(): self { - return $this->updateSchemaProperty( 'format', 'uri' ); + return $this->update_schema_property( 'format', 'uri' ); } + /** + * Set the format of the string according to uuid. + */ public function formatUuid(): self { - return $this->updateSchemaProperty( 'format', 'uuid' ); + return $this->update_schema_property( 'format', 'uuid' ); } } diff --git a/packages/php/email-editor/src/Validator/class-builder.php b/packages/php/email-editor/src/Validator/class-builder.php index cbfd47f923..3c929089e0 100644 --- a/packages/php/email-editor/src/Validator/class-builder.php +++ b/packages/php/email-editor/src/Validator/class-builder.php @@ -1,5 +1,11 @@ -items( $items ) : $array; } - /** @param array|null $properties */ + /** + * Creates a schema for an object. + * + * @param array|null $properties Properties of the object. + */ public static function object( array $properties = null ): Object_Schema { $object = new Object_Schema(); - return $properties === null ? $object : $object->properties( $properties ); + return null === $properties ? $object : $object->properties( $properties ); } - /** @param Schema[] $schemas */ - public static function oneOf( array $schemas ): One_Of_Schema { + /** + * Creates a schema that allows a value to match one of the given schemas. + * + * @param Schema[] $schemas List of schemas. + */ + public static function one_of( array $schemas ): One_Of_Schema { return new One_Of_Schema( $schemas ); } - /** @param Schema[] $schemas */ - public static function anyOf( array $schemas ): Any_Of_Schema { + /** + * Creates a schema that allows a value to match any of the given schemas. + * + * @param Schema[] $schemas List of schemas. + */ + public static function any_of( array $schemas ): Any_Of_Schema { return new Any_Of_Schema( $schemas ); } } diff --git a/packages/php/email-editor/src/Validator/class-schema.php b/packages/php/email-editor/src/Validator/class-schema.php index a87e948059..c41d2b518a 100644 --- a/packages/php/email-editor/src/Validator/class-schema.php +++ b/packages/php/email-editor/src/Validator/class-schema.php @@ -1,96 +1,177 @@ -schema['type'] ?? array( 'null' ); - return $this->updateSchemaProperty( 'type', is_array( $type ) ? $type : array( $type, 'null' ) ); + return $this->update_schema_property( 'type', is_array( $type ) ? $type : array( $type, 'null' ) ); } - /** @return static */ - public function nonNullable() { + /** + * Sets the schema as non-nullable. + * + * @return static + */ + public function non_nullable() { $type = $this->schema['type'] ?? null; - return $type === null - ? $this->unsetSchemaProperty( 'type' ) - : $this->updateSchemaProperty( 'type', is_array( $type ) ? $type[0] : $type ); + return null === $type + ? $this->unset_schema_property( 'type' ) + : $this->update_schema_property( 'type', is_array( $type ) ? $type[0] : $type ); } - /** @return static */ + /** + * Sets the schema as required. + * + * @return static + */ public function required() { - return $this->updateSchemaProperty( 'required', true ); + return $this->update_schema_property( 'required', true ); } - /** @return static */ + /** + * Unsets the required property. + * + * @return static + */ public function optional() { - return $this->unsetSchemaProperty( 'required' ); + return $this->unset_schema_property( 'required' ); } - /** @return static */ + /** + * Set the title of the schema. + * + * @param string $title Title. + * @return static + */ public function title( string $title ) { - return $this->updateSchemaProperty( 'title', $title ); + return $this->update_schema_property( 'title', $title ); } - /** @return static */ + /** + * Set the description of the schema. + * + * @param string $description Description. + * @return static + */ public function description( string $description ) { - return $this->updateSchemaProperty( 'description', $description ); + return $this->update_schema_property( 'description', $description ); } - /** @return static */ - public function default( $default ) { - return $this->updateSchemaProperty( 'default', $default ); + /** + * Set the default value. + * + * @param mixed $default_value Default value. + * @return static + */ + public function default( $default_value ) { + return $this->update_schema_property( 'default', $default_value ); } - /** @return static */ + /** + * Set the field name and value. + * + * @param string $name Name of the field. + * @param mixed $value Value of the field. + * @return static + * @throws \Exception When the field name is reserved. + */ public function field( string $name, $value ) { - if ( in_array( $name, $this->getReservedKeywords(), true ) ) { - throw new \Exception( "Field name '$name' is reserved" ); + if ( in_array( $name, $this->get_reserved_keywords(), true ) ) { + throw new \Exception( \esc_html( "Field name '$name' is reserved" ) ); } - return $this->updateSchemaProperty( $name, $value ); + return $this->update_schema_property( $name, $value ); } - public function toArray(): array { + /** + * Returns the schema as an array. + */ + public function to_array(): array { return $this->schema; } - public function toString(): string { - $json = json_encode( $this->schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION ); + /** + * Returns the schema as a JSON string. + * + * @throws \Exception When the schema cannot be converted to JSON. + */ + public function to_string(): string { + $json = wp_json_encode( $this->schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION ); $error = json_last_error(); - if ( $error || $json === false ) { - throw new \Exception( json_last_error_msg(), (string) $error ); + if ( $error || false === $json ) { + throw new \Exception( \esc_html( json_last_error_msg() ), \esc_html( (string) $error ) ); } return $json; } - /** @return static */ - protected function updateSchemaProperty( string $name, $value ) { + /** + * Updates the schema property. + * + * @param string $name Property name. + * @param mixed $value Property value. + * @return static + */ + protected function update_schema_property( string $name, $value ) { $clone = clone $this; $clone->schema[ $name ] = $value; return $clone; } - /** @return static */ - protected function unsetSchemaProperty( string $name ) { + /** + * Unsets the schema property. + * + * @param string $name Property name. + */ + protected function unset_schema_property( string $name ) { $clone = clone $this; unset( $clone->schema[ $name ] ); return $clone; } - protected function getReservedKeywords(): array { + /** + * Returns reserved keywords. + * + * @return string[] + */ + protected function get_reserved_keywords(): array { return rest_get_allowed_schema_keywords(); } - protected function validatePattern( string $pattern ): void { + /** + * Validates the regular expression pattern. + * + * @param string $pattern Regular expression pattern. + * @throws \Exception When the pattern is invalid. + */ + protected function validate_pattern( string $pattern ): void { $escaped = str_replace( '#', '\\#', $pattern ); $regex = "#$escaped#u"; - if ( @preg_match( $regex, '' ) === false ) { - throw new \Exception( "Invalid regular expression '$regex'" ); + if ( @preg_match( $regex, '' ) === false ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged + throw new \Exception( \esc_html( "Invalid regular expression '$regex'" ) ); } } } diff --git a/packages/php/email-editor/src/Validator/class-validation-exception.php b/packages/php/email-editor/src/Validator/class-validation-exception.php index ccb69633c6..e91f6955f3 100644 --- a/packages/php/email-editor/src/Validator/class-validation-exception.php +++ b/packages/php/email-editor/src/Validator/class-validation-exception.php @@ -1,22 +1,43 @@ -withMessage( $wpError->get_error_message() ); - $exception->wpError = $wpError; + /** + * Creates a new instance of the exception. + * + * @param WP_Error $wp_error WP_Error instance. + */ + public static function create_from_wp_error( WP_Error $wp_error ): self { + $exception = self::create() + ->withMessage( $wp_error->get_error_message() ); + $exception->wp_error = $wp_error; return $exception; } - public function getWpError(): WP_Error { - return $this->wpError; + /** + * Returns the WP_Error instance. + */ + public function get_wp_error(): WP_Error { + return $this->wp_error; } } diff --git a/packages/php/email-editor/src/Validator/class-validator.php b/packages/php/email-editor/src/Validator/class-validator.php index 617486312a..801f1d1f23 100644 --- a/packages/php/email-editor/src/Validator/class-validator.php +++ b/packages/php/email-editor/src/Validator/class-validator.php @@ -1,5 +1,11 @@ -wp = $wp; + /** + * Strict validation & sanitization implementation. + * It only coerces int to float (e.g. 5 to 5.0). + * + * @param Schema $schema The schema to validate against. + * @param mixed $value The value to validate. + * @param string $param_name The parameter name. + * @return mixed + */ + public function validate( Schema $schema, $value, string $param_name = 'value' ) { + return $this->validate_schema_array( $schema->to_array(), $value, $param_name ); } /** * Strict validation & sanitization implementation. * It only coerces int to float (e.g. 5 to 5.0). * - * @param mixed $value + * @param array $schema The array must follow the format, which is returned from Schema::toArray(). + * @param mixed $value The value to validate. + * @param string $param_name The parameter name. * @return mixed + * @throws Validation_Exception If the value does not match the schema. */ - public function validate( Schema $schema, $value, string $paramName = 'value' ) { - return $this->validateSchemaArray( $schema->toArray(), $value, $paramName ); - } - - /** - * Strict validation & sanitization implementation. - * It only coerces int to float (e.g. 5 to 5.0). - * - * @param array $schema. The array must follow the format, which is returned from Schema::toArray(). - * @param mixed $value - * @return mixed - */ - public function validateSchemaArray( array $schema, $value, string $paramName = 'value' ) { - $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + public function validate_schema_array( array $schema, $value, string $param_name = 'value' ) { + $result = $this->validate_and_sanitize_value_from_schema( $value, $schema, $param_name ); if ( $result instanceof WP_Error ) { - throw Validation_Exception::createFromWpError( $result ); + throw Validation_Exception::create_from_wp_error( $result ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } return $result; } @@ -47,51 +51,51 @@ class Validator { /** * Mirrors rest_validate_value_from_schema() and rest_sanitize_value_from_schema(). * - * @param mixed $value - * @param array $schema - * @param string $paramName + * @param mixed $value The value to validate. + * @param array $schema The schema to validate against. + * @param string $param_name The parameter name. * @return mixed|WP_Error */ - private function validateAndSanitizeValueFromSchema( $value, array $schema, string $paramName ) { - // nullable - $fullType = $schema['type'] ?? null; - if ( is_array( $fullType ) && in_array( 'null', $fullType, true ) && $value === null ) { + private function validate_and_sanitize_value_from_schema( $value, array $schema, string $param_name ) { + // nullable. + $full_type = $schema['type'] ?? null; + if ( is_array( $full_type ) && in_array( 'null', $full_type, true ) && null === $value ) { return null; } - // anyOf, oneOf + // anyOf, oneOf. if ( isset( $schema['anyOf'] ) ) { - return $this->validateAndSanitizeAnyOf( $value, $schema, $paramName ); + return $this->validate_and_sanitize_any_of( $value, $schema, $param_name ); } elseif ( isset( $schema['oneOf'] ) ) { - return $this->validateAndSanitizeOneOf( $value, $schema, $paramName ); + return $this->validate_and_sanitize_one_of( $value, $schema, $param_name ); } - // make types strict - $type = is_array( $fullType ) ? $fullType[0] : $fullType; + // make types strict. + $type = is_array( $full_type ) ? $full_type[0] : $full_type; switch ( $type ) { case 'number': if ( ! is_float( $value ) && ! is_int( $value ) ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } break; case 'integer': if ( ! is_int( $value ) ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } break; case 'boolean': if ( ! is_bool( $value ) ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } break; case 'array': if ( ! is_array( $value ) ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } if ( isset( $schema['items'] ) ) { foreach ( $value as $i => $v ) { - $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['items'], $paramName . '[' . $i . ']' ); + $result = $this->validate_and_sanitize_value_from_schema( $v, $schema['items'], $param_name . '[' . $i . ']' ); if ( $this->wp->isWpError( $result ) ) { return $result; } @@ -100,28 +104,28 @@ class Validator { break; case 'object': if ( ! is_array( $value ) && ! $value instanceof stdClass && ! $value instanceof JsonSerializable ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } - // ensure string keys + // ensure string keys. $value = (array) ( $value instanceof JsonSerializable ? $value->jsonSerialize() : $value ); if ( count( array_filter( array_keys( $value ), 'is_string' ) ) !== count( $value ) ) { - return $this->getTypeError( $paramName, $fullType ); + return $this->get_type_error( $param_name, $full_type ); } - // validate object properties + // validate object properties. foreach ( $value as $k => $v ) { if ( isset( $schema['properties'][ $k ] ) ) { - $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['properties'][ $k ], $paramName . '[' . $k . ']' ); + $result = $this->validate_and_sanitize_value_from_schema( $v, $schema['properties'][ $k ], $param_name . '[' . $k . ']' ); if ( $this->wp->isWpError( $result ) ) { return $result; } continue; } - $patternPropertySchema = $this->wp->restFindMatchingPatternPropertySchema( $k, $schema ); - if ( $patternPropertySchema ) { - $result = $this->validateAndSanitizeValueFromSchema( $v, $patternPropertySchema, $paramName . '[' . $k . ']' ); + $pattern_property_schema = rest_find_matching_pattern_property_schema( $k, $schema ); + if ( $pattern_property_schema ) { + $result = $this->validate_and_sanitize_value_from_schema( $v, $pattern_property_schema, $param_name . '[' . $k . ']' ); if ( $this->wp->isWpError( $result ) ) { return $result; } @@ -129,7 +133,7 @@ class Validator { } if ( isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ) ) { - $result = $this->validateAndSanitizeValueFromSchema( $v, $schema['additionalProperties'], $paramName . '[' . $k . ']' ); + $result = $this->validate_and_sanitize_value_from_schema( $v, $schema['additionalProperties'], $param_name . '[' . $k . ']' ); if ( $this->wp->isWpError( $result ) ) { return $result; } @@ -138,23 +142,25 @@ class Validator { break; } - $result = $this->wp->restValidateValueFromSchema( $value, $schema, $paramName ); - if ( $this->wp->isWpError( $result ) ) { + $result = rest_validate_value_from_schema( $value, $schema, $param_name ); + if ( is_wp_error( $result ) ) { return $result; } - return $this->wp->restSanitizeValueFromSchema( $value, $schema, $paramName ); + return rest_sanitize_value_from_schema( $value, $schema, $param_name ); } /** * Mirrors rest_find_any_matching_schema(). * - * @param mixed $value + * @param mixed $value The value to validate. + * @param array $any_of_schema The schema to validate against. + * @param string $param_name The parameter name. * @return mixed|WP_Error */ - private function validateAndSanitizeAnyOf( $value, array $anyOfSchema, string $paramName ) { + private function validate_and_sanitize_any_of( $value, array $any_of_schema, string $param_name ) { $errors = array(); - foreach ( $anyOfSchema['anyOf'] as $index => $schema ) { - $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + foreach ( $any_of_schema['anyOf'] as $index => $schema ) { + $result = $this->validate_and_sanitize_value_from_schema( $value, $schema, $param_name ); if ( ! $this->wp->isWpError( $result ) ) { return $result; } @@ -164,21 +170,23 @@ class Validator { 'index' => $index, ); } - return $this->wp->restGetCombiningOperationError( $value, $paramName, $errors ); + return rest_get_combining_operation_error( $value, $param_name, $errors ); } /** * Mirrors rest_find_one_matching_schema(). * - * @param mixed $value + * @param mixed $value The value to validate. + * @param array $one_of_schema The schema to validate against. + * @param string $param_name The parameter name. * @return mixed|WP_Error */ - private function validateAndSanitizeOneOf( $value, array $oneOfSchema, string $paramName ) { - $matchingSchemas = array(); - $errors = array(); - $data = null; - foreach ( $oneOfSchema['oneOf'] as $index => $schema ) { - $result = $this->validateAndSanitizeValueFromSchema( $value, $schema, $paramName ); + private function validate_and_sanitize_one_of( $value, array $one_of_schema, string $param_name ) { + $matching_schemas = array(); + $errors = array(); + $data = null; + foreach ( $one_of_schema['oneOf'] as $index => $schema ) { + $result = $this->validate_and_sanitize_value_from_schema( $value, $schema, $param_name ); if ( $this->wp->isWpError( $result ) ) { $errors[] = array( 'error_object' => $result, @@ -186,26 +194,31 @@ class Validator { 'index' => $index, ); } else { - $data = $result; - $matchingSchemas[ $index ] = $schema; + $data = $result; + $matching_schemas[ $index ] = $schema; } } - if ( ! $matchingSchemas ) { - return $this->wp->restGetCombiningOperationError( $value, $paramName, $errors ); + if ( ! $matching_schemas ) { + return $this->wp->restGetCombiningOperationError( $value, $param_name, $errors ); } - if ( count( $matchingSchemas ) > 1 ) { - // reuse WP method to generate detailed error - $invalidSchema = array( 'type' => array() ); - $oneOf = array_replace( array_fill( 0, count( $oneOfSchema['oneOf'] ), $invalidSchema ), $matchingSchemas ); - return $this->wp->restFindOneMatchingSchema( $value, array( 'oneOf' => $oneOf ), $paramName ); + if ( count( $matching_schemas ) > 1 ) { + // reuse WP method to generate detailed error. + $invalid_schema = array( 'type' => array() ); + $one_of = array_replace( array_fill( 0, count( $one_of_schema['oneOf'] ), $invalid_schema ), $matching_schemas ); + return rest_find_one_matching_schema( $value, array( 'oneOf' => $one_of ), $param_name ); } return $data; } - /** @param string|string[] $type */ - private function getTypeError( string $param, $type ): WP_Error { + /** + * Returns a WP_Error for a type mismatch. + * + * @param string $param The parameter name. + * @param string|string[] $type The expected type. + */ + private function get_type_error( string $param, $type ): WP_Error { $type = is_array( $type ) ? $type : array( $type ); return new WP_Error( 'rest_invalid_type',