Fluent Interfaces 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.