Closures or create_function in PHP

2019-02-11 01:55发布

问题:

I had made a decision to use closures for my callbacks instead of create_function and as such only support PHP > 5.3 mostly due to the increased debugability and also because I assumed (what is it they say about assumption?) that the overhead of the on-the-fly compilation of the create_function in my situation would probably offset any extra comparisons and such that had to be made within in the function.

This may well still be the case (for my application) and further testing is required, but I was interested in the output of this (very) simple test, that shows the create_function method being more than twice as fast as the closure when it can remove just four conditionals (and concats). Obviously there is no extra processing going on in my test case, and that is where most of the speed will be gained or lost, but in the case where you have little extra processing but a lot of conditionals (that could be removed) and the callback is called enough times I started to think that it may be better to use create_function.

However with the obvious similarity between create_function and eval, I'm wary of it.

So the main question is what are the differences between anonymous functions created with create_function and those of closures?

A few specific questions I'm thinking of are, will create_function even work when eval functionality is disabled? And, I'm sure I read somewhere recently that create_function functions will pollute the global (or class) namespace even if declared as inner functions, but closures won't. I can't find the reference to this now, but are either or both of those statements true?


This is the little test I ran:

<?php

function foo($a=true, $b=true, $c=true, $d=true)
{
    $inner1 = create_function(
        '', 
        '$r = \''.($a ? 'a' : '').
                  ($b ? 'b' : '').
                  ($c ? 'c' : '').
                  ($d ? 'd' : '').'\';
         return $r;'
    );  


    $inner2 = function() use ($a, $b, $c, $d) 
    {   
        $r = ''; 
        if($a) { $r .= 'a'; }
        if($b) { $r .= 'b'; }
        if($c) { $r .= 'c'; }
        if($d) { $r .= 'd'; };
        return $r; 
    };  


    $time = microtime(true);
    for ($x=0; $x<99999; ++$x)
    {   
        $i1out = $inner1();
    }   
    echo '1:'.(microtime(true)-$time).'<br>';

    $time = microtime(true);
    for ($x=0; $x<99999; ++$x)
    {   
        $i2out = $inner2();
    }   
    echo '2:'.(microtime(true)-$time).'<br>';

    echo var_dump($i1out===$i2out).'<br>';
}

foo();

回答1:

The construct function() {..} is an anonymous function, and this feature is often implemented together with closures. Neither create_function nor anonymous functions pollute the global namespace.

Since anonymous functions can access surrounding variables (the closure part), they can, in theory, be slightly slower. On the other hand, if you're using bytecode caching (and if you're not, you are obviously not concerned about performance), I'd expect the "compilation" overhead of anonymous functions to be slightly slower.

However, it is extremely unlikely that the difference between anonymous functions and create_function is a source of performance problems. Therefore, I'd choose the more readable form of an anonymous function if you are so fortunate to have a target platform with php>5.3.



回答2:

create_function creates a global function that persists for the rest of the program. create_function simply returns the function name (a string), and therefore can have no idea if you still have access to that name stored somewhere somehow or not. Therefore, it can't be "garbage collected", even if you don't have access to the name anymore.

That means that a big problem is if you create a lot of functions using create_function, it will cause your program to run out of memory:

for ($i = 0; $i < 1000000; $i++) {
  $f = create_function('', '');
  // do stuff

  // don't use $f anywhere after this point
}

whereas with anonymous functions, that won't happen (the closure will be garbage collected).