PHP method chaining - Fluent interface

If you have used ever used Drupal or any other frameworks like Symfony, Laravel and so on have you probably come across code that look something like:

<?php
$foo
->setName('My name')
    ->
setPublished(1)
    ->
setTitle('This is my title');

// A alternative way to write it the code above in a single line.
$foo->setName('My name')->setPublished(1)->setTitle('This is my title');
?>

If you have a lot of setter methods is this a rather quick way of populating objects. All this do is, calling two setter methods from the class foo was made from. setName, setTitle and setPublished. It is called a Fluent interface where you are chaining your method calls. The regular way is to write your code like this:

<?php
$foo
->setName('My name')
$foo->setPublished(1);
$foo->->setTitle('This is my title');
?>

In this article are we going to have a look on how you can make your own class that do this in PHP. Though this is for PHP do many programming languages share look more and less like this. Let us make a simple Class that have three private variables that can only be set with its setter method.

<?php
declare(strict_types=1);

Class
User
{
    private
$userName;
    private
$workTitle;
    private
$status;

    public function
setName(string $userName): void
   
{
       
$this->userName = $userName;
    }

    public function
setWorkTitle(string $workTitle): void
   
{
       
$this->workTitle = $workTitle;
    }

    public function
setStatus(bool $status): void
   
{
       
$this->status = $status;
    }

    public function
__toString()
    {
        return
"User name: {$this->userName}, Work title: {$this->workTitle}";
    }
}
?>

A small Class that store: user name, work title and a employee status. Lets us create a object from this Class and make sure it works before we continue.

<?php
$user
= new User();
$user->setName('Modern PHP');
$user->setWorkTitle('Thinker');
$user->setStatus(true);
print
$user;
?>

User name: Modern PHP, Work title: Thinker

Great. The Class worked, and we verified that the variables got set. Let us try populating this Class by chaining these setter methods.

<?php
$user
= new User();
$user->setName('Modern PHP')->setWorkTitle('Thinker')->setStatus(true);
print
$user;
?>

PHP Fatal error: Uncaught Error: Call to a member function setWorkTitle() on null.

So that did not work. We are obviously missing something. The hint is in the error message. setName seemed to work but the following method setWorkTitle did not. To make method chaining work. Each method needs to return a reference to the object. See self/this/me. Let us try that and update our Class code.

<?php
declare(strict_types=1);

Class
User
{
    private
$userName;
    private
$workTitle;
    private
$status;

    public function
setName(string $userName): self
   
{
       
$this->userName = $userName;

        return
$this;
    }

    public function
setWorkTitle(string $workTitle): self
   
{
       
$this->workTitle = $workTitle;

        return
$this;
    }

    public function
setStatus(bool $status): self
   
{
       
$this->status = $status;

        return
$this;
    }

    public function
__toString()
    {
        return
"User name: {$this->userName}, Work title: {$this->workTitle}";
    }
}
?>

Let us re-test the Class.

<?php
$user
= new User();
$user->setName('Modern PHP')->setWorkTitle('Thinker')->setStatus(true);
print
$user;
?>

User name: Modern PHP, Work title: Thinker

Great! It worked. That is all to it but are you new to object oriented programming could this easily confuse you. You and also write the code like this, skipping the separate line when creating the object.

<?php
$user
= (new User())
  ->
setName('Modern PHP')
  ->
setWorkTitle('Thinker')
  ->
setStatus(true);
print
$user;
?>

Before I save and publish. Is the any drawback to this? There is always are some. Nothing comes for free. For me it is debugging. It could make debugging errors a little harder but writing Classes like this where with type hinting all over the place could make debugging easier. Edit: Barney Laurance made a valuable comment about other drawbacks to fluent interfaces, and links to a article written by Marco Pivetta (Ocramius).

Comments

I think this is not quite right: 'Each method needs to return a reference to the Class, called self. ' It's a reference to the object, i.e. the instance of the class, not a reference to the class. It's called $this, not self.

I totally agree. I updated that section. Thank you for your feedback :)

I think there are some much bigger disadvantages to this that aren't mentioned, big enough that I've mostly stopped using method chaining. Using method chaining makes the calling code a little shorter, but also significantly less self explanatory. With the regular version of the code it's easy to see that setName and setWorkTitle are both being called on the same object, $user . With the final version of the code, assuming we're not looking inside the User.php file, all we can see is that setWorkTitle() is called on the object which is returned from the setName() call. We can't actually see that it's the same object that setName was called on - setName could have returned a different object. Also with the first version, where everything returns void, nothing is likely to be using the return value. That means that if we come back a month later and decide it would be useful to start returning something - say the number of characters in the name - we can add in a return statement and start writing new code that uses it. Old code will just ignore the return value and keep working. Marco Pivetta goes into more detail on this: https://ocramius.github.io/blog/fluent-interfaces-are-evil/

Thank your for sharing your thoughts and bringing the excellent article written by Marco Pivetta to my attention. I found especially the paragraph where he touch on: *Fluent Interfaces break Decorators (and Composition)*. I have updated the article to include this and a link to your comment.

Add new comment