Ale*_*lex 10 php arrays loops reference
所以我正在使用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);
Run Code Online (Sandbox Code Playgroud)
我明白了 1 2 3 1 2 3 rec
但我期待 1 2 3 rec
如果我这样做,test($arr)那么它的工作原理,但问题是我需要将测试函数包装在另一个接受值而不是引用的函数中:(
有没有什么方法可以用我的包装函数在适当的时刻检测到引用?
Bab*_*aba 12
介绍
我认为更好的方法是创建数组的副本并比较修改而不是使用全局引脚,它仍然可以是一个 100% Recursive
例1
这来自上面的示例:
$array = array(1,2,3);
$array[4] = &$array;
wrap($array);
Run Code Online (Sandbox Code Playgroud)
产量
Array
(
[0] => 1
[1] => 2
[2] => 3
[4] => ref
)
Run Code Online (Sandbox Code Playgroud)
例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);
Run Code Online (Sandbox Code Playgroud)
产量
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
)
)
Run Code Online (Sandbox Code Playgroud)
例3
这真的是递归的吗?
$array = array(1, 2, 3, array(1, 2, 3));
$array[4][4][2][6][1] = array(1,2,3=>&$array);
wrap( $array);
Run Code Online (Sandbox Code Playgroud)
产量
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
)
)
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
你修改过的功能
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
我觉得你过于复杂了.我通过循环遍历数组并检查数组中的当前值是否与数组等效(===)来解决这个问题.
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 . ', ';
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我使用了以下测试用例:
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);
Run Code Online (Sandbox Code Playgroud)
哪个产生了这个输出:
Array 1:
1, 2, 3, ref
Array 2:
1, 2, ref, 1, 2, 3,
Run Code Online (Sandbox Code Playgroud)
但是,对于嵌套引用,上述方法将失败.如果可以嵌套引用,如下面的测试用例:
echo "\nArray 3:\n";
$array3 = array(1, 2, 3, array(1, 2, 3));
$array3[3][2] = &$array3;
wrap( $array3);
Run Code Online (Sandbox Code Playgroud)
然后我们需要跟踪我们看到的所有数组引用,如下所示:
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);
}
Run Code Online (Sandbox Code Playgroud)
使用上面的测试用例,输出:
Array 3:
1, 2, 3, 1, ref, 3,
Run Code Online (Sandbox Code Playgroud)
编辑:我已经更新了跟踪所有引用以消除全局依赖关系的最终函数.
function wrap($arr){ test($arr); }
/// ...
wrap($array);
Run Code Online (Sandbox Code Playgroud)
您的wrap()函数为其分配新的内存块$arr.当你test()在wrap()s体内调用函数时,它会引用$arr内存块,但不是$arrays内存块,因为它$arr 是$array PHP内存管理系统的副本,它们是分开存储的.
有一个通用的参考识别功能:
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
}
Run Code Online (Sandbox Code Playgroud)
所以,让我们做一些测试:
<?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);
?>
Run Code Online (Sandbox Code Playgroud)
显示:
1 // < $arr
2
3
1 // < $array
2
3
ref // < $array doubled -> reference found
Run Code Online (Sandbox Code Playgroud)
这种行为的原因是$arr[3]包含对$arrays存储块的引用,但不包含对其自身存储块的引用.
让我们删除$array[] = &$array;一行,并修改wrap()函数来检查:
function wrap($arr){
$arr[] = &$arr;
test($arr);
}
Run Code Online (Sandbox Code Playgroud)
结果将是:
1 // < $arr
2
3
ref // < $arr doubled -> reference found
Run Code Online (Sandbox Code Playgroud)
因为$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);
?>
Run Code Online (Sandbox Code Playgroud)
这表现了:
1
2
3
ref
Run Code Online (Sandbox Code Playgroud)
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
?>
Run Code Online (Sandbox Code Playgroud)
显示:
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
Run Code Online (Sandbox Code Playgroud)