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 using eval()
, and a string containing its name is returned. That name is then passed as argument to the array_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, inside create_function()
, executes a piece of PHP code which creates the wanted function. Somewhat like this:
function create_function($arguments,$code)
{
$name = <_lambda_>; // just a unique string
eval('function '.$name.'($arguments){$code}');
return $name;
}
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:
while (true)
{
$func = create_function('$o', 'return $o->id;');
$objects = array_map($func, $objects);
echo memory_get_usage() ."\n";
sleep(1);
}
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:
$func = create_function('$o', 'return $o->id;');
while (true)
{
$objects = array_map($func, $objects);
echo memory_get_usage() ."\n";
sleep(1);
}
And should only be used when closure-style anonymous function are not supported by your PHP version.
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