Fluent Interfaces in PHP

December 20, 2006

Tutorials

p. Fluent Interfaces are not a new programming construct. However, PHP developers have not been able to use them until PHP 5. Now with PHP 5 and the ability to directly dereference an object, PHP developers can build objects using fluent interfaces.

p. For those who do not recognize this particular buzzword fluent interfaces is a way of chaining methods of an object together. By having a method return a reference to the object itself, @return $this;@ you chain methods together like this @$this->methodOne()->methodTwo()->methodThree();@. This can make your code easier to read, and that is the point of using fluent interfaces, making your code easier to read.

p. To be honest, until recently I had not paid much attention to fluent interfaces. You can write good programs without them. These days however, I’m doing a lot of work with the “Zend Framework”:http://framework.zend.com and the framework contributors make use of fluent interfaces. So I decided to dig a little deeper and find out if I was missing anything.

p. I started my digging “here”:http://weierophinney.net/matthew/. Since I talk with Matthew on a regular basis I know that he uses fluent interfaces in some of his Zend Framework contributions. Matthew pointed me to his friend “Mike Naberezny”:http://www.mikenaberezny.com. About this time last year Mike wrote a post in his blog talking about “fluent interfaces”:http://www.mikenaberezny.com/archives/35. In it he had this to say.

bq. 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.

p. In his blog post, Mike’s blog points to a “blog post by Martin Fowler”:http://martinfowler.com/bliki/FluentInterface.html where Martin discusses, among other things, the origins of the name “fluent interfaces”. Martin shows some good but Java centric examples of fluent interfaces in his post but the most interesting thing he contributed to the discussion was this line explaining in more detail than “make things look pretty” why fluent interfaces are useful.

bq. Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal “DomainSpecificLanguage”:http://martinfowler.com/bliki/DomainSpecificLanguage.html. Indeed this is why we chose the term ‘fluent’ to describe it. The API is primarily designed to be readable and to flow. The price of this fluency is more effort, both in thinking and in the API construction itself. The simple API of constructor, setter, and addition methods is much easier to write. Coming up with a nice fluent API requires a good bit of thought.

p. Both Mike and Martin discuss that fact that fluent interfaces are not for every situation. Like most programming techniques, there are times when it will simplify you code and make life easier. However, there are time when trying to implement them just does not make sense. As with any design decision, careful thought is recommended before implementing fluent interfaces.

p. Mike takes Martin’s Java example code and translates it into PHP 5 code so PHP developers can get a feel for how fluent interfaces work and how they can be beneficial. The example that Martin originally wrote and that Mike translated is that of an order entry system. In it they show the following two pieces of code. The first example is a snippet from a class in which the method makeNormal() is used to create an order in the system. The makeNormal() method is part of the order object.

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);
}
?>

p. Next, they show the fluent interface version of the code.

==
newOrder()
             ->with(6, 'TAL')
             ->with(5, 'HPK')->skippable()
             ->with(3, 'LGV')
             ->priorityRush();
}            
?>
==

p. As you can see, the second example is easier to read, assuming you understand that the with() method adds lines to the order. As stated earlier, this bit of magic is accomplished by the @with()@, @skippable()@, and @priorityRush()@ methods retuning a reference to the object. (@$this@) That is the one and only secret to fluent interfaces. Mike has a HelloWorld example in “his post”:http://www.mikenaberezny.com/archives/35 for those who want to see more code.

p. Paul M. Jones, also discusses fluent interfaces on his “blog”:http://paul-m-jones.com/blog/?p=188, responding to Mike’s original post. While Paul’s examples all revolve around his framework, Solar, the advice he gives on the subject makes his post well worth the read.

bq. I think, for a fluent interface to be effective, you need situations where you actually have all that information at one time so that you can chain the methods in a fluid way.

p. This will limit where you can use fluent interfaces but if you consider it carefully before implementing them, it will help you avoid having to re-code to remove them later.

p. In response to both Mike and Paul’s posts, Andi Gutmans posted about “fluent interfaces”:http://andigutmans.blogspot.com/2005/12/fluent-interfaces.html on his blog. In his post Andi gave more detailed advice on when he thinks they should be used.

bq. The million dollar question is when to actually use this kind of programming style. Of course there are no definitive answers but I suggest to consider the following points:
a) Use your intuition. If you don’t feel this will address the 95% of common use-case for using your interface, then it’s probably not the right solution for what you’re trying to accomplish.
b) As Paul noted, if in the common use-case you don’t have all the necessary data available to complete the task in one go, you should think twice about doing it.
c) Probably the most important point: It really has to read well in your language (e.g. English), preferably as a complete sentence. If you can’t read the code out aloud then it’s probably not what you want.

p. Mike talks in his post about using them on set*() methods but warns that using them on just some setters but not all setters will quickly create an unmaintainable situation. As we saw in the example above though, setters are not the only place they are useful. As Andi and Paul have noted, fluent interfaces can be implemented anytime you have all the information necessary to perform all of the tasks and your method does not need to return information directly to it’s caller.

p. This is where my code comes in. I just finished my second iteration of my “TagCloud”:http://www.calevans.com/view.php/page/tagcloud class. (“phpclasses.org page”:http://www.phpclasses.org/browse/package/3587.html) As you can see from the “source code”:http://www.calevans.com/tagCloud/TagCloud.phps I make use of fluent interfaces not only in my setters but in the other methods as well. In my case, only the get*() methods needed to return any information to the calling program. Therefore fluent interfaces worked well for me in this case. The culmination of the work can be see in the public method buildCloud().

==
 public function buildCloud()
    {
        $this->consolidateArray()
             ->filterArray()
             ->computeBounds()
             ->sortArray()
             ->computeWeights()
             ->buildOutput();
        return true;
    } // public function get_cloud()
==

p. Because all of the worker methods return @$this@, the buildCloud() method is a mere 2 lines long and very easy to read.

p. A small example of a method would be the @consolidateArray()@ method. IN a tag cloud, you want the tags weighted by frequency so you can score them. This function takes a single dimension array and counts the values in it. The results are stored in the _consolidatedArray property for later use. This method does not need to return any data to it’s caller so it’s a good candidate. It also passes Paul and Andi’s rule that we know all the information beforehand.

==    
    protected function consolidateArray()
    {
        $this->_consolidatedArray = array_count_values($this->inputArray);
        return $this;
    } // protected function consolidateArray()    
==

p. In reality though, you need more than one method to meet the criteria to make fluent interfaces a good choice. Unless you want to drive the programmer behind you batty, you really need to make it an all-or-nothing effort. Either all (most) of your methods in your class meet the criteria for fluent interfaces or they don’t. If they don’t, don’t try and force it. Having to remember which methods you can chain and which you can’t on a complex piece of code could become a maintenance nightmare.

p. If all of this seems a long way to go for readability, think of this. As PHP matures and more Enterprise scale application are built in it, the ability to produce easy to read code is skill that is actively being sought after.

p. =C=

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

3 Responses to “Fluent Interfaces in PHP”

  1. _____anonymous_____ Says:

    You might as well use pooQuery (just google for it)

  2. _____anonymous_____ Says:

    I have often thought about requesting this feature ("With") to be added to php. But hesitated to do so, however as a result of finding/reading your article I have now made an official request, which can be found here.

    http://bugs.php.net/bug.php?id=41023

    Hoy!
    Erik

  3. _____anonymous_____ Says:

    I have often wished that php had a language construct that is equivlent to the VB "With". How interesting that people even choose to name the "Fluent Interface" method "With". However, I see a couple of problems with this approach such as the lack of the ability to provide error feedback or useable output. The VB solution is much better in that it provides a generic across the board functionality that will work without modifying any of your code and you don’t give up any flexibility.

    <?php
    private function makeFluent(Customer $customer) {
    $customer->newOrder()
    ->with(6, ‘TAL’)
    ->with(5, ‘HPK’)->skippable()
    ->with(3, ‘LGV’)
    ->priorityRush();
    }
    //I suspect that debugging a chained operation like this could be rather difficult
    ?>

    Here is the same example written in VB6
    Each line has an implied reference to the object specified by the With construct. Note: in VB the dot ‘.’ is exactly equivlent to ‘->’

    Private Function VB6_UsingWith($customer as Customer)
    With $customer
    $Err = .newOrder()
    If $Err Then DoSomething()
    .NewLine(6, ‘TAL’)
    .NewLine(5, ‘HPK’)
    $Foo = .skippable()
    .NewLine(3, ‘LGV’)
    .priorityRush()
    End With
    End Function

    I added a couple of assignments to make it clear that each line is independant and capable of returning a result. You get full functionality without the baggage.

    Furthermore, the VB docs say that With is more speed efficient because the object only has to be resolved once.

    It’s been a long time since I have written any VB code. I am very satisfied with php as a replacment for VB6. But one thing that I really miss is php’s lack of a "With" operator.

    I refuse to jump on the VB.NET (aka Visual Fred) bandwagon. And I refuse to ever again be tied to a company as capricious and arbitrary as Microsoft. I ended up abandoning 200K lines of VB code — several years of work — because Microsoft decided that maintaining backwards compatibility was not important (not profitable enough).