The Liskov Substitution Principle

Tweet

Welcome to the (overridden) Matrix. Hush… don’t tell anybody! In a deleted scene from the Matrix trilogy, the following dialogue takes place:

Morpheus: Neo, I’m inside the Matrix right now. Sorry to give you the bad news but our agent-tracking PHP program needs a quick update. It currently uses PDO’s query() method with strings to fetch the status of all the Matrix agents from our database, but we need to do that with prepared queries instead.

Neo: Sounds fine, Morpheus. Can I get a copy of the program?

Morpheus: No problem. Just clone our repository and take a look at the AgentMapper.php and index.php files.

Neo issues a few Git commands and soon the following code appear before his eyes.

<?php
namespace ModelMapper;

class AgentMapper
{
    protected $_adapter;
    protected $_table = "agents";

    public function __construct(PDO $adapter) {
        $this->_adapter = $adapter;
    }

    public function findAll() {
        try {
            return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ);
        }
        catch (Exception $e) {
            return array();
        }
    }   
}
<?php
use ModelMapperAgentMapper;

// a PSR-0 compliant class loader
require_once __DIR__ . "/Autoloader.php";

$autoloader = new Autoloader();
$autoloader->register();

$adapter = new PDO("mysql:dbname=Nebuchadnezzar", "morpheus", "aa26d7c557296a4e8d49b42c8615233a3443036d");

$agentMapper = new AgentMapper($adapter);
$agents = $agentMapper->findAll();

foreach ($agents as $agent) {
    echo "Name: " . $agent->name .  " - Status: " . $agent->status . "<br>";
}

Neo: Morpheus, I just got the files. I’m going to subclass PDO and override its query() method so it can work with prepared queries. Because of my superhuman powers, I should be able to get this working in a snap. Keep calm.

The smooth sound of a computer keyboard fills the air.

Neo: Morpheus, the subclass is ready to be tested. Feel free to check it out on your side.

Morpheus does a quick search on his laptop and sees the class below.

<?php
namespace LibraryDatabase;

class PdoAdapter extends PDO
{
    protected $_statement;

    public function __construct($dsn, $username = null, $password = null, array $driverOptions = array()) {
        // check if a valid DSN has been passed in
        if (!is_string($dsn) || empty($dsn)) {
            throw new InvalidArgumentException("The DSN must be a non-empty string.");
        }
        try {
            // attempt to create a valid PDO object and set some attributes.
            parent::__construct($dsn, $username, $password, $driverOptions);
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }

    public function query($sql, array $parameters = array())
    {
        try {
           $this->_statement = $this->prepare($sql);
           $this->_statement->execute($parameters);
           return $this->_statement->fetchAll(PDO::FETCH_OBJ);        
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
}

Morpheus: The adapter looks good. I’ll give it a shot right away just to check if our agent mapper will be able to keep track of the active agents traveling across the Matrix. Wish me luck.

Morpheus hesitates for a moment and runs the previous index.php file, this time using Neo’s masterpiece PdoAdapter class.

And then, a scream!

Morpheus: Neo, I’m sure you’re the One! It’s just that I got an awful fatal error on my face with the following message:

Catchable fatal error: Argument 2 passed to LibraryDatabasePdoAdapter::query() must be an array, integer given, called in path/to/AgentMapper on line (who cares?)

Another scream.

Neo: What went wrong?! What went wrong?!

More screams.

Morpheus: I really don’t know. Oh, Agent Smith is now coming for me!

The communication suddenly goes off. A long, heavy silence wraps up the dialogue, suggesting that Morpheus got caught by surprise and was seriously injured by Agent Smith.

LSP Doesn’t Stand for (L)azy, (S)illy (P)rogrammers

Needless to say the dialog above is fictional, but the problem is unquestionably real. If Neo had learned only one or two things about the Liskov Substitution Principle (LSP) as the renowned hacker he used to be, Mr. Smith could have been traced in a jiffy. Best of all, Morpheus would have been saved from the agent’s evils intentions. What a pity for him, indeed.

In many cases, however, PHP developers think about the LSP pretty much as Neo did before: LSP is nothing but a purist’s theoretical principle that has little or no application in practice. But they’re treading down the wrong path.

Even when the formal definition of the LSP makes eyes roll back (including mine), at its core it boils down to avoiding brittlely-defined class hierarchies where the descendants expose a behavior radically different from the base abstractions consuming the same contract.

In simple terms, the LSP establishes that when overriding a method in a subclass, it must fulfill the following requirements:

  1. Its signature must match that of its parent
  2. Its preconditions (what to accept) must be the same or weaker
  3. Its post conditions (what to expect) must be the same or stronger
  4. Exceptions (if any) must be of the same type than the ones thrown by its parent

Now, feel free to reread the above list over again (don’t worry, I’ll wait), and you’ll hopefully realize why this makes a lot of sense.

Back to the example, Neo’s cardinal sin was simply not to keep method signatures the same, hence breaking the contract with client code. To fix up this issue, the agent mapper’s findAll() method could be rewritten with some conditionals (a clear sign of code smell), as shown below:

<?php
public function findAll() {
    try {
        return ($this->_adapter instanceof PdoAdapter)
            ? $this->_adapter->query("SELECT * FROM " . $this->_table)
            : $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ);
    }
    catch (Exception $e) {
        return array();
    }
}

If you’re in a good mood and give the refactored method a try, it will work just fine, either when using a native PDO object or an instance of the PDO adapter. It may sound rough, I know, but this is only a quick and dirty fix which flagrantly violates the Opened/Closed principle.

On the other hand, it’s feasible to refactor the adapter’s query() method in order to match the signature of its overridden parent. But in doing so, all of the other conditions stated by the LSP should be fulfilled also. Simply put, this means that method overriding should be done with due caution, and only with strong, really strong, reasons.

In many use cases, and assuming that it’s not possible to use Interfaces, it’s preferable to create subclasses that only extend (not override) the functionality of their base classes. In the case of Neo’s PDO adapter, this approach will function like a charm, and definitively won’t blow up client code at any level.

As I just said, there’s a more efficient – yet radical – solution which appeals to the goodies of implementing interfaces. While the earlier PDO adapter was created via inheritance and admittedly broke the LSP commandments, the flaw comes actually from the way the agent mapper class was designed in the first place. Effectively, it relies from top to bottom on a concrete database adapter implementation, rather than on the contract defined by an interface. And the big OO powers say from ancient times that this is always a bad thing.

So, how would the aforementioned solution be brought to life?

Design by Contract and the Case against Inheritance

Well, first off it’d be necessary to define a simple contract, which should be implemented later on by concrete database adapters. A trivial interface, like the one below, will do the trick nicely:

<?php
namespace LibraryDatabase;

interface DatabaseAdapterInterface
{
    public function connect();    
    public function disconnect();
    public function query($sql);
}

So far, so good. With this interface up and running already, spawning a concrete database adapter is as simple as creating the following implementer:

<?php
namespace LibraryDatabase;

class PdoAdapter implements DatabaseAdapterInterface
{
    protected $_config = array();
    protected $_connection;  

    public function __construct($dsn, $username = null, $password = null, array $driverOptions = array())
    {
        if (!is_string($dsn) || empty($dsn)) {
            throw new InvalidArgumentException("The DSN must be a non-empty string.");
        }
        // save connection parameters in the $_config field
        $this->_config = compact("dsn", "username", "password", "driverOptions");
    }

    public function connect()
    {
        // if there is a PDO object already, return early
        if ($this->_connection) {
            return;
        }
        // otherwise try to create a PDO object
        try {
            $this->_connection = new PDO(
                $this->_config["dsn"], 
                $this->_config["username"], 
                $this->_config["password"], 
                $this->_config["driverOptions"]);
            $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
            $this->_connection->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
            
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    public function disconnect() {
        $this->_connection = null;
    }
    
    public function query($sql, $fetchStyle = PDO::FETCH_OBJ) {
        $this->connect();
        try {
            return $this->_connection->query($sql, $fetchStyle);
            
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
}

Done. Even when the above class is pretty contrived, it adheres neatly to the contract that it implements. This allows for a bunch of SQL queries to be run without much struggle, and above all, without overriding anything relevant by mistake.

The last change that must be made is to define an additional segregated interface, and refactor the initial mapper class so that the pitiless agents in the Matrix can be easily traced and beaten down in turn by injecting different database adapters at runtime.

That said, here’s how the aforementioned interface looks:

<?php
namespace ModelMapper;

interface AgentMapperInterface {
    public function findAll();
}

And here’s the revamped version of the agent mapper:

<?php
namespace ModelMapper;
use LibraryDataBaseDatabaseAdapterInterface;

class AgentMapper implements AgentMapperInterface
{
    protected $_adapter;
    protected $_table = "agents"; 

    public function __construct(DatabaseAdapterInterface $adapter) {
        $this->_adapter = $adapter;
    }
    
    public function findAll() {
        try {
            return $this->_adapter->query("SELECT * FROM " . $this->_table);
        }
        catch (Exception $e) {
            return array();
        }
    }      
}

Mission accomplished. Considering that now the refactored mapper expects to receive an implementer of the earlier database interface, it’s safe to say that its findAll() method won’t need to go into ugly checks to see what it got injected in the constructor. Definitively, we have a big winner here!

What’s more, the following code snippet shows how to put the previous elements to work side by side in sweet harmony:

<?php
use LibraryDatabasePdoAdapter,
    ModelMapperAgentMapper;

// a PSR-0 compliant class loader
require_once __DIR__ . "/Autoloader.php";

$autoloader = new Autoloader();
$autoloader->register();

$adapter = new PdoAdapter("mysql:dbname=Nebuchadnezzar", "morpheus", "aa26d7c557296a4e8d49b42c8615233a3443036d");
$agentMapper = new AgentMapper($adapter);
$agents = $agentMapper->findAll();

foreach ($agents as $agent) {
    echo "Name: " . $agent->name .  " - Status: " . $agent->status . "<br>";
}

Not bad at all, huh? The downside with this approach is that the whole refactoring process is way too drastic. And in more realistic use cases it won’t even be a viable solution (especially when dealing with large chunks of messy legacy code). Despite this, it shows in a nutshell how to create abstractions whose derivatives won’t break up the conditions imposed by the LSP, by using composition over inheritance, and with a pinch of Design by Contract also.

Of course, let’s not forget the morale of this story: the hard fight against mean machines that enjoy enslaving humans and using them like plain batteries will hopefully end up in a resounding triumph. In Neo we trust.

The end.

Closing Remarks (Outside the Matrix)

Being a central point of object-oriented design, and the “L” in the SOLID principles, the Liskov Substitution Principle has gained many angry detractors over the years, most likely because its academic definition is full of technical terms which blind sight and make hard to grasp what’s really behind its veil. Moreover, at face value it seems to contradict, or even condemn to an unavoidable doom of existence, of inheritance.

But this is just a misleading impression which vanishes into thin air as soon as one gets the principle’s actual meaning. At its core, LSP’s commitment is aimed at designing hierarchies of classes that expose a real IS-A relationship with each other, and where subclasses can be replaced by their base abstractions without spoiling the contract with client code.

So, make sure to stick to the principle’s commandments, and your life as a PHP developer (and hence your fat-wallet customers’) will be easier and way more enjoyable.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • mario

    I wonder if throwing buzzwords around will further peoples interest in sound design. There’s nothing wrong with explaining method signature compatibility without cargo cult programming acronyms.
    More contemporary and real OOP languages (Python) and userbases seem to do better there by just explaining the concept and without contracts (interface workaround).

    • Alex Gervasio

      I’d rather politely disagree with you. If by “buzzwords” you mean basic OOP stuff like Inheritance/Composition (or any other form of delegation)/interface implementation, I’d say you’re somewhat overrating central concepts that are fairly easy to catch, even for newcomers. IMHO, learning the LSP or other OO-related principle(s) it’s not just a matter of personal interest or even landing in the corners of a specific language; it’s just embracing the paradigm from a more committed standpoint. The sooner, the better.
      Thanks for the feedback :-)

  • http://blog.wallbash.com Edor

    Amazing post Alex!
    It’s been quite some time that I’ve been saying “We need someone to write a LSP post with Dollar signs in it” and then you delivered something that isn’t just a short “What is LSP” post but a really understandable walkthrough of the principle with everything I wanted to see in a post like that.

    Thanks a bunch for writing this. Finally something to link people that reads a lot better than wikipedia for phpeople!

    You made me *sigh* a little with the psr-0 zero remark thats really out of place in the sample code but all in all the text between the code is the really good part of this post anyway :)

    I btw. wouldn’t worry to much about people shouting at the “SOLID” acronym. As currently OO practices are making their way in all of the scripting languages there seem to be a lot of people that get offended by the notion that they might do something wrong/not-perfect and then ‘suddenly’ start complaining about the fact that those concepts have names for over 10 years.

    At least I’ve had 6 or 7 of those dicussions in the last two month while nothing the like happened in the reminder of last year.

    I know everyone is talking about SOLID at the moment and the topic seems to dominate current conferences but maybe you could do the whole series?

    I know that the explanations are there already but your way of expressing them might give people an easier time following along than liking them to Fowler does.

    • Alex Gervasio

      Hey Edor,
      Glad you enjoyed the writeup, specially if you’re riding on the SOLID train too (yeap, the rails make you jump up and down sometimes, but the trip is really worth :-)). So, basically what you mean is doing a PHP version of Robert Martin’s? Sounds good, even though Keith Casey already wrote a nice post here http://devzone.zend.com/1915/solid-oo-principles/. Maybe I could develop some approachable examples as well, and drop them into a pretty formal reading. I’ll keep that idea in mind.
      Thanks.

  • http://blog.icerock.com.ar Fernando Martin

    Hey Alex.
    Great post! Let me congratulate you by the simplicity and clarity you spread over it.
    Also let me say that it was so funny the Matrix scene!
    I’ve already 5 years programming but since I’ve learnt myself have little to no theoretical background (now I read books all the time) and your examples made it clear to me, with help of Wikipedia links.
    Un abrazo desde Buenos Aires!

    Saludos,
    Fernando

    • ALex Gervasio

      Hi Fernando,
      Thanks for the compliments. Good to know you’re digging deeper into the theoretical “goodies” of OOP and trying to get the most out them as well. The learning process is pretty hard, but fairly rewarding also. At least, you’ll have a few more things to throw out to your mates at work (assuming they’re real mates :-), of course). So, if you’re willing to tackle things more seriously, just make sure to grab at least GoF and Fowler’s/Martin’s classics, and keep them around all the time. The patterns/principles are language agnostic, so you shouldn’t have a hard a hard time implementing them in PHP.
      Un abrazo desde Mendoza.

  • Ivan

    Hey Alex, good explanation of LSP. Just one little remark about adapter. I was wondering why should “decouple” “Adapter” and “Adaptee” when the goal of adapter is both classes to be coupled. Sometimes Adaptee object is hard to create (lot of dependencies to be set). In case you create Adaptee instance in the adapter you should pass all the dependencies in single function (e.g. construct). Also it is hard to mock Adaptee object for unit tests.
    In the example with pdo your interface should have only query method. Your client code should use only query method (as it is in AgentMapper). Here is some code:
    interface DatabaseAdapterInterface { public function query($sql); }

    class PdoAdapter implements DatabaseAdapterInterface {
    public function __construct(Pdo $pdo) {$this->pdo = $pdo;}
    public function query($sql) {$this->pdo->query($sql);}
    }

    With such implementation you’re free to mock Pdo object for unit tests, and also you can set as many options as you need before passing Pdo instance to adapter (e.g. force new connection). It’s just more flexible.
    Ivan

    • Alex Gervasio

      Hey Ivan,
      I’m a big fan of DI too. When it comes down to creating adapters (and specially PDO ones), though, there’re several approaches to pick up, wit each one having its pros and cons. The first one and most straightforward is via Inheritance (PdoAdapter extends PDO, not lazy database connection). Second, via Composition/Aggregation (your case in point, where the adaptee is easily mockable, but still not lazy database connection). Third, by means of Delegation, by creating a reference to the adaptee in the adapter, without injecting anything in the constructor. There’s lazy database connection here.

      In the post’s example, I went with the first one, because of brevity/clarity. With PDO, I normally tend to go under the umbrella of the third one, due to the lazy connection thing. With more generic adapters that don’t use native PHP classes (PDO, ArrayObject and so forth), Composition/Aggregation is definitively more flexible/testable.
      Thanks for the feedback.

  • Ivan

    Hi again :)
    I’m agree there are three approaches.
    First one (via extend) violates LSP. That’s enough to stop talking about it.
    Few weeks ago I was wondering witch one of other two is better. I decided to ask in stackoverflow about it.
    http://stackoverflow.com/questions/8838337/adapter-pattern-and-dependency
    After I read the answers and links other people gave me I think the only way to create adapter is via composition.
    Best regards,
    Ivan.

    • http://WebsiteURL Alex Gervasio

      A few points worth stressing:

      1) Inheritance doesn’t break the LSP, as long as you extend correctly the interface of the adaptee. Check this out: https://github.com/leftnode/jolt/blob/master/jolt/pdo.php

      2) Sure, “new” operators are evil in the constructor. So, just think of the “connect()” method as a factory, which could be further extracted out.

      3) Whenever possible, use Composition to achieve Inversion of Control.
      Thanks.

  • Neo

    zzzzzzzzzzzzzzzzzzzzzzzzz

  • http://meta-blogger.com Michael Hall

    Hi Alex, i’ve been trying to wrap my head around oop php for a while now(and javascript) but only understand most of the basic stuff like defining classes and creating objects from them, but not much more with regard to inheritance and polymorphism and the rest of that stuff, since you suggested that these are kinda easy to catch onto, can you also suggest a nice tutorial that can walk a beginner from basic to intermediate to advanced, or perhaps you or someone else can start a series, i’m also open to simply buying a book on oop php but they never seem to give good examples of the concepts if they even explain the concepts clearly in the first place. I’ve been coding for 10 plus years in php, javascript, and created complex membership site scripts and even autoresponders but i can’t seem to find a really goos resource for oop php

  • http://meta-blogger.com Michael Hall

    many thanks Alex. I understand that oop is more of a concept or rather a programming paradigm that helps us code with re-suable objects instead of procedurally, and i think it is a really intuitive way to code, but in the end you still need good tutorials by good teachers to show you the standard way of doing it in any particular language – i especially want to know more about extending classes and composition and the various design patterns, but they are real tough to decode since they don’t tend to show actual syntax and often use pseudo code to describe the concepts. i’m definetly going to dive in and holla at you if need further enlightenment, who knows maybe i can break down the concepts even more for other beginners once i have grokked it, lol. Thank you very much for the head start.

  • http://www.dowridgestudio.com Eric Jones

    I know that this was posted a long time ago. But I have just now come across a situation that matches the situation described here. At work, we are creating an abstract class called Collection. We will then extend this class for use with our business objects such as MemberCollection and LocationCollection. There are corresponding classes Member and Location which extend a base class, DomainObject.

    So finally here is my question:
    When we are extending our Collection Classes the default is to have the type hint of DomainObject. But because we don’t want to have a Location Object in our MemberCollection should we overwrite the add method with the type hint of Member or leave the method signature the same and check that the passed in object is an instance of Member from within the method?

    Here is my take:
    I think we should overwrite the type hint in the extending class because the Collection class is abstract anyway so it was never meant to have a “signature” anyway. Am I on target following this principle or am I totally missing the point?
    Thanks.