CHAPTER 4 ■ ADVANCED FEATURES
function writeAge( Person $p ) {
print $p->getAge()."\n";
}
}
I could, of course, subclass this to output Person data in various ways. Here is an implementation of
the Person class that uses both a PersonWriter object and the __call() method:
class Person {
private $writer;
function __construct( PersonWriter $writer ) {
$this->writer = $writer;
}
function __call( $methodname, $args ) {
if ( method_exists( $this->writer, $methodname ) ) {
return $this->writer->$methodname( $this );
}
}
function getName() { return "Bob"; }
function getAge() { return 44; }
}
The Person class here demands a PersonWriter object as a constructor argument and stores it in a
property variable. In the __call() method, I use the provided $methodname argument, testing for a
method of the same name in the PersonWriter object I have stored. If I encounter such a method, I
delegate the method call to the PersonWriter object, passing my current instance to it (in the $this
pseudo-variable). So if the client makes this call to Person:
$person = new Person( new PersonWriter() );
$person->writeName();
the __call() method is invoked. I find a method called writeName() in my PersonWriter object and
invoke it. This saves me from manually invoking the delegated method like this:
function writeName() {
$this->writer->writeName( $this );
}
The Person class has magically gained two new methods. Although automated delegation can save a
lot of legwork, there can be a cost in clarity. If you rely too much on delegation, you present the world
with a dynamic interface that resists reflection (the runtime examination of class facets) and is not
always clear to the client coder at first glance. This is because the logic that governs the interaction
between a delegating class and its target can be obscure—buried away in methods like __call() rather
than signaled up front by inheritance relationships or method type hints, as is the case for similar
relationships. The interceptor methods have their place, but they should be used with care, and classes
that rely on them should document this fact very clearly.
I will return to the topics of delegation and reflection later in the book.