chain()

Like many other languages, PHP is home to method chaining, a pattern that allows writing several mutators on the same object without having to name it more than once. A typical example can be found in the Zend Framework for configuration of e-mails, among other things :

$mail = new Zend_Mail();
$mail -> setBodyText('This is the text of the mail.')
      -> setFrom('somebody@example.com', 'Some Sender')
      -> addTo('somebody_else@example.com', 'Some Recipient')
      -> setSubject('TestSubject');

This is a very simple trick that is accomplished by having every mutator return the object itself.
However, the PHP syntax rules forbid calling a member function on the result of a new-expression, so that you always require a two-step sequence: initialize the object, then call its chain of mutators.

Of course, a simple solution is to use a function:

 function chain($obj) { return $obj; }

 $mail = chain(new Zend_Mail())
   -> setBodyText('This is the text of the mail.')
   -> setFrom('somebody@example.com', 'Some Sender')
   -> addTo('somebody_else@example.com', 'Some Recipient')
   -> setSubject('TestSubject');

In a similar vein, there’s the matter of using the method chaining pattern on objects that were not designed for that. This is where a quick wrapper can come in handy:

 // Define the appropriate class and function
 class WithWrapper
 {
   public $value;
   public function __construct($obj) {
     $this -> value = $obj;
   }
   public function __call($name, $args) {
     assert (count($args) === 1);
     $this -> value -> $name = $args[0];
     return $this;
   }
 }

 function with($obj) {
   return new WithWrapper($obj);
 }

 // A typical record class
 class Person
 {
   var $age;
   var $firstName;
   var $lastName;
   var $married;
 }

 // Create entry for Jane
 $jane = with(new Person())
   -> age(24)
   -> firstName("Jane")
   -> lastName("Smith")
   -> married(false)
   -> value;

 // Jane gets married
 with($jane)
   -> lastName("Brown")
   -> married(false);

This is starting to look like Visual Basic

3 Responses to “chain()”


  1. Hi,

    If Jane gets married, I guess you would better add married(true) :)
    Well ok, not fair from me talking about a mere typo…

    So let’s write a real comment.
    I went through your articles (but didn’t want to work on your “colles”). I really like the spirit you describe in “Filling in the Holes”. Specially, the way developpers should think, act and initiate and not act as basic coders.
    I deeply believe a real interaction is the key methodology to reach the best results.

    I’m curious to know why you chose to take Zend as an example and I’d like to have your point of view concerning Symfony.

    Best,

    Benjamin

    PS: you could improve your web skin by adding a favicon

  2. Hello Victor,

    That’s a nice trick you mention here. Now, I wonder if it’s desirable from a software design point of view and from a maintenance point of view.

    1) every step could throw an exception ; difficult to know which step, and thus to understand where the problem lies (if there is any problem ; I understand that you, like me, don’t put bugs in your code. We’re just too good at coding :) ) Les typing, but less robust too.

    2) I’m pretty sure I’m abusing it…
    with($jane)
    -> get_oldest_child()
    -> set_first_name(“Kirk”)
    -> married(true)

    3) Useful if you have many mutators ; but many mutators might not be a good thing from a design point of view (c’mon, you know I don’t like mutators).

    In the end, I wonder if method chaining is a pattern or an anti-pattern. Any idea ?

    • Victor Nicollet - November 16, 2009 at 10:14 am - Reply

      The entire point of exceptions is that you should not care what step they are thrown in (or, if you do care, you should separate the various calls and wrap them into distinct try-catch blocks). So I don’t really think 1) is an issue.

      Most of the time, mutators are exceedingly simple functions that serve as a shorthand notation for assigning member variables (possibly with a debug-version type check) or otherwise change small bits of state. So yes, your 2) is an example of abuse, since it adds a getter in the middle of a chain of mutators :)

      You could say that ‘with’ should be used in the same way as Visual Basic:

      with($jane -> getOldestChild())
      -> setFirstName("Kirk")
      -> married(true);

      That is, ‘with()’ represents the object being modified, and is only followed by mutators.

      Oh, and your cpp_naming_style betrays you ;)

      Having a lot of mutators can be useful: modern programming is rife with concepts that have insane numbers of parameters and configuration tweaks, be it email, HTTP requests or responses, databases… It is not very surprising that the classic example of mutator method chaining is Zend_Mail: an email class. While from a design point of view you could split that class into several classes, it wouldn’t match the actual concept of an email anymore…

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



1170 feed subscribers
(readers who polled a feed this week)