PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 4 ■ ADVANCED FEATURES

}


}


This code is designed to run my various callbacks. It consists of two classes. Product simply stores
$name and $price properties. I’ve made these public for the purposes of brevity. Remember, in the real
world you’d probably want to make your properties private or protected and provide accessor methods.
ProcessSale consists of two methods: registerCallback() accepts an unhinted scalar, tests it, and adds
it to a callback array. The test, a built-in function called is_callable(), ensures that whatever I’ve been
given can be invoked by a function such as call_user_func() or array_walk().
The sale() method accepts a Product object, outputs a message about it, and then loops through
the $callback array property. It passes each element to call_user_func() which calls the code, passing
it a reference to the product. All the following examples will work with the framework.
Why are callbacks useful? They allow you to plug functionality into a component at runtime that is
not directly related to that component's core task. By making a component callback aware, you give
others the power to extend your code in contexts you don’t yet know about.
Imagine, for example, that a future user of ProcessSale wants to create a log of sales. If the user has
access to the class she might add logging code directly to the sale() method. This isn’t always a good
idea though. If she is not the maintainer of the package, which provides ProcessSale, then her
amendments will be overwritten next time the package is upgraded. Even if she is the maintainer of the
component, adding many incidental tasks to the sale() method will begin to overwhelm its core
responsibility, and potentially make it less usable across projects. I will return to these themes in the
next section.
Luckily, though, I made ProcessSale callback-aware. Here I create a callback that simulates logging:


$logger = create_function( '$product',
'print " logging ({$product->name})\n";' );


$processor = new ProcessSale();
$processor->registerCallback( $logger );


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


I use create_function() to build my callback. As you can see, it accepts two string arguments.
Firstly, a list of parameters, and secondly the function body. The result is often called an anonymous
function since it's not named in the manner of a standard function. Instead, it can be stored in a variable
and passed to functions and methods as a parameter. That’s just what I do, storing the function in the
$logger variable and passing it to ProcessSale::registerCallback(). Finally I create a couple of
products and pass them to the sale() method. You have already seen what happens there. The sale is
processed (in reality a simple message is printed about the product), and any callbacks are executed.
Here is the code in action:


shoes: processing
logging (shoes)


coffee: processing
logging (coffee)


Look again at that create_function() example. See how ugly it is? Placing code designed to be
executed inside a string is always a pain. You need to escape variables and quotation marks, and, if the
callback grows to any size, it can be very hard to read indeed. Wouldn't it be neater if there were a more

Free download pdf