Asked  7 Months ago    Answers:  5   Viewed   58 times

I'm checking out some PHP 5.3.0 features and ran across some code on the site that looks quite funny:

public function getTotal($tax)
{
    $total = 0.00;

    $callback =
        /* This line here: */
        function ($quantity, $product) use ($tax, &$total)
        {
            $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                strtoupper($product));
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);
        };

    array_walk($this->products, $callback);
    return round($total, 2);
}

as one of the examples on anonymous functions.

Does anybody know about this? Any documentation? And it looks evil, should it ever be used?

 Answers

36

This is how PHP expresses a closure. This is not evil at all and in fact it is quite powerful and useful.

Basically what this means is that you are allowing the anonymous function to "capture" local variables (in this case, $tax and a reference to $total) outside of it scope and preserve their values (or in the case of $total the reference to $total itself) as state within the anonymous function itself.

Wednesday, March 31, 2021
 
Zigglzworth
answered 7 Months ago
49

Your problem is that you aren't injecting your dependency (the closure), which always makes unit testing harder, and can make isolation impossible.

Inject the closure into SUT::foo() instead of creating it inside there and you'll find testing much easier.

Here is how I would design the method (bearing in mind that I know nothing about your real code, so this may or may not be practical for you):

class SUT 
{
    public function foo($bar, $someFunction) 
    {
        $bar->baz($someFunction);
    }
}

class SUTTest extends PHPUnit_Framework_TestCase 
{
    public function testFoo() 
    {
        $someFunction = function() {};

        $mockBar = $this->getMockBuilder('Bar')
             ->setMethods(array('baz'))
             ->getMock();
        $mockBar->expects($this->once())
             ->method('baz')
             ->with($someFunction);

        $sut = new SUT();

        $sut->foo($mockBar, $someFunction);
    }
}
Wednesday, March 31, 2021
 
mgraph
answered 7 Months ago
13

PHP will support closures natively in 5.3. A closure is good when you want a local function that's only used for some small, specific purpose. The RFC for closures give a good example:

function replace_spaces ($text) {
    $replacement = function ($matches) {
        return str_replace ($matches[1], ' ', ' ').' ';
    };
    return preg_replace_callback ('/( +) /', $replacement, $text);
}

This lets you define the replacement function locally inside replace_spaces(), so that it's not:
1) Cluttering up the global namespace
2) Making people three years down the line wonder why there's a function defined globally that's only used inside one other function

It keeps things organized. Notice how the function itself has no name, it simply is defined and assigned as a reference to $replacement.

But remember, you have to wait for PHP 5.3 :)

You can also access variables outside it's scope into a closure using the keyword use. Consider this example.

// Set a multiplier  
 $multiplier = 3;

// Create a list of numbers  
 $numbers = array(1,2,3,4);

// Use array_walk to iterate  
 // through the list and multiply  
 array_walk($numbers, function($number) use($multiplier){  
 echo $number * $multiplier;  
 }); 

An excellent explanation is given here What are php lambdas and closures

Wednesday, March 31, 2021
 
Vlad
answered 7 Months ago
75

You can't. You'd have to call it first. And since PHP doesn't support closure de-referencing yet, you'd have to store it in a variable first:

$f = function(){
    $data = array(
        'fruit'     => 'apple',
        'vegetable' => 'broccoli',
        'other'     => 'canned soup');
    return $data;
};
myfunction($f());
Saturday, May 29, 2021
 
pop
answered 5 Months ago
pop
15

The copy constructor exists to make copies. In theory when you write a line like:

CLASS c(foo());

The compiler would have to call the copy constructor to copy the return of foo() into c.

Copy elision is a technique to skip calling the copy constructor so as not to pay for the overhead.

For example, the compiler can arrange that foo() will directly construct its return value into c.

Here's another example. Let's say you have a function:

void doit(CLASS c);

If you call it with an actual argument, the compiler has to invoke the copy constructor so that the original parameter cannot be modified:

CLASS c1;
doit(c1);

But now consider a different example, let's say you call your function like this:

doit(c1 + c1);

operator+ is going to have to create a temporary object (an rvalue). Instead of invoking the copy constructor before calling doit(), the compiler can pass the temporary that was created by operator+ and pass that to doit() instead.

Thursday, June 3, 2021
 
khaverim
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :