bnoeafk
bnoeafk

Reputation: 539

Recursion of multi dimension array using 2nd array for checking keys and type values

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

Answers (1)

Kuba Birecki
Kuba Birecki

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

Related Questions