PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 4 ■ ADVANCED FEATURES

class Totalizer {
static function warnAmount() {
return function( $product ) {
if ( $product->price > 5 ) {
print " reached high price: {$product->price}\n";
}
};
}
}


$processor = new ProcessSale();
$processor->registerCallback( Totalizer::warnAmount() );
...


Apart from the convenience of using the warnAmount() method as a factory for the anonymous
function, I have not added much of interest here. But this structure allows me to do much more than just
generate an anonymous function. It allows me to take advantage of closures. The new style anonymous
functions can reference variables declared in the anonymous functions parent scope. This is a hard
concept to grasp at times. It’s as if the anonymous function continues to remember the context in which
it was created. Imagine that I want Totalizer::warnAmount() to do two things. First of all, I’d like it to
accept an arbitrary target amount. Second, I want it to keep a tally of prices as products are sold. When
the total exceeds the target amount, the function will perform an action (in this case, as you might have
guessed, it will simply write a message.
I can make my anonymous function track variables from its wider scope with a use clause:


class Totalizer {
static function warnAmount( $amt ) {
$count=0;
return function( $product ) use ( $amt, &$count ) {
$count += $product->price;
print " count: $count\n";
if ( $count > $amt ) {
print " high price reached: {$count}\n";
}
};
}
}


$processor = new ProcessSale();
$processor->registerCallback( Totalizer::warnAmount( 8) );


$processor->sale( new Product( "shoes", 6 ) );
print "\n";
$processor->sale( new Product( "coffee", 6 ) );


The anonymous function returned by Totalizer::warnAmount() specifies two variables in its use
clause. The first is $amt. This is the argument that warnAmount() accepted. The second closure variable is
$count. $count is declared in the body of warnAmount() and set initially to zero. Notice that I prepend an
ampersand to the $count variable in the use clause. This means the variable will be accessed by reference
rather than by value in the anonymous function. In the body of the anonymous function, I increment
$count by the product's value, and then test the new total against $amt. If the target value has been
reached, I output a notification.
Here is the code in action:

Free download pdf