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:
関数スコープで参照を復元するには、回避策を探す必要があります。$array
wrap()
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