vlio20
vlio20

Reputation: 9295

PHP object schema validation

is it possible to dynamically check if two objects schema is the same (in php)? for example:

{
   name: "Name1",
   age: 2,
   kids: [1,3,4]
}

{
   name: "Name2",
   age: 2,
   kids: [1,6,4,3]
}

For the above example I am expecting to return true. Here is another example:

{
   name: "Name1",
   age: 2,
   kids: [1,3,4]
}

{
   name: "Name1",
   kids: [1,3,4]
}

Here I am expecting to get false (schema not the same: age missing from the second object).

The function definition should look like: Boolean isSchemaEqual($obj1, $obj2) (I know there are no function definitions in php, just did it in order to make my question more clear).

Note: the schema could be nested, in that I mean some property can hold an object which also needs to be checked against the other object's (same) property.

Upvotes: 1

Views: 421

Answers (2)

Austin Brunkhorst
Austin Brunkhorst

Reputation: 21130

If you're looking to validate multiple levels of schema, you can use recursion in conjunction with an order-less array comparison idiom for the keys.

// determines if an array is associative
function isAssociative($array) {
    return (bool)count(array_filter(array_keys($array), 'is_string'));
}

// determines if the keys of two arrays are equal regardless of order
function keysEqual($a, $b) {
    if (!is_array($a) || !is_array($b))
        return false;

    $keysA = array_keys($a);
    $keysB = array_keys($b);

    return array_diff($keysA, $keysB) === array_diff($keysB, $keysA);
}

function isSchemaEqual($a, $b) {
    // validate keys - basic validation
    if (!keysEqual($a, $b))
        return false;

    // if we come across any key where the value is associative and 
    // the schema isn't the same for b's value at $key, then we know 
    // the schemas aren't equal 
    foreach ($a as $key => $value) {
        if (is_array($value) && isAssociative($value) && !isSchemaEqual($value, $b[$key]))
            return false;
    }

    // we couldn't find any differences, so they're equal
    return true;
}

You can find some tests, here.

Upvotes: 2

Maxim Krizhanovsky
Maxim Krizhanovsky

Reputation: 26729

Compare the keys:

if (array_keys($schema1) == array_keys($schema2)) {

}

For nested object, you will have to make your own function. One approach would be to make it one-level, with paths like {kids: {boys: [1,2,3]}} becoming kids/boys, then you can compare the arrays:

function flatten_keys($object, $prefix = '') {
   $result = array();
   foreach ($object as $key=>$value) {
     $result[] = $prefix . $key;
     if (is_object($value) || (is_array($value) && array_values($value) != $value) ) {
         $result = array_merge($result, flatten_keys($value, $key .'/'));
     }
   }
   return $result;
}

$a = flatten_keys($schema2);
$b = flatten_keys($schema2);
sort($a);
sort($b);
if ($a == $b){

}

Upvotes: 0

Related Questions