Reputation: 539
I have a JSON array consisting of something like the following:
$json =
Array (
[authmode] => ad
[usertype] => sam
[user] => BobJones
[server] => Array (
[0] => Array (
[ip] => 1.2.3.4
[title] => sony
[id] => Array (
[0] => 1.2.840.1.1.1
[1] => 1.2.840.1.1.2
)
)
[1] => Array (
[ip] => 10.20.30.40
[title] => panasonic
[id] => Array (
[0] => 1.2.840.1.1.10
[1] => 1.2.840.1.1.11
[2] => 1.2.840.1.1.12
[3] => 1.2.840.1.1.13
)
)
)
[recipient] => Array (
[0] => [email protected]
[1] => [email protected]
)
[date] => 2014-12-31
[options] => Array (
[0] => alpha
[1] => beta
[2] => gamma
)
)
Some elements of this array are required, others are optional. The required elements I hold in a separate array, containing their keys and their value types:
$req =
Array (
'authmode' => 'string',
'usertype' => 'string',
'user' => 'string'
'server' => 'array',
'ip' => 'string',
'title' => 'string'
'id' => 'array'
)
I have made a function that uses recursion to check for the required keys and ensures that their value types match those in the $req array
function bob($key, $val, $arr){
echo '<br>Checking ' . $key . ' with value of ' . $val . ' within ' . $arr;
// is key found in base array?
if(array_key_exists($key, $arr)){
if(gettype($arr[$key]) == $val){
echo '... gettype reports that ' . $arr . '[' . $key . '] is a ' . $val . ' and is correct';
return true;
}
}
echo '<br>' . $key . ' not in base array, sending back to itself...';
// check arrays within this array
foreach ($key as $ele){
if(is_array($ele)){
echo '<br>' . $ele . ' is an array ....';
if(bob($key, $val, $ele)){
return true;
}
}
}
return false;
}
Finally, the manner in which I call all of this is as follows:
foreach ($reqKeys as $key => $val){
if (!bob($key, $val, $json)){
echo '<p>Failed on ' . $key . ' being a ' . $val . '</p>';
}
}
The function works just fine on the base array, but as soon as I start inspecting any subarrays (the [server] array for example), the function returns an error on the ip not being a string, of which it clearly is.
The response I'm getting is:
Checking authmode with value of string within Array... gettype reports that Array[authmode] is a string and is correct
Checking usertype with value of string within Array... gettype reports that Array[usertype] is a string and is correct
Checking user with value of string within Array... gettype reports that Array[user] is a string and is correct
Checking server with value of array within Array... gettype reports that Array[server] is a array and is correct
Checking ip with value of string within Array
ip not in base array, sending back to itself...
Failed on ip being a string
Any thoughts or comments on a better way of doing things?
Upvotes: 0
Views: 57
Reputation: 3016
Since you want to do more complex comparisons than just type, you should use the strategy pattern and pass validation callbacks in $req
.
$req
will look like this:
$req = [
'authmode' => 'is_string',
'usertype' => 'is_string',
'user' => 'is_string',
'server' => function ($servers) {
// check is `server` is an array
if (!is_array($servers)) {
return false;
}
foreach ($servers as $server) {
// check conditions for each server.id, server.ip and server.title
if (
!is_array($server['id']) ||
!is_string($server['ip']) ||
!is_string($server['title'])
) {
return false;
}
// validate all server.id's
foreach ($server['id'] as $id) {
if (!is_string($id)) {
return false;
}
}
}
return true;
}
];
Because $arr['server']
is an array, you have to validate its contents using your own function, as you cannot easily refer to them.
This makes our validation function very simple:
function isPropertyValid($key, $isValid, $arr) {
if (!isset($arr[$key])) {
return false;
}
return isset($arr[$key]) && $isValid($arr[$key]);
}
You no longer need recursion there which is great, as it would introduce some complications.
You might just as well throw in a function that validates all requirements instead of doing it one by one:
function isArrayValid($requirements, $arr) {
foreach ($requirements as $key => $validator) {
if (!isPropertyValid($key, $validator, $arr)) {
return false;
}
}
return true;
}
I've put together a working example here.
Upvotes: 1