10

だから私は pin メソッドを使用していますが、参照が 1 レベル遅れて検出されます。

$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);

私は得る1 2 3 1 2 3 rec

しかし、私は期待しています1 2 3 rec

そうすればうまくいきますtest($arr)が、問題は、参照ではなく値を受け入れる別の関数内にテスト関数をラップする必要があることです:(

ラッパー関数でも適切なタイミングで参照を検出できる方法はありますか?

4

3 に答える 3

12

序章

グローバルピンを使用するよりも、配列のコピーを作成して変更を比較する方が良いと思いますが、それでも100% Recursive

例 1

これは上記の例からのものです:

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

出力

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

例 2

参照または配列の単なるコピーを検出していると本当に確信していますか

//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);

出力

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
        )

)

例 3

これは本当に再帰的ですか?

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

出力

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
                                        )

                                )

                        )

                )

        )

)

変更された関数

/**
 * 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;
}
于 2013-04-25T08:38:42.017 に答える
5
function wrap($arr){ test($arr); }
/// ...
wrap($array);

関数wrap()は に新しいメモリ ブロックを割り当てます$arr。s 本体test()内で関数を呼び出すと、メモリ ブロックは参照されますが、s メモリ ブロックは参照されません。これは、 PHP メモリ管理システムがそれらを別々に格納するためです。wrap()$arr$array$arr $array


ユニバーサル リファレンス スポッティング機能があります。

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
}

それで、いくつかのテストをしましょう:

<?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);
?>

ショー:

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

このような動作の理由は、s メモリ ブロックの$arr[3]参照が含まれて$arrayいますが、それ自体のメモリ ブロックの参照は含まれていません。

$array[] = &$array;行を削除し、wrap()関数を変更して確認します。

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

    test($arr);
}

結果は次のようになります。

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

$arrを指している$arrayのではなく、 内のそれ自体を指しているから$arr[3]です。したがって、コードには、見つけたいさまざまな参照があります。


結論:達成したいことは、PHP のメモリ管理ルールを打ち破ることです。


UPDv1:

関数スコープで参照を復元するには、回避策を探す必要があります。$arraywrap()

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);
?>

これは次を示しています。

1
2
3
ref

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
?>

ショー:

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
于 2013-04-25T08:43:36.020 に答える