传递引用不使用array_walk_recursive的其他参数,除非它是不推荐的调用时间传递引用

时间:2011-12-21 09:28:10

标签: php pass-by-reference

有一段时间了,我一直在使用“传统”递归函数来展平多维数组,例如

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

到一个简单的一维数组。

昨晚,我想我会看一下使用array_walk_recursive()来看看我是否能让它更高效,更清洁。

我的第一次尝试不太成功:

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


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

我认为它应该有用,但我得到的只是一系列错误:

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

和结果:

array(0) { }

我的flattenArray()函数中的类型提示给了我

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

使用闭包产生相同的结果

我可以让它工作的唯一方法(不使用全局或静态的flattenedArray)使用调用时传递引用:

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


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

产生正确的结果

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" }

但是给了我一个意想不到的警告

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

我知道PHP是一种古怪的语言,但这看起来有点极端。文档清楚地显示了array_walk_recursive的第一个参数是pass-by-reference,但似乎附加参数只能在调用时通过引用传递。怪异!

PHP版本是5.3.8

有关如何使用array_walk_recursive正确展平我的数组的任何建议,而不会被弃用的错误(除了提交错误报告)。

修改

P.S。

我知道我可以使用闭包来绕过这个问题:

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

但是因为这是当前允许与PHP 5.2.0一起使用的库所必需的,所以使用需要更高版本的PHP的功能不是一个实用的选择

4 个答案:

答案 0 :(得分:6)

以这种方式思考:您将$flatArray传递给array_walk_recursive 按值。然后,array_walk_recursive会将参数通过参考进一步传递给您的函数。但是,当它按值传递给array_walk_recursive时,引用已经指向不同的值。

我知道,这似乎是一个奇怪的限制,但是当你想到它时,它也是合乎逻辑的。

顺便说一句,我认为你不小心也发现了另一个问题,它实际上看起来像是严重的内存损坏(看看@ http://codepad.viper-7.com/ZYNrNd打印的数组的第三个元素)。我会调查一下。

在旁注中,一种简单的展平方法是使用RecursiveArrayIterator

$flattenedArray = array();
foreach (new RecursiveIteratorIterator(
             new RecursiveArrayIterator($baseArray),
             RecursiveIteratorIterator::LEAVES_ONLY
         ) as $value) {
    $flattenedArray[] = $value;
}

答案 1 :(得分:1)

在这个阶段没有特别的帮助。

通过PHP文档阅读,我发现call_user_func()有一条说明参数参数的说明:

  

请注意,call_user_func()的参数不会传递   参考

但是array_walk_recursive()没有这样的通知。

这可能纯粹是一个文档问题...如果我在array_walk_recursive()文档中看到类似的通知,我可能不会尝试它(虽然好奇可能已经尝试过,只是为了看看发生了什么)。

但是,我不明白为什么这两种情况都不应该接受回调函数定义中的pass-by-reference参数。这确实感觉有点像向后退一步......一个语言功能确实有用(尽管使用调用时传递引用)不再这样做,而不依赖于已弃用的功能。

答案 2 :(得分:1)

由于您的解决方案是特定于版本的(根据文档,PHP 5.2不应该抱怨调用时间pass-by-reference),一个选项是创建包含每个实现的不同脚本,然后有条件地包含另一个脚本定义扁平化函数(以及任何其他特定于版本的代码)的脚本,具体取决于PHP版本。

if (! defined('PHP_VERSION_ID')) {
    $version = explode('.', PHP_VERSION);
    define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
    if (PHP_VERSION_ID < 50207) {
        define('PHP_MAJOR_VERSION',   $version[0]);
        define('PHP_MINOR_VERSION',   $version[1]);
        define('PHP_RELEASE_VERSION', $version[2]);
    }
}
if (PHP_VERSION_ID < 50300) {
    include_once('php-5.2-.php');
} else {
    include_once('php-5.3+.php');
}

答案 3 :(得分:0)

不推荐在通话时通过引用传递:

http://uk3.php.net/manual/en/ini.core.php#ini.allow-call-time-pass-reference

这在array_walk_recursive文档中没有记录,因为它不是特定于该函数。

您可以做的一件事是将对象和方法名称而不是函数名称作为回调传递,并将展平数组的状态保持为该对象的成员。

e.g。

class ArrayFlattener
{
    //implementation and array state
}

$flattener = new ArrayFlattener();
array_walk_recursive($baseArray, array($flattener, 'flatten'));

print_r($flattener->getFlattenedArray());