Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass-by-reference not working with additional parameters for array_walk_recursive, unless it's the deprecated call-time pass-by-reference

For a while now, I've been using a "traditional" recursive function to flatten multi-dimensional arrays such as

$baseArray = array(array('alpha'),
                   array('beta','gamma'),
                   array(),
                   array(array('delta','epsilon'),
                         array('zeta',array('eta',
                                            'theta'
                                           ),
                              ),
                        ),
                   array('iota'),
                  );

to a simple 1-d array.

Last night, I thought I'd take a look at using array_walk_recursive() to see if I could make it more efficient and cleaner.

My first attempt wasn't very successful:

function flattenArray($arrayValue, $arrayKey, &$flatArray) {
    $flatArray[] = $arrayValue;
}


$flattenedArray = array();
array_walk_recursive($baseArray,'flattenArray',$flattenedArray);

I thought it should work, but all I got was a series of errors:

Warning: Cannot use a scalar value as an array in C:\xampp\htdocs\arrayTest.php on line 16

and a result of:

array(0) { }

Type hinting in my flattenArray() function gave me

Catchable fatal error: Argument 3 passed to flattenArray() must be an array, integer given in C:\xampp\htdocs\arrayTest.php on line 16

Using a closure gave identical results

The only way I could get it to work (without recourse to using a global or a static for my flattenedArray) was using call-time pass-by-reference:

function flattenArray($arrayValue, $arrayKey, $flatArray) {
    $flatArray[] = $arrayValue;
}


$flattenedArray = array();
array_walk_recursive($baseArray,'flattenArray',&$flattenedArray);

which produces the correct result

array(9) { [0]=> string(5) "alpha" [1]=> string(4) "beta" [2]=> string(5) "gamma" [3]=> string(5) "delta" [4]=> string(7) "epsilon" [5]=> string(4) "zeta" [6]=> string(3) "eta" [7]=> string(5) "theta" [8]=> string(4) "iota" }

but gives me a not-unexpected warning

Deprecated: Call-time pass-by-reference has been deprecated in C:\xampp\htdocs\arrayTest.php on line 22

I know PHP is a quirky language, but this seems a bit extreme. The documentation clearly shows that the first parameter to array_walk_recursive is pass-by-reference, but it seems that additional arguments can only be pass-by-reference at call-time. Weird!

PHP version is 5.3.8

Any suggestions as to how I can use array_walk_recursive to flatten my array correctly, without getting deprecated errors (besides filing a bug report).

EDIT

P.S.

I am aware that I can bypass this problem using a closure:

$flattenedArray = array();
array_walk_recursive($baseArray, function($arrayValue, $arrayKey) use(&$flattenedArray){ $flattenedArray[] = $arrayValue; } );
var_dump($flattenedArray);

but as this is required for a library which currently allows use with PHP 5.2.0, it's not a practical option to use a feature that requires a significantly later version of PHP

like image 834
Mark Baker Avatar asked Dec 21 '11 09:12

Mark Baker


1 Answers

Think about it this way: You pass $flatArray into array_walk_recursive by value. array_walk_recursive will then further pass the argument by reference to your function. But as it was passed to array_walk_recursive by value, the reference will already point to a different value.

I know, this might seem like a strange limitation, but when you think about it, it's quite logical too.

By the way, I think you accidentally also found another issue with this, it actually looks like serious memory corruption (look at the third elements of the array printed @ http://codepad.viper-7.com/ZYNrNd). I will look into this.

On a side note, an easy way to flatten is using a RecursiveArrayIterator:

$flattenedArray = array();
foreach (new RecursiveIteratorIterator(
             new RecursiveArrayIterator($baseArray),
             RecursiveIteratorIterator::LEAVES_ONLY
         ) as $value) {
    $flattenedArray[] = $value;
}
like image 122
NikiC Avatar answered Sep 19 '22 01:09

NikiC