I found following solution here on StackOverflow to get an array of a specific object property from array of objects: PHP - Extracting a property from an array of objects
The proposed solution is to use array_map
and within create a function with create_function
as following:
$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
What happens?: array_map
runs through each array element in this case a stdClass
object. First it creates a function like this:
function($o) {
return $o->id;
}
Second it calls this function for the object in the current iteration. It works, it works nearly same like this similar solution:
$catIds = array_map(function($o) { return $o->id; }, $objects);
But this solution is only running in PHP version >= 5.3 because it uses the Closure
concept => http://php.net/manual/de/class.closure.php
Now the real problem:
The first solution with create_function
increases the memory, because the created function will be written to the memory and not be reused or destroyed. In the second solution with Closure
it will.
So the solutions gives the same results but have different behaviors with respect to the memory.
Following example:
// 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
...
GOOD
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
...
I spend so many time to find this out and now I want to know, if it's a bug with the garbage collector or do I made a mistake? And why it make sense to leave the already created and called function in memory, when it'll never be reuse?
Here is a running example: http://ideone.com/9a1D5g
Updated: When I recursively search my code and it's dependencies e.g. PEAR and Zend then I found this BAD way too often.
Updated: When two functions are nested, we proceed from the inside out in order to evaluate this expression. In other words, it is first starting create_function
(once) and that returning function name is the argument for the single call of array_map
. But because GC forget to remove it from memory (no pointer left to the function in memory) and PHP not be able to reuse the function already located in memory let me think that there is an error and not only a thing with "bad performance". This specific line of code is an example in PHPDoc and reused in so many big frameworks e.g. Zend and PEAR and more. With one line more you can work around this "bug", check. But I'm not searching for a solution: I'm searching for the truth. Is it a bug or is it just my approach. And latter I could not decide yet.
In the case of
create_function()
a lambda-style function is created usingeval()
, and a string containing its name is returned. That name is then passed as argument to thearray_map()
function.This differs from the closure-style anonymous function where no string containing a name is used at all.
function($o) { return $o->id; }
IS the function, or rather an instance of the Closure class.The
eval()
function, insidecreate_function()
, executes a piece of PHP code which creates the wanted function. Somewhat like this:Note that this is a simplification.
So, once the function is created it will persist until the end of the script, just like normal functions in a script. In the above BAD example, a new function is created like this on every iteration of the loop, taking up more and more memory.
You can, however, intentionally destroy the lambda-style function. This is quite easy, just change the loop to:
The string containting the reference (= name) to the function was made expliciet and accessible here. Now, every time
create_function()
is called, the old function is overwritten by a new one.So, no, there's no 'Memory leak', it is meant to work this way.
Of course the code below is more efficient:
And should only be used when closure-style anonymous function are not supported by your PHP version.
OK, I think the problem is, that first solution with
create_function
is running on older versions of PHP and the second solution doesn't increase the memory unnecessary. But let's have a look at first solution. Thecreate_function
method is called inside thearray_map
, namely for eachwhile
iteration. If we want a solution to work with older PHP versions and without increasing memory we have to do following to the older function instance on eachwhile
iteration:That's all. So simple.
But it also isn't answering the question at all. What remains is the question if it is a bug with PHP or a feature. For my understanding that way to write the result of
create_function
in a variable SHOULD be the same as put it directly as parameter inarray_map
, isn't it?Don't use
create_function()
if you can avoid it. Particularly not repeatedly. Per the big yellow Caution box in the PHP manual: