0

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?

3
  • Welcome to StackOverflow. Please read and follow the posting guidelines in the help documentation. Minimal, complete, verifiable example applies here. We cannot effectively help you until you post your code and accurately describe the problem. "something falls foul" is not a problem description. You already have some good tracing statements (echo) in your code; show us the output from that, along with the evidence of failure: stack trace, wrong output, etc. Commented May 2, 2016 at 17:32
  • @Prune you're right - I've updated the OP accordingly Commented May 2, 2016 at 18:43
  • Good work -- I retracted my "close" vote, and it looks like you have a good answer. :-) Commented May 2, 2016 at 20:20

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

6 Comments

many thanks, this certainly worked as I expected. As a potential follow up (unsure if I can ask this here, but...) how would I check the id array to ensure that all elements are strings? I'm not sure the notation to use : $req = array(..., 'id' => 'array', 'id[]' => 'string') ???
You can't do it like that, but I updated my answer to describe how it should be done.
Am struggling to get this working. I've tried to put everything together here to see where I'm going wrong... From what I can see, bob is going to not even process the first element in $json as it's not an array
I went through your code, you can find the changes here. Things to note: 1) you were trying to run the code against PHP 5.0 which doesn't support closures (anonymous functions). They are available from PHP 5.3, but honestly, you shouldn't be using anything lower than 5.6 nowadays. 2) you were supposed to replace $val argument withing the function with $isValid, otherwise the changed line wouldn't work.
This is so close @Kuba ... I never thought of using the callback within the function. Problem now is if one of the required elements (for example authmode) is missing from $json, bob() doesn't fail it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.