Fluent Interfaces in PHP

  • Posted by Mike Naberezny in PHP

    Today on Martin Fowler’s bliki, I read a great new article describing what he calls “fluent interfaces“. Normally, most setter methods of an object will return void. In an object with a fluent interface, setters will instead return an object to promote object chaining.

    In PHP 4, it was not possible to do chaining, but one of the great new features of PHP 5 is that it supports directly deferencing objects returned by methods. It might sound complicated, but it’s really not and it has the potential to greatly improve the readability of some code. Let’s take a look at how to implement a fluent interface in PHP.

    Fowler’s Java code uses the example of building an order for a customer, which I’ll show here as the equivalent PHP 5 method:

    private function makeNormal(Customer $customer) {
        $o1 = new Order();
        $customer->addOrder($o1);
        $line1 = new OrderLine(6, Product::find("TAL"));
        $o1->addLine($line1);
        $line2 = new OrderLine(5, Product::find("HPK"));
        $o1->addLine($line2);
        $line3 = new OrderLine(3, Product::find("LGV"));
        $o1->addLine($line3);
        $line2->setSkippable(true);
        $o1->setRush(true);
    }

    The example above could definitely benefit from some whitespace and inline comments, but it’s still pretty unruly even with them. A possible solution to improve this code is to implement a fluent interface. Here’s the same code as above, expressed as a fluent interface:

    private function makeFluent(Customer $customer) {
        $customer->newOrder()
                 ->with(6, "TAL")
                 ->with(5, "HPK")->skippable()
                 ->with(3, "LGV")
                 ->priorityRush();
    }

    This is much more readable compared to the last example. Notice one thing unconventional about the example above: the lines do not terminate with semicolons. Since PHP is whitespace agnostic, it is the $same->as()->this().

    In PHP 5 terms, a fluent interface to an object is one where the setter methods return an object handle. It is perhaps simplest to always return $this, however any object handle can be returned. Here’s a simple PHP class that demonstrates how a fluent interface is built:

    class Fluent {
        public function hello() {
            echo "hello ";
            return $this;
        }
     
        public function world() {
            echo "world";
            return $this;
        }
    }
     
    $fluent = new Fluent();
    $fluent->hello()
           ->world();

    The code above will output hello world. As you can see, a fluent interface is quite easy to implement. Both Martin Fowler and Richard Davey recommend carefully considering where to use them. When done right, this technique has the potential to significantly improve the readability of some code.

15 comments

  • pingback by Paul M. Jones » Blog Archive » Fluent Interfaces Require Fluent Situations 29 Dec 05

    [...] equire Fluent Situations My friend and coworker Mike Nabarezny wrote recently of fluent interfaces (a.k.a. object chaining). You should read more about it there, but in the mean time, [...]

  • pingback by The Infotainment Telesector » Archive » Fluent Interfaces, aka Method Chaining 29 Dec 05

    [...] uent Interfaces, aka Method Chaining 29 December 2005 A few folks have been talking about “fluent interfaces”. I first saw this pattern (and it’s [...]

  • pingback by Full(o)bloG » Blog Archive » PHP e programmazione fluente 30 Dec 05

    [...] con un certo interesse del nuovo meme di planet-php, cioè dell’uso di interfaccie fluent. [...]

  • pingback by Fluyd.net » POO 8 Jan 06

    [...] href=”http://www.aditus.nu/jpgraph/phptip01.php”> fluent interfaces (object chaining) http://www.mikenaberezny.com/archives/35 Entry Filed under: Ressources Leave a Commen [...]

  • pingback by StR » Blog Archive » Interfaces fluidas 15 Feb 06

    [...] c message($message){ $this->message = $message; return $this; } } ?> En los blogs de Mike Naberezny y Paul M. Jones pueden encontrar otros ejemplos. This entry was [...]

  • pingback by Herbert AraujoSeção de dados do blog » Fluent Interfaces no PHP 5 24 Feb 06

    [...] uma explicação mais detalhada da técnica acesse “FluentInterface” e “Fluent Interfaces in PHP“. Este conteúdo foi publicado em Sexta, 24 de Fev [...]

  • comment by Anon 10 Mar 06

    hello,

    > It is perhaps simplest to always return $this, however any object
    > handle can be returned.

    looking at the zend framework, i note that there are places in the controller package, which return $this, so i have to think that is why you have done this, in regards to fluent interfaces?

  • comment by FGM 4 Nov 06

    There’s still something unclear with this example: in the traditional style, skippable() clearly applies to $line2.

    However, in the fluent style, you apply it to the result of with(5, ‘HPK’), which is supposed to be $this representing the customer object, meaning the method skippable() is actually applied to the customer, not to the OrderLine to which it is appended.

    Now, if the methods do not return $this, but maybe the created object (OrderLine in this case), AND if the the created object inherits from the original object (Customer) or contains a back reference to it, skippable() can be defined as a method of the created OrderLine and still return the original Customer for the next call to with(), but now, just suppose the OrderLine also needs another optional method like skippable() : how will this one obtain the OrderLine object ? This introduces limitations that seem unexpected initially.

    Of course, in any case, methods could store additional information in the original object so methods like skippable() could fetch the OrderLine info from the Customer object, but this means that the class decoupling has been removed: methods for OrderLine now need knowledge about Customer, which they shouldn’t be needing in the first place (law of Demeter ?).

    Or maybe I missed something ?

  • comment by Nick 16 Oct 07

    Hi Mike,

    Hope you see this, given that I’m commenting on a 2-year-old article, but since this page is the first Google hit for “PHP fluent interfaces” I’ve got a question-

    Is there a workaround for when a method in the middle of a fluent call returns null, or a non-object, or something else unexpected? For instance, the SimpleXML API tries to expose a fluent interface, but it’ll throw you a fatal error if you try to access nodes that don’t exist, then call methods on them. Like
    foo->bar->baz->children();
    ?>
    if there is no node.

    Or maybe you have an ORM where an Order has Items, and an Item may or may not have a shipping record with a shipping date, you might do this:
    getItem(0)->getShippingRecord()->date;
    ?>
    which would fail miserably if the Item has no shipping record.

    I feel like usability would improve if my program would just return null in these cases. Are you aware of anything that would facilitate this?

  • comment by Mike Naberezny 22 Oct 07

    No, there is not a workaround.

    $ php -r '$o = null; $o->anything;';
    PHP Notice:  Trying to get property of non-object in Command line code on line 1
    
    $ php -r '$o = null; $o->anything();';
    PHP Fatal error:  Call to a member function anything() on a non-object in Command line code on line 1
    

    If you attempt to get a property of a non-object, a notice will be raised. If you attempt to call a method on a non-object, a fatal error will occur.

    There are few examples of true fluent interfaces in PHP. One is the PHPUnit mocking API, which you can read about in the Pocket Guide. Its methods use appropriate vocabulary that make it a fluent interface as described by Martin Fowler’s original article. Since it is designed as an actual fluent interface, each method always returns the object handle and you don’t have these problems.

    Both of your examples above use object chaining but neither is a fluent interface. It’s important to understand the difference and not abuse chaining.

  • comment by Nick 22 Oct 07

    I think I explained my thoughts pretty poorly, so I’ll try again. I’ll take your above example of a fluent interface to fill out a customer’s order.

    Think of the process of filling up that order as if it were a physical assembly line in a factory. The order starts as an empty container headed down a conveyor belt. At each step, a robot picks up the container, operates on its contents to add, alter, or remove things, and puts the container back on the line.

    But when there is a side-effect created by one of those robots, how do we deal with that further down the assembly line? Maybe the factory is out of “TAL” widgets. One option would be to fail immediately. I tend to think that the construction of our customer’s order should continue, adding the HPK and LGV widgets. The robot in charge of adding that TAL widget to the order, should attach a notice with some nice info, perhaps the next expected date of TAL availability. At the end of my metaphorical assembly line, a quality-control engineer would see that notice and take action, perhaps throwing out the order, perhaps presenting some options to the customer.

    It’s pretty obvious that Order->with() can never return null or throw an exception. These fluent interfaces like phpUnit’s mock object API surely make certain that null returns or exceptions will never arise. So, is it part of the definition of a fluent interface that the chained method calls will never return null, and never throw exceptions?

  • trackback by This Dev For Hire 31 Mar 08

    Fluent interfaces and code readability…

    Fluent interfaces. Sigh. They help sometimes, they really do. In fact I don’t have anything against the idea, it’s the actual use that makes me uncomfortable….

  • pingback by This Dev For Hire : Fluent interfaces and code readability 7 Apr 08

    [...] to make my case stronger, I will use an example taken directly out of the blog of Mike Naberezny – Fluent Interfaces in PHP. Mike’s a renown PHP authority, so it should make you [...]

  • comment by Pablo Morales 27 May 08

    Great explanation. I know this method with Zend_Db_Select :P

  • pingback by PHP Fluency | Mullen Mills 7 Jul 08

    [...] Mike Naberezny’s articleis where I started [...]

Post a comment


Thanks for commenting. Please remember to be nice to others and keep your comment relevant to the discussion. I reserve the right to remove comments that don't meet these simple guidelines.