woocommerce/plugins/woocommerce-blocks/tests/php/Helpers/ValidateSchema.php

114 lines
3.4 KiB
PHP

<?php
/**
* Helper used to validate schema differences.
*/
namespace Automattic\WooCommerce\Blocks\Tests\Helpers;
/**
* Validate schema.
*/
class ValidateSchema {
/**
* The schema.
*
* @var array
*/
protected $schema = [];
/**
* Constructor passed schema object.
*
* @param array $schema API schema representation.
*/
public function __construct( $schema ) {
$this->schema = $schema;
}
/**
* Validate properties and return diff.
*
* @param array|object $object Object to compare.
* @param array $schema Schema to find nested properties under.
* @param string $prefix Prefix to append to diff property names.
* @return array
*/
public function get_diff_from_object( $object, $schema = null, $prefix = '' ) {
$missing = [];
$invalid_type = [];
$no_schema = [];
if ( is_null( $schema ) ) {
$schema = $this->schema['properties'];
}
if ( ! is_array( $object ) && ! is_object( $object ) ) {
return [];
}
$object = (array) $object;
$no_schema_diffs = array_diff( array_keys( (array) $object ), array_keys( $schema ) );
foreach ( $no_schema_diffs as $no_schema_diff ) {
$no_schema[] = $prefix . $no_schema_diff;
}
foreach ( $schema as $property_name => $property_schema ) {
// Validate property is set in object. Avoids isset in case value is NULL.
if ( ( is_array( $object ) && ! array_key_exists( $property_name, $object ) ) || ( is_object( $object ) && ! property_exists( $object, $property_name ) ) ) {
$missing[] = $prefix . $property_name;
continue;
}
// Validate type.
$type = gettype( $object[ $property_name ] );
if ( $type && ! $this->validate_type( $type, $property_schema['type'] ) ) {
$expected = is_array( $property_schema['type'] ) ? implode( ', ', $property_schema['type'] ) : $property_schema['type'];
$invalid_type[] = $prefix . $property_name . " ({$type}, expected {$expected})";
continue;
}
// Validate nested props.
if ( isset( $property_schema['items']['properties'] ) ) {
$nested_value = is_array( $object[ $property_name ] ) ? current( $object[ $property_name ] ) : $object[ $property_name ];
if ( $nested_value ) {
$diff = $this->get_diff_from_object(
$nested_value,
$property_schema['items']['properties'],
$prefix . $property_name . ':'
);
$missing = isset( $diff['missing'] ) ? array_merge( $missing, $diff['missing'] ) : $missing;
$invalid_type = isset( $diff['invalid_type'] ) ? array_merge( $invalid_type, $diff['invalid_type'] ) : $invalid_type;
$no_schema = isset( $diff['no_schema'] ) ? array_merge( $no_schema, $diff['no_schema'] ) : $no_schema;
}
}
}
return array_filter(
[
'missing' => array_values( array_filter( $missing ) ),
'invalid_type' => array_values( array_filter( $invalid_type ) ),
'no_schema' => array_values( array_filter( $no_schema ) ),
]
);
}
/**
* Validate type and return if it matches.
*
* @param string $type Type to check.
* @param string|array $expected Expected types.
* @return boolean
*/
protected function validate_type( $type, $expected ) {
$type = strtolower( $type );
$expected = is_array( $expected ) ? $expected : [ $expected ];
if ( in_array( 'number', $expected ) ) {
$expected = array_merge( $expected, [ 'double', 'float', 'integer' ] );
}
return in_array( $type, $expected, true );
}
}