我在StackOverflow上找到了以下解决方案,以从对象数组中获取特定对象属性的数组:PHP - Extracting a property from an array of objects
建议的解决方案是使用array_map
并在create_function
内创建一个函数,如下所示:
$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
会发生什么?:array_map
遍历每个数组元素,在这种情况下是stdClass
个对象。首先,它创建一个这样的函数:
function($o) {
return $o->id;
}
其次,它为当前迭代中的对象调用此函数。它的工作原理与它类似的解决方案几乎相同:
$catIds = array_map(function($o) { return $o->id; }, $objects);
但是此解决方案仅在PHP版本> = 5.3中运行,因为它使用Closure
概念=> http://php.net/manual/de/class.closure.php
现在真正的问题是:
create_function
的第一个解决方案会增加内存,因为创建的函数将被写入内存而不会被重用或销毁。在Closure
的第二个解决方案中,它会。
因此,解决方案提供了相同的结果,但在内存方面有不同的行为。
以下示例:
// following array is given
$objects = array (
[0] => stdClass (
[id] => 1
),
[1] => stdClass (
[id] => 2
),
[2] => stdClass (
[id] => 3
)
)
BAD
while (true)
{
$objects = array_map(create_function('$o', 'return $o->id;'), $objects);
// result: array(1, 2, 3);
echo memory_get_usage() ."\n";
sleep(1);
}
4235616
4236600
4237560
4238520
...
不可
while (true)
{
$objects = array_map(function($o) { return $o->id; }, $objects);
// result: array(1, 2, 3);
echo memory_get_usage() ."\n";
sleep(1);
}
4235136
4235168
4235168
4235168
...
我花了这么多时间来找到它,现在我想知道,如果它是垃圾收集器的错误或我犯了错误? 为什么将已经创建和调用的函数留在内存中才有意义,何时它永远不会被重用?
以下是一个正在运行的示例:http://ideone.com/9a1D5g
已更新:当我递归搜索我的代码及其依赖关系时,例如PEAR和Zend然后我经常发现这种 BAD 方式。
更新:当嵌套两个函数时,我们从内到外继续以评估此表达式。换句话说,它首先开始create_function
(一次),并且返回的函数名称是array_map
的单个调用的参数。但是因为GC忘记将其从内存中删除(没有指针留在内存中的函数)并且PHP无法重用已经位于内存中的函数让我觉得有一个错误,而不仅仅是"表现不好"。这个特定的代码行是PHPDoc中的一个例子,并在许多大框架中重用,例如Zend和PEAR等等。只需一行就可以解决这个问题" bug",检查。但我并没有寻找解决方案:我正在寻找真相。这是一个错误还是仅仅是我的方法。后者我还不能决定。
答案 0 :(得分:11)
在create_function()
的情况下,使用eval()
创建lambda样式的函数,并返回包含其名称的字符串。然后将该名称作为参数传递给array_map()
函数。
这与闭包式匿名函数不同,后者根本不使用包含名称的字符串。 function($o) { return $o->id; }
是函数,或者更确切地说是Closure类的一个实例。
eval()
函数在create_function()
内执行一段PHP代码,用于创建所需的函数。有点像这样:
function create_function($arguments,$code)
{
$name = <_lambda_>; // just a unique string
eval('function '.$name.'($arguments){$code}');
return $name;
}
请注意,这是一种简化。
因此,一旦创建了该函数,它将一直持续到脚本结束,就像脚本中的普通函数一样。在上面的BAD示例中,在循环的每次迭代中都会像这样创建一个新函数,占用的内存越来越多。
然而,你可以故意破坏lambda风格的功能。这很简单,只需将循环更改为:
while (true)
{
$func = create_function('$o', 'return $o->id;');
$objects = array_map($func, $objects);
echo memory_get_usage() ."\n";
sleep(1);
}
包含函数引用(= name)的字符串是expliciet,可在此处访问。现在,每次调用create_function()
时,旧函数都会被新函数覆盖。
所以,不,没有'内存泄漏',它意味着以这种方式工作。
当然,下面的代码效率更高:
$func = create_function('$o', 'return $o->id;');
while (true)
{
$objects = array_map($func, $objects);
echo memory_get_usage() ."\n";
sleep(1);
}
并且只应在PHP版本不支持闭包式匿名函数时使用。
答案 1 :(得分:3)
如果可以避免,请不要使用create_function()
。特别不重复。根据{{3}}中的大黄色警告框:
...它具有糟糕的性能和内存使用特性。
答案 2 :(得分:2)
好吧,我认为问题是,create_function
的第一个解决方案是在旧版本的PHP上运行,而第二个解决方案并没有增加不需要的内存。但是,让我们来看看第一个解决方案。 create_function
方法在array_map
内调用,即每次while
次迭代。如果我们想要一个解决方案来使用旧的PHP版本而不增加内存,我们必须在每个while
次迭代中对旧的函数实例进行跟踪:
$func = create_function('$o', 'return $o->id;');
$catIds = array_map($func, $objects);
这就是全部。这么简单。
但它根本没有回答这个问题。剩下的问题是它是否是PHP或功能的错误。根据我的理解,在变量中写create_function
的结果的方式应该与将它直接作为参数放在array_map
中相同,不是吗?