No, but you can roll your own and get pretty close. For example, I've a class that acts as a settings storage and extends ArrayObject
for that, so it's pretty standard non-exotic stuff.
If you're fine with just checking the leaf attributes (e.g. "Is attribute X a boolean?" instead of "Is attribute X as a child of attribute Y a boolean"?), you could do a basic type checking like so (I've shortened the example code a bit, you'll find the full code here):
// Your data
$data = '{
"settings": {
"signup": {
"logging": true,
"forcePrompt": false,
"completedSteps": [
1,
2,
3
]
},
"trash": {
"retentionDays": 30,
"enabled": true
}
}
}
';
// We'll use this for type hinting
$defaults = '{
"settings": {
"signup": {
"logging": true,
"forcePrompt": false,
"completedSteps": []
},
"trash": {
"retentionDays": 1,
"enabled": true
}
}
}
';
/**
* Basic collection object.
*
* Usage:
* ------
* $collection->acme_social->twitter->client_id
*/
class Collection extends \ArrayObject {
/**
* Initialize and turn all levels of the given
* array into a collection object.
*
* @param Array $data
*/
public function __construct(array $data, array $defaults = []) {
parent::__construct($data, \ArrayObject::ARRAY_AS_PROPS);
// Turn all arrays into Collections
$iterator = new \RecursiveIteratorIterator(
new \RecursiveArrayIterator($this), \RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $key => $value) {
// Evaluate types
array_walk_recursive($defaults, function ($v, $k) use ($key, $value) {
if ($key == $k) {
$expected = gettype($v);
$given = gettype($value);
if ($expected != $given) {
throw new InvalidArgumentException("{$k} needs to be of type {$expected}, {$given} given.");
}
}
});
if (is_array($value)) {
$iterator->getInnerIterator()->offsetSet($key, new static($value));
}
}
}
}
try {
$collection = new Collection(json_decode($data, true), json_decode($defaults, true));
}
catch(Exception $e) {
var_dump($e->getMessage());
}