Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在纯PHP中有没有检测循环数组的方法?_Php_Arrays_Recursion_Identity_Circular Reference - Fatal编程技术网

在纯PHP中有没有检测循环数组的方法?

在纯PHP中有没有检测循环数组的方法?,php,arrays,recursion,identity,circular-reference,Php,Arrays,Recursion,Identity,Circular Reference,我试图在PHP中实现我自己的序列化/var_转储样式函数。如果存在圆形阵列的可能性,这似乎是不可能的 在最近的PHP版本中,var_dump似乎可以检测循环数组: php > $a = array(); php > $a[] = &$a; php > var_dump($a); array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } 我将如何在PHP中实现我自己的序列化类型的

我试图在PHP中实现我自己的序列化/var_转储样式函数。如果存在圆形阵列的可能性,这似乎是不可能的

在最近的PHP版本中,var_dump似乎可以检测循环数组:

php > $a = array();
php > $a[] = &$a;
php > var_dump($a);
array(1) {
  [0]=>
  &array(1) {
    [0]=>
    *RECURSION*
  }
}
我将如何在PHP中实现我自己的序列化类型的方法,该方法可以进行类似的检测?我不能只跟踪我访问过的数组,因为在PHP中对数组进行严格比较会为包含相同元素的不同数组返回true,而比较循环数组会导致致命错误

php > $b = array(1,2);
php > $c = array(1,2);
php > var_dump($b === $c);
bool(true)
php > $a = array();
php > $a[] = &$a;
php > var_dump($a === $a);
PHP Fatal error:  Nesting level too deep - recursive dependency? in php shell code on line 1
我一直在寻找一种为数组查找唯一id(指针)的方法,但找不到。spl_object_hash仅对对象有效,而对数组无效。如果我将多个不同的数组强制转换为对象,它们都会得到相同的spl\U object\U哈希值(为什么?)

编辑:

在每个数组上调用print_r、var_dump或serialize,然后使用一些机制来检测这些方法检测到的递归的存在,这是一个算法复杂性的噩梦,基本上会导致在大型嵌套数组上的任何使用都太慢而不实用

接受的答复:


我接受了下面的答案,这是第一个建议临时修改数组以查看它是否确实与另一个数组相同的答案。这就回答了“如何比较两个数组的标识?”的问题,从中递归检测是微不足道的。

它并不优雅,但解决了您的问题(至少如果您没有人使用*递归*作为值)

有趣的方法(我知道它很愚蠢:),但您可以修改它并跟踪递归元素的“路径”。这只是一个想法:)基于序列化字符串的属性,在中开始递归时,将与原始数组的字符串相同。正如你所看到的——我在许多不同的变体上尝试了它,可能有什么东西能够“愚弄”它,但它“检测”所有列出的递归。我没有尝试使用对象递归数组

$a = array('b1'=>'a1','b2'=>'a2','b4'=>'a3','b5'=>'R:1;}}}');
$a['a1'] = &$a;
$a['b6'] = &$a;
$a['b6'][] = array(1,2,&$a);
$b = serialize($a); 
print_r($a);
function WalkArrayRecursive(&$array_name, &$temp){
    if (is_array($array_name)){
        foreach ($array_name as $k => &$v){
           if (is_array($v)){
                if (strpos($temp, preg_replace('#R:\d+;\}+$#', '', 
                               serialize($v)))===0) 
                { 
                  echo "\n Recursion detected at " . $k ."\n"; 
                  continue; 
                }
                WalkArrayRecursive($v, $temp);
            }
        }
    }
}
WalkArrayRecursive($a, $b);
regexp适用于具有递归的元素位于数组“末尾”的情况。是的,这个递归与整个数组有关。对子元素进行递归是可能的,但对我来说考虑它们已经太晚了。不知何故,数组的每个元素都应该检查其子元素中的递归。同样的方法,如上所述,通过print_r函数的输出,或者在序列化字符串中查找递归的特定记录(
r:4;}
类似的内容)。跟踪应该从这个元素开始,通过我的脚本比较下面的所有内容。只有当您想检测递归从何处开始,而不仅仅是您是否拥有它时,才需要这样做

ps:但是,正如我所想,最好的事情应该是从php本身创建的序列化字符串中编写自己的非序列化函数

下面的isRecursiveArray(数组)方法检测循环/递归数组。它通过将包含已知对象引用的元素临时添加到数组末尾来跟踪访问了哪些数组

如果您需要有关编写序列化方法的帮助,请更新主题问题,并在问题中提供序列化格式示例

function removeLastElementIfSame(array & $array, $reference) {
    if(end($array) === $reference) {
        unset($array[key($array)]);
    }
}

function isRecursiveArrayIteration(array & $array, $reference) {
    $last_element   = end($array);
    if($reference === $last_element) {
        return true;
    }
    $array[]    = $reference;

    foreach($array as &$element) {
        if(is_array($element)) {
            if(isRecursiveArrayIteration($element, $reference)) {
                removeLastElementIfSame($array, $reference);
                return true;
            }
        }
    }

    removeLastElementIfSame($array, $reference);

    return false;
}

function isRecursiveArray(array $array) {
    $some_reference = new stdclass();
    return isRecursiveArrayIteration($array, $some_reference);
}



$array      = array('a','b','c');
var_dump(isRecursiveArray($array));
print_r($array);



$array      = array('a','b','c');
$array[]    = $array;
var_dump(isRecursiveArray($array));
print_r($array);



$array      = array('a','b','c');
$array[]    = &$array;
var_dump(isRecursiveArray($array));
print_r($array);



$array      = array('a','b','c');
$array[]    = &$array;
$array      = array($array);
var_dump(isRecursiveArray($array));
print_r($array);

我的方法是使用一个临时数组,它保存已迭代的所有对象的副本。像这样:

// We use this to detect recursion.
global $recursion;
$recursion = [];

function dump( $data, $label, $level = 0 ) {
    global $recursion;

    // Some nice output for debugging/testing...
    echo "\n";
    echo str_repeat( "  ", $level );
    echo $label . " (" . gettype( $data ) . ") ";

    // -- start of our recursion detection logic
    if ( is_object( $data ) ) {
        foreach ( $recursion as $done ) {
            if ( $done === $data ) {
                echo "*RECURSION*";
                return;
            }
        }

        // This is the key-line: Remember that we processed this item!
        $recursion[] = $data;
    }
    // -- end of recursion check

    if ( is_array( $data ) || is_object( $data ) ) {
        foreach ( (array) $data as $key => $item ) {
            dump( $item, $key, $level + 1 );
        }
    } else {
        echo "= " . $data;
    }
}
下面是一些快速演示代码,以说明其工作原理:

$obj = new StdClass();
$obj->arr = [];
$obj->arr[] = 'Foo';
$obj->arr[] = $obj;
$obj->arr[] = 'Bar';
$obj->final = 12345;
$obj->a2 = $obj->arr;

dump( $obj, 'obj' );
此脚本将生成以下输出:

obj (object) 
  arr (array) 
    0 (string) = Foo
    1 (object) *RECURSION*
    2 (string) = Bar
  final (integer) = 12345
  a2 (array) 
    0 (string) = Foo
    1 (object) *RECURSION*
    2 (string) = Bar

答案是:你不能。看见不可能进行类似指针的引用比较,因此也不可能检测循环。在您的情况下,更好的解决方法可能是通过一个本机函数(
json\u decode(json\u encode())
)来消除引用,然后才应用您自己的序列化内容。现在甚至PHPUnit也在使用临时“标记”用于检测数组递归的方法。如果在数组中的任何位置碰巧有字符串
“*recursion*”
,则“相关”不起作用。。。无可否认,这很少见,但也有可能。是的,这就是我说的“递归”作为一个值的意思。不管怎样,你可以为这种情况添加一个检查,迭代数组并检查该值,然后根据print\r输出给你的行数得到迭代的限制。好吧,这检测到(以一种非常低效的方式)某个地方可能存在递归。。。这并不能真正解决我的用例。正如Mario之前所说的,没有办法检测引用,print\r使用的是您无法访问的内部结构,效率低下,但可以做到最好:(我认为临时更改数组可能是测试两个数组是否相同的唯一合理方法。这是建议该方法的第一个答案,因此我接受。如果引用它的变量丢失,是否也可以检测到循环引用?例如,这会导致无限循环(直到内存耗尽):
$array=[&$array]];$copy=$array;unset($array);isRecursiveArray($copy);
问题是关于循环数组,而不是对象。如果将这样的数组传递给
转储()
,它将无限循环。
obj (object) 
  arr (array) 
    0 (string) = Foo
    1 (object) *RECURSION*
    2 (string) = Bar
  final (integer) = 12345
  a2 (array) 
    0 (string) = Foo
    1 (object) *RECURSION*
    2 (string) = Bar