从另一个函数在数组中进行引用检测

时间:2020-12-05 15:58:06

So I'm using the pin method, but the reference is detected one level too late:

所以我使用的是pin方法,但是引用被检测到的时间太迟了:

$pin = time();

function wrap($arr){
  test($arr);
}

function test(&$arr){
    global $pin;

    if(in_array($pin, $arr))
        return print "ref";

    $arr[] = $pin;

    foreach($arr as &$v){

        if($v != $pin){

            if(is_array($v))
                return test($v);

            print $v . " ";

      }

    }

}

$array = array(1, 2, 3);
$array[4] = &$array;

wrap($array);

I get 1 2 3 1 2 3 rec

得到1 2 3 1 2 3

But I expect 1 2 3 rec

但我希望是1,23 rec

If I just do test($arr) then it works, but the problem is that I need to wrap the test function inside another one that accepts values not references :(

如果我只做测试($arr),那么它就可以工作,但是问题是我需要将测试函数包装在另一个接受值而不是引用的函数中:

Is there any way I can detect the reference at the right moment with my wrapper function too?

有什么方法可以在适当的时候用包装器函数检测引用吗?

3 个解决方案

#1


12  

Introduction

介绍

I think a better approach would be to create a copy of the array and compare modification rather than use global pin and it can still be a 100% Recursive

我认为更好的方法是创建数组的副本并比较修改,而不是使用全局pin,它仍然可以100%递归

Example 1

示例1

This is from your example above :

这是你上面的例子:

$array = array(1,2,3);
$array[4] = &$array;
wrap($array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [4] => ref
)

Example 2

示例2

Are we really sure its detecting reference or just a copy of the array

我们真的确定它的检测引用或只是数组的一个拷贝吗

//Case 1 : Expect no modification
$array = array(1, 2, 3, array(1, 2, 3));
wrap( $array);

//Case 2 : Expect Modification in Key 2
$array = array(1, 2, 3, array(1, 2, 3));
$array[2] = &$array;
wrap( $array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)
Array
(
    [0] => 1
    [1] => 2
    [2] => ref
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)

Example 3

示例3

Is this really recursive ?

这真的是递归的吗?

$array = array(1, 2, 3, array(1, 2, 3));
$array[4][4][2][6][1] = array(1,2,3=>&$array);
wrap( $array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

    [4] => Array
        (
            [4] => Array
                (
                    [2] => Array
                        (
                            [6] => Array
                                (
                                    [1] => Array
                                        (
                                            [0] => 1
                                            [1] => 2
                                            [3] => ref   <-- GOT YOU
                                        )

                                )

                        )

                )

        )

)

Your Modified Function

你的修改功能

/**
 * Added printf since test now returns array
 * @param array $arr
 */
function wrap(array $arr) {
    printf("<pre>%s<pre>", print_r(test($arr), true));
}


/**
 * - Removed Top Refrence
 * - Removed Global
 * - Add Recursion
 * - Returns array
 * @param array $arr
 * @return array
 */
function test(array $arr) {
    $temp = $arr;
    foreach ( $arr as $key => &$v ) {
        if (is_array($v)) {
            $temp[$key]['_PIN_'] = true;
            $v = isset($arr[$key]['_PIN_']) ? "ref" : test($v);
        }
    }
    unset($temp); // cleanup
    return $arr;
}

#2


5  

I think you are over-complicating things. I solved this by looping over the array and checking if the current value in the array is equivalent (===) with the array.

我认为你把事情弄得太复杂了。我通过在数组上循环并检查数组中的当前值是否与数组相等(=== =)来解决这个问题。

function wrap( $arr){
    test($arr);
}

function test( $arr){
    foreach( $arr as $v) {
        if( $v === $arr) { 
            print 'ref, ';
        } else {
            if( is_array( $v)) { 
                test( $v); 
            } else {
                print $v . ', ';
            }
        }
    }
}

I used the following test cases:

我使用了以下测试案例:

echo "Array 1:\n";
$array1 = array(1, 2, 3);
$array1[4] = &$array1;
wrap( $array1);

echo "\nArray 2:\n";
$array2 = array(1, 2, 3, array(1, 2, 3));
$array2[2] = &$array2;
wrap( $array2);

Which produced this output:

产生该输出:

Array 1: 
1, 2, 3, ref 
Array 2: 
1, 2, ref, 1, 2, 3, 

However, the above method will fail for nested references. If nested references are possible, as in the following test case:

但是,上述方法对于嵌套引用将失败。如果可以嵌套引用,如下面的测试用例所示:

echo "\nArray 3:\n";
$array3 = array(1, 2, 3, array(1, 2, 3));
$array3[3][2] = &$array3;
wrap( $array3);

Then we need to keep track of all the array references we've seen, like this:

然后我们需要跟踪我们看到的所有数组引用,比如:

function wrap( $arr){
    test( $arr);
}

function test( $arr){
    $refs = array(); // Array of references that we've seen

    $f = function( $arr) use( &$refs, &$f) {
        $refs[] = $arr;
        foreach( $arr as $v) {
            if( in_array( $v, $refs)) { 
                print 'ref, ';
            } else {
                if( is_array( $v)) {
                    $f( $v); 
                } else {
                    print $v . ', ';
                }
            }
        }
    };
    $f( $arr);
}

Using the above test case, this outputs:

使用上面的测试用例,输出如下:

Array 3: 
1, 2, 3, 1, ref, 3,

Edit: I've updated the final function that keeps track of all references to eliminate the global dependencies.

编辑:我已经更新了最终函数,该函数跟踪所有引用,以消除全局依赖关系。

#3


5  

function wrap($arr){ test($arr); }
/// ...
wrap($array);

Your wrap() function allocates new memory block for $arr. When you calling test() function within wrap()s body, it takes reference of $arr memory block, but not an $arrays memory block, because $arr is a copy of $array and PHP memory management system stores them separately.

您的wrap()函数为$arr分配新的内存块。当您在wrap()的主体中调用test()函数时,它将引用$arr内存块,而不是$array内存块,因为$arr是单独存储它们的$array和PHP内存管理系统的副本。


There is a universal reference spotting function:

有一个通用的参考点定位功能:

function is_equal_refs(&$a, &$b){
    $buffer = $a;          // saving current value in temporary variable

    $a = md5(time());      // assigning new value to memory block, pointed by reference

    $result = ($a === $b); // if they're still equal, then they're point to the same place.

    $a = $buffer;          // restoring value

    return $result;        // returning result
}

So, lets do some testing:

那么,让我们做一些测试:

<?php
header('Content-Type: text/plain');

function is_equal_refs(&$a, &$b){
    $buffer = $a;

    $a = md5(time());

    $result = ($a === $b);

    $a = $buffer;

    return $result;
}

function wrap($arr){ test($arr); }

function test(&$arr){
    foreach($arr as &$v){

        if(is_equal_refs($arr, $v)){
            print_r('ref');
            echo PHP_EOL;
            break;
        }

        if(is_array($v))return test($v);

        print_r($v);
        echo PHP_EOL;
    }
}

$array   = array(1, 2, 3);
$array[] = &$array;

wrap($array);
?>

Shows:

显示:

1   // < $arr
2
3
1   // < $array
2
3
ref // < $array doubled -> reference found

The reason of such behavior is $arr[3] contains reference for $arrays memory block, but not reference of itself's memory block.

这种行为的原因是$arr[3]包含对$array内存块的引用,但不包含对自身内存块的引用。

Lets remove a $array[] = &$array; row, and modify wrap() function to check:

让我们删除一个$array[] = &$array;行,并修改wrap()函数来检查:

function wrap($arr){
    $arr[] = &$arr;

    test($arr);
}

And result would be:

结果将是:

1   // < $arr
2
3
ref // < $arr doubled -> reference found

Because $arr not points to $array, but to itself in $arr[3]. So, in your code there are different references you want to spot.

因为$arr不是指向$array,而是指向$arr[3]。所以,在你的代码中有不同的引用。


CONCLUSION: What you want to achieve is breaking out PHP memory management rules.

结论:您需要实现的是打破PHP内存管理规则。


UPDv1:

UPDv1:

Need to seek a workaround, to restore $array reference in a wrap() function scope.

需要寻找一种变通方法,以便在wrap()函数范围内恢复$array引用。

1) A "bad" / "globals" practice:

1)“坏”/“全球化”实践:

<?php
header('Content-Type: text/plain');

function is_equal_refs(&$a, &$b){
    $buffer = $a;

    $a = md5(time());

    $result = ($a === $b);

    $a = $buffer;

    return $result;
}

function wrap($array){
    global $check; // <- THIS

    test(empty($check) ? $array : $check); // <- THIS
}

function test(&$arr){
    foreach($arr as &$v){

        if(is_equal_refs($v, $arr)){
            print_r('ref');
            echo PHP_EOL;
            break;
        }

        if(is_array($v)){
            test($v);
        } else {
            print $v . ' ';
            echo PHP_EOL;
        }
    }
}

$array   = array(1, 2, 3);
$array[] = &$array;
$check   = &$array; // <- and THIS

wrap($array);
?>

Which shows:

显示:

1
2
3
ref

2) A "wrap everything in array or object" practice: (prefered and reliable)

2)“在数组或对象中包装所有内容”实践:(首选和可靠)

<?php
header('Content-Type: text/plain');

define('REF_MARKER', 'x-my-tr!cky-ref'); // trick key definition

function is_equal_refs(&$a, &$b){
        $buffer = $a;

        $a = md5(time());

        $result = ($a === $b);

        $a = $buffer;

        return $result;
    }

function wrap(array $arr){
        // restore reference, if trick.
        // it might be moved to the top part of test() function (might affect performance).
        if(isset($arr[REF_MARKER]))$arr = &$arr[REF_MARKER];

        test($arr);
    }

// $array - subject to test;
// $refs  - internal ref list of all `subjects`;
function test(&$array, $refs = array()){
        $refs[] = &$array;

        foreach($array as &$value){

            foreach($refs as &$ref){
                if(is_equal_refs($ref, $value))return print 'ref ';
            }

            if(is_array($value)){
                $refs[] = &$value;

                test($value, $refs);
            } else {
                print $value . ' ';
            }
        }
    }

$array   = array(1, 2, 3);
$array[] = &$array;

wrap(array(REF_MARKER => &$array)); // trick

print PHP_EOL;

$ring      = array(1, 2, 3, array(4, 5, 6));
$ring[3][] = &$ring;

wrap(array(REF_MARKER => &$ring)); // trick

print PHP_EOL;

$test = array('a', 'b', 'c');
$ring = array(1, 2, 3);

$ring[] = &$test;
$test[] = &$ring;

wrap(array(REF_MARKER => &$ring)); // trick

print PHP_EOL;

wrap(range(1, 5)); // normal

print PHP_EOL;

$test = array(1, 2, 3, array(1, 2, 3), 4, array(5, 2, 3), array(6, array(1, 2, 3), 7), array(1, 2, 3));

wrap($test); // normal

print PHP_EOL;

$test[]    = &$test;
$test[3][] = &$test;
$test[5][] = &$test[3];

wrap(array(REF_MARKER => &$test)); // trick
?>

Shows:

显示:

1 2 3 ref
1 2 3 4 5 6 ref
1 2 3 a b c ref
1 2 3 4 5
1 2 3 1 2 3 4 5 2 3 6 1 2 3 7 1 2 3
1 2 3 1 2 3 ref 4 5 2 3 ref 6 1 2 3 7 1 2 3 ref

#1


12  

Introduction

介绍

I think a better approach would be to create a copy of the array and compare modification rather than use global pin and it can still be a 100% Recursive

我认为更好的方法是创建数组的副本并比较修改,而不是使用全局pin,它仍然可以100%递归

Example 1

示例1

This is from your example above :

这是你上面的例子:

$array = array(1,2,3);
$array[4] = &$array;
wrap($array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [4] => ref
)

Example 2

示例2

Are we really sure its detecting reference or just a copy of the array

我们真的确定它的检测引用或只是数组的一个拷贝吗

//Case 1 : Expect no modification
$array = array(1, 2, 3, array(1, 2, 3));
wrap( $array);

//Case 2 : Expect Modification in Key 2
$array = array(1, 2, 3, array(1, 2, 3));
$array[2] = &$array;
wrap( $array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)
Array
(
    [0] => 1
    [1] => 2
    [2] => ref
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)

Example 3

示例3

Is this really recursive ?

这真的是递归的吗?

$array = array(1, 2, 3, array(1, 2, 3));
$array[4][4][2][6][1] = array(1,2,3=>&$array);
wrap( $array);

Output

输出

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

    [4] => Array
        (
            [4] => Array
                (
                    [2] => Array
                        (
                            [6] => Array
                                (
                                    [1] => Array
                                        (
                                            [0] => 1
                                            [1] => 2
                                            [3] => ref   <-- GOT YOU
                                        )

                                )

                        )

                )

        )

)

Your Modified Function

你的修改功能

/**
 * Added printf since test now returns array
 * @param array $arr
 */
function wrap(array $arr) {
    printf("<pre>%s<pre>", print_r(test($arr), true));
}


/**
 * - Removed Top Refrence
 * - Removed Global
 * - Add Recursion
 * - Returns array
 * @param array $arr
 * @return array
 */
function test(array $arr) {
    $temp = $arr;
    foreach ( $arr as $key => &$v ) {
        if (is_array($v)) {
            $temp[$key]['_PIN_'] = true;
            $v = isset($arr[$key]['_PIN_']) ? "ref" : test($v);
        }
    }
    unset($temp); // cleanup
    return $arr;
}

#2


5  

I think you are over-complicating things. I solved this by looping over the array and checking if the current value in the array is equivalent (===) with the array.

我认为你把事情弄得太复杂了。我通过在数组上循环并检查数组中的当前值是否与数组相等(=== =)来解决这个问题。

function wrap( $arr){
    test($arr);
}

function test( $arr){
    foreach( $arr as $v) {
        if( $v === $arr) { 
            print 'ref, ';
        } else {
            if( is_array( $v)) { 
                test( $v); 
            } else {
                print $v . ', ';
            }
        }
    }
}

I used the following test cases:

我使用了以下测试案例:

echo "Array 1:\n";
$array1 = array(1, 2, 3);
$array1[4] = &$array1;
wrap( $array1);

echo "\nArray 2:\n";
$array2 = array(1, 2, 3, array(1, 2, 3));
$array2[2] = &$array2;
wrap( $array2);

Which produced this output:

产生该输出:

Array 1: 
1, 2, 3, ref 
Array 2: 
1, 2, ref, 1, 2, 3, 

However, the above method will fail for nested references. If nested references are possible, as in the following test case:

但是,上述方法对于嵌套引用将失败。如果可以嵌套引用,如下面的测试用例所示:

echo "\nArray 3:\n";
$array3 = array(1, 2, 3, array(1, 2, 3));
$array3[3][2] = &$array3;
wrap( $array3);

Then we need to keep track of all the array references we've seen, like this:

然后我们需要跟踪我们看到的所有数组引用,比如:

function wrap( $arr){
    test( $arr);
}

function test( $arr){
    $refs = array(); // Array of references that we've seen

    $f = function( $arr) use( &$refs, &$f) {
        $refs[] = $arr;
        foreach( $arr as $v) {
            if( in_array( $v, $refs)) { 
                print 'ref, ';
            } else {
                if( is_array( $v)) {
                    $f( $v); 
                } else {
                    print $v . ', ';
                }
            }
        }
    };
    $f( $arr);
}

Using the above test case, this outputs:

使用上面的测试用例,输出如下:

Array 3: 
1, 2, 3, 1, ref, 3,

Edit: I've updated the final function that keeps track of all references to eliminate the global dependencies.

编辑:我已经更新了最终函数,该函数跟踪所有引用,以消除全局依赖关系。

#3


5  

function wrap($arr){ test($arr); }
/// ...
wrap($array);

Your wrap() function allocates new memory block for $arr. When you calling test() function within wrap()s body, it takes reference of $arr memory block, but not an $arrays memory block, because $arr is a copy of $array and PHP memory management system stores them separately.

您的wrap()函数为$arr分配新的内存块。当您在wrap()的主体中调用test()函数时,它将引用$arr内存块,而不是$array内存块,因为$arr是单独存储它们的$array和PHP内存管理系统的副本。


There is a universal reference spotting function:

有一个通用的参考点定位功能:

function is_equal_refs(&$a, &$b){
    $buffer = $a;          // saving current value in temporary variable

    $a = md5(time());      // assigning new value to memory block, pointed by reference

    $result = ($a === $b); // if they're still equal, then they're point to the same place.

    $a = $buffer;          // restoring value

    return $result;        // returning result
}

So, lets do some testing:

那么,让我们做一些测试:

<?php
header('Content-Type: text/plain');

function is_equal_refs(&$a, &$b){
    $buffer = $a;

    $a = md5(time());

    $result = ($a === $b);

    $a = $buffer;

    return $result;
}

function wrap($arr){ test($arr); }

function test(&$arr){
    foreach($arr as &$v){

        if(is_equal_refs($arr, $v)){
            print_r('ref');
            echo PHP_EOL;
            break;
        }

        if(is_array($v))return test($v);

        print_r($v);
        echo PHP_EOL;
    }
}

$array   = array(1, 2, 3);
$array[] = &$array;

wrap($array);
?>

Shows:

显示:

1   // < $arr
2
3
1   // < $array
2
3
ref // < $array doubled -> reference found

The reason of such behavior is $arr[3] contains reference for $arrays memory block, but not reference of itself's memory block.

这种行为的原因是$arr[3]包含对$array内存块的引用,但不包含对自身内存块的引用。

Lets remove a $array[] = &$array; row, and modify wrap() function to check:

让我们删除一个$array[] = &$array;行,并修改wrap()函数来检查:

function wrap($arr){
    $arr[] = &$arr;

    test($arr);
}

And result would be:

结果将是:

1   // < $arr
2
3
ref // < $arr doubled -> reference found

Because $arr not points to $array, but to itself in $arr[3]. So, in your code there are different references you want to spot.

因为$arr不是指向$array,而是指向$arr[3]。所以,在你的代码中有不同的引用。


CONCLUSION: What you want to achieve is breaking out PHP memory management rules.

结论:您需要实现的是打破PHP内存管理规则。


UPDv1:

UPDv1:

Need to seek a workaround, to restore $array reference in a wrap() function scope.

需要寻找一种变通方法,以便在wrap()函数范围内恢复$array引用。

1) A "bad" / "globals" practice:

1)“坏”/“全球化”实践:

<?php
header('Content-Type: text/plain');

function is_equal_refs(&$a, &$b){
    $buffer = $a;

    $a = md5(time());

    $result = ($a === $b);

    $a = $buffer;

    return $result;
}

function wrap($array){
    global $check; // <- THIS

    test(empty($check) ? $array : $check); // <- THIS
}

function test(&$arr){
    foreach($arr as &$v){

        if(is_equal_refs($v, $arr)){
            print_r('ref');
            echo PHP_EOL;
            break;
        }

        if(is_array($v)){
            test($v);
        } else {
            print $v . ' ';
            echo PHP_EOL;
        }
    }
}

$array   = array(1, 2, 3);
$array[] = &$array;
$check   = &$array; // <- and THIS

wrap($array);
?>

Which shows:

显示:

1
2
3
ref

2) A "wrap everything in array or object" practice: (prefered and reliable)

2)“在数组或对象中包装所有内容”实践:(首选和可靠)

<?php
header('Content-Type: text/plain');

define('REF_MARKER', 'x-my-tr!cky-ref'); // trick key definition

function is_equal_refs(&$a, &$b){
        $buffer = $a;

        $a = md5(time());

        $result = ($a === $b);

        $a = $buffer;

        return $result;
    }

function wrap(array $arr){
        // restore reference, if trick.
        // it might be moved to the top part of test() function (might affect performance).
        if(isset($arr[REF_MARKER]))$arr = &$arr[REF_MARKER];

        test($arr);
    }

// $array - subject to test;
// $refs  - internal ref list of all `subjects`;
function test(&$array, $refs = array()){
        $refs[] = &$array;

        foreach($array as &$value){

            foreach($refs as &$ref){
                if(is_equal_refs($ref, $value))return print 'ref ';
            }

            if(is_array($value)){
                $refs[] = &$value;

                test($value, $refs);
            } else {
                print $value . ' ';
            }
        }
    }

$array   = array(1, 2, 3);
$array[] = &$array;

wrap(array(REF_MARKER => &$array)); // trick

print PHP_EOL;

$ring      = array(1, 2, 3, array(4, 5, 6));
$ring[3][] = &$ring;

wrap(array(REF_MARKER => &$ring)); // trick

print PHP_EOL;

$test = array('a', 'b', 'c');
$ring = array(1, 2, 3);

$ring[] = &$test;
$test[] = &$ring;

wrap(array(REF_MARKER => &$ring)); // trick

print PHP_EOL;

wrap(range(1, 5)); // normal

print PHP_EOL;

$test = array(1, 2, 3, array(1, 2, 3), 4, array(5, 2, 3), array(6, array(1, 2, 3), 7), array(1, 2, 3));

wrap($test); // normal

print PHP_EOL;

$test[]    = &$test;
$test[3][] = &$test;
$test[5][] = &$test[3];

wrap(array(REF_MARKER => &$test)); // trick
?>

Shows:

显示:

1 2 3 ref
1 2 3 4 5 6 ref
1 2 3 a b c ref
1 2 3 4 5
1 2 3 1 2 3 4 5 2 3 6 1 2 3 7 1 2 3
1 2 3 1 2 3 ref 4 5 2 3 ref 6 1 2 3 7 1 2 3 ref