使用第二个数组递归多维数组以检查键和类型值

时间:2016-05-02 17:27:10

标签: php arrays recursion

我有一个JSON数组,包含如下内容:

$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] => john@smith.org 
    [1] => jsmith@smith.com 
  ) 
  [date] => 2014-12-31 
  [options] => Array ( 
    [0] => alpha
    [1] => beta
    [2] => gamma
  )
) 

此阵列的某些元素是必需的,其他元素是可选的。我在一个单独的数组中保存所需的元素,包含它们的键及其值类型:

$req = 
Array (
  'authmode' => 'string',
  'usertype' => 'string',
  'user' => 'string'
  'server' => 'array',
  'ip' => 'string',
  'title' => 'string'
  'id' => 'array'
)

我创建了一个使用递归检查所需键的函数,并确保它们的值类型与$ req数组中的值匹配

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;
}

最后,我调用所有这些的方式如下:

foreach ($reqKeys as $key => $val){
    if (!bob($key, $val, $json)){
        echo '<p>Failed on ' . $key . ' being a ' . $val . '</p>';
    }
}

该函数在基础数组上工作得很好,但是一旦我开始检查任何子数组(例如[server]数组),该函数就会在ip上返回错误而不是字符串,其中显然是

我得到的回应是:

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

对更好的做事方式有任何想法或意见吗?

1 个答案:

答案 0 :(得分:1)

由于您希望进行更复杂的比较,而不仅仅是键入,您应该使用策略模式并在$req中传递验证回调。

$req将如下所示:

$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;
    }
];

由于$arr['server']是一个数组,因此您必须使用自己的函数验证其内容,因为您无法轻松引用它们。

这使我们的验证功能非常简单:

function isPropertyValid($key, $isValid, $arr) {
    if (!isset($arr[$key])) {
        return false;
    }

    return isset($arr[$key]) && $isValid($arr[$key]);
}

你不再需要那里很好的递归,因为它会带来一些复杂性。

你也可以抛出一个验证所有需求的函数,而不是一个一个地执行:

function isArrayValid($requirements, $arr) {
    foreach ($requirements as $key => $validator) {
        if (!isPropertyValid($key, $validator, $arr)) {
            return false;
        }
    }

    return true;
}

我已经汇总了一个工作示例here