So I'm using the pin method, but the reference is detected one level too late:
$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
But I expect 1 2 3 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 :(
Is there any way I can detect the reference at the right moment with my wrapper function too?
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
Example 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
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
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With