【问题标题】:Validate PHP multidimensional array (nestable) to avoid infinite loop验证 PHP 多维数组(可嵌套)以避免无限循环
【发布时间】:2017-09-20 07:27:03
【问题描述】:

我正在使用https://github.com/dbushell/Nestable 库。

使用该库一切正常,但如果有人手动强制请求(假设),我想验证请求作为额外保护以避免无限循环。

我想知道是否有人知道一种优雅的方法来获得它,而无需执行另一个递归函数来检查父母是否在某个时候变成了孩子。

示例:

$data = [
    [
        "id" => 1,
    ],
    [
        *"id"       => 2,*
        "children" => [
            [
                "id" => 3,
            ],
            [
                "id" => 4,
            ],
            [
                "id"       => 5,
                "children" => [
                    [
                        "id" => 6,
                    ],
                    [
                        "id" => 7,
                    ],
                    [
                        "id" => 8,
                        "children" => [
                            [
                                *"id" => 2,*
                            ],
                            [
                                "id" => 10,
                            ],
                        ],
                    ],
                ],
            ],
            [
                "id" => 11,
            ],
            [
                "id" => 12,
            ],
        ],
    ],
    [
        "id" => 13,
    ],
    [
        "id" => 14,
    ],

];

nestableLinks($data);

/**
 * Nestable links.
 *
 * @param      $links
 * @param null $parent_id
 * @param int  $weight
 */
function nestableLinks($links, $parent_id = NULL, $weight = 0)
{
    foreach ($links as $link) {
        $weight++;

        var_dump(['id' => $link['id'], 'parent_id' => $parent_id, 'weight' => $weight]);

        if (array_key_exists('children', $link)) {
            nestableLinks($link['children'], $link['id']);
        }
    }
}

【问题讨论】:

    标签: php arrays validation recursion multidimensional-array


    【解决方案1】:

    您可以使用RecursiveArrayIterator。它必须扩展以满足我们的需求:

    class NestedRecursiveArrayIterator extends RecursiveArrayIterator
    {
        public function hasChildren()
        {
            return isset($this->current()['children']);
        }
    
        public function getChildren()
        {
            return new static($this->current()['children']);
        }
    }
    

    有了这个类,我们可以用 RecursiveIteratorIterator 对其进行迭代:

    $iterator = new RecursiveIteratorIterator(
        new NestedRecursiveArrayIterator($data),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    $ancestors = [];
    foreach ($iterator as $datum) {
        if ($iterator->getDepth() === 0) {
            $ancestors = [];
        }
    
        if (isset($ancestors[$datum['id']])) {
            // Invalid child that will cause loop.
            var_dump($datum);
        }
    
        $ancestors[$datum['id']] = true;
    }
    

    这里是working demo

    【讨论】:

      【解决方案2】:

      你的逻辑看起来不错。或者,您可以使用array_walk_recursive 而不是定义你自己的迭代逻辑,它会自动迭代变量中的每个数组。

      $data = [["id"=>1,],["id"=>2,"children"=>[["id"=>3,],["id"=>4,],["id"=>5,"children"=>[["id"=>2,],["id"=>7,],["id"=>8,],],],["id"=>9,],["id"=>10,],],],["id"=>11,],["id"=>12,],];
      
      function test_print($item, $key) {
          echo "key $key holds: $item\n";
      }
      array_walk_recursive($data, 'test_print');
      

      【讨论】:

        【解决方案3】:

        使用以 id 为索引的数组,并在遍历树时插入。如果您发现那里已经有一个节点,则您有一个循环并返回 false。

        当所有孩子都返回 true 时,您返回 true。一个错误的结果你这么早返回。

        这可以递归完成,也可以使用另一个数组作为节点堆栈。选择取决于您期望三者获得的深度。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-12-26
          • 1970-01-01
          • 1970-01-01
          • 2012-06-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-27
          相关资源
          最近更新 更多