There’s a programming style I rarely see in the PHP world, but one which I use from my C programming days – programming by contract. It’s a very useful technique for writing code that is demonstrably robust, and a useful compliment to unit testing with PHPUnit.

At it’s most basic, programming by contract can be summed up as:

  • Does my function or method have inputs that are acceptable to me?
  • Has my function or method generated return data that I’m happy to pass back.

PHP has the assert() method to help with this, but it is deficient and best avoided. I’d like to share the approach I’m currently using for this, to get constructive feedback on how to evolve the style further.

At the heart of this approach lies the constraint. It is a test that must be satisfied; a bit like a runtime unit test. We could do this inline:

function breakMe($inputData = "I am bad data")
{
    // enforce our constraint
    if (!is_array($inputData))
    {
        throw new Exception('Bad data $inputData; expected array()');
    }
}

… but the problem with that is that it quickly bulks out your code with a lot of repetitive (and avoidable) content. An ideal candidate to make into a function or a method, which could yield:

function constraint_mustBeArray(&$testData)
{
    if (!is_array($testData))
    {
        throw new Exception("Constraint failed");
    }
}

function breakMe($inputData = "I am bad data")
{
    // our constraint is now in a nice function
    constraint_mustBeArray($inputData);
}

Here, we are trading performance (the cost of a function call) for both developer efficiency and reduced future maintenance costs. In general, the trade-off is worth it; most PHP developers work on small sites where developers are more expensive than runtime costs (within reason). The time saved from proving that code is working (and bailing immediately we prove otherwise) is worth saving.

We’re also introducing an important principle: there’s no return value to check. If execution continues on the line below the call to constraint_mustBeArray(), we can assume that the constraint was passed. If the constraint failed, we let whatever exception handlers there are, well, handle it.

There’s a couple of problems with this style that have been nagging me. It has a lot of advantages, and is probably good enough, but …

  • There’s no autoload support in PHP for functions, making it a pain to work with a large number of functions in an app or framework. Life is easier if the constraints are defined as object methods instead.
  • There’s limited potential to re-use constraints once they have been defined. They have to be explicitly called. It would be great if they could be passed into (say) a data model layer of some kind as parameters, to assist in data integrity checking. (Note the common thread of making sure that bad data is detected as early as possible, and pro-actively rejected by the app).

This led me to a more OO style:

class MyFramework_Array
{
    static public function mustBeArray(&$testData)
    {
        if (!is_array($testData))
        {
            throw new Exception('Constraint failed');
        }
    }
}

function breakMe($inputData = "I am bad data")
{
    // constraint is now in a reusable object method
    MyFramework_Array::mustBeArray($inputData);
}

Well, it is object-oriented for sure, but it still isn’t reusable … but it could be with lambda functions …

class MyFramework_Array
{
    static public function mustBeArray(&$testData)
    {
        // create the lambda function
        $constraint = function($testData)
        {
            if  (!is_array($testData))
            {
                throw new  Exception('Constraint failed');
            }
        };

        if ($testData !== null)
        {
            $constraint($testData);
        }
        else
        {
            return $constraint;
        }
    }
}

function breakMe($inputData = "I am bad data")
{
    // we can still call the constraint as before ...
    MyFramework_Array::mustBeArray($inputData);

    // ... but we can now also do the following ...
    $constraint = MyFramework_Array::mustBeArray();
    $constraint($inputData);
}

This approach gives us the flexibility of both worlds … a constraint method that we can call directly, and also a constraint function that we can assign to a variable to re-use as appropriate.

There’s still a couple of weaknesses with this approach, the most obvious one to me that finding these constraint methods is no longer quite as easy (you need to know which class they are defined on), but the counter-argument is that this is why frameworks have to rely on convention.

We’re quite heavily constrained by PHP’s syntax and parser limitations here, specifically the lack of macros (which could avoid runtime costs in production environments) and that we can’t assign lambda functions to class properties at declaration time.

I’m wondering how this style of declaring constraints could be refined further. You can take it as given that we would normally throw something other than Exception. Comments welcome :)

13 Comments

  1. Werner says:
    July 13th, 2010 at 7:45 pm

    Input checking is a good idea if you’re at risk of funky stuff happening.

    If you’re only type checking php can do that for you. See http://php.net/manual/en/language.oop5.typehinting.php

  2. SchizoDuckie says:
    July 13th, 2010 at 7:54 pm

    Isn’t what you are doing here exactly like type hinting in php5 ?

    http://ch2.php.net/language.oop5.typehinting

  3. SchizoDuckie says:
    July 13th, 2010 at 7:55 pm

    and whoever still uses &$ in php5 ??

  4. Stuart Herbert says:
    July 13th, 2010 at 8:50 pm

    Hi,

    Type hinting is a useful tool, and definitely should be used, but with programming by contract you can go a lot further (for example, checking for non-empty strings, checking for a well-formatted email address, and so on) that the PHP language can’t reasonably be expected to ever support.

    As for using & … well, it seems a waste of CPU and RAM to pass a copy of an array into that example constraint, especially as there’s no way that the constraint’s author can be sure of the size of the array. Forcing pass by reference is a reasonable optimisation here.

    Best regards,
    Stu

  5. a says:
    July 13th, 2010 at 9:03 pm

    I love how people think that wrapping function inside a class (and making it static) makes it somehow OO. Static methods are like ordinary function in different namespace – nothing “object oriented”.

    But let’s get back to the topic. Design by contract is possible with PHP but it goes against basic principle of PHP – dynamic/duck typing. It’s like trying to convert your car into motorcycle. If you want design by contract, use java or better eiffel.

    But more on the topic. Probably nicest and natural way to implement this would be through PHP reflection API and PHPDoc. In PHPDoc you can declare argument types and whichever constraints you want – in baseclass you will check these constraints and raise exceptions etc. The downsides are several: low performance – reflection API is quite understandably slow, you can’t use it with PHP accelerators (which deletes comments, including PHPDoc), it’s quite hard to do it right and to newcomers may seem magic-like.

    I’ve come through this and decided to leave design by contract for other languages and instead use dynamic/duck typing of PHP to maximal extent. Robustness can be achieved better by unit testing.

  6. Martin Kuckert says:
    July 13th, 2010 at 9:11 pm

    No Stuart, you’re thinking too much in terms of C. PHP does not copy your arguments, passed to a function. Each zval is copied just when writing to it. – called copy-on-write. Read more here: http://www.thedeveloperday.com/php-lazy-copy/

  7. Stuart Herbert says:
    July 13th, 2010 at 9:32 pm

    Hi Martin,

    Thanks for the link. Lazy copying of arrays is a PHP engine feature that I’d missed. I wonder why the PHP Manual has never been updated to reflect this? (http://www.php.net/manual/en/functions.arguments.php) The feature has been around since at least 2004 (http://bugs.php.net/29493) but at the time of writing it appears that it’s not implemented 100% accurately throughout the Zend Engine (http://bugs.php.net/bug.php?id=50894).

    Best regards,
    Stu

  8. Stuart Herbert says:
    July 13th, 2010 at 9:38 pm

    Hi anonymous,

    I remember in the early 90′s when C programmers objected to programming by contract too. Dynamic / duck typing doesn’t address all of the pitfalls that programming by contract can (because the contract can be the contents of the data, not just the data’s type), and I think you’ll ship more robust code by both having more robust code and better unit tests than just having better unit tests.

    Best regards,
    Stu

  9. Sebs says:
    July 14th, 2010 at 6:39 am

    Good explanation and standard php response: “This is not OOP” ;)

    The Typehinting example maybe was too simple, more complex examples may be useful, considering you are rebuilding a PHP buildt in function ;)
    As soon as you have abstracted the state of your application the whole concept comes more natural. But this is php, so all state is hidden in if’s and switches mixed up with other code, promoted as OOP.
    Maybe assert() should be mentioned too, a easy way to achieve some design by contract features.

  10. Juan José González says:
    July 14th, 2010 at 2:59 pm

    Since I read ‘The pragmatic programmer’ I am really interested to bring programming by contract to PHP, I am learning Eiffel to understand programming by contract almost perfectly.

    Maybe you can open a forum, a wiki, or an email list to discuss this interesting feature.

    Please send me news about this matter if you decide continue to my email.

  11. hallec says:
    January 11th, 2011 at 3:56 am

    Stuart, could you elaborate on why you believe the assert() method is deficient and best avoided?

  12. Stuart Herbert says:
    March 20th, 2011 at 5:08 pm

    @hallec: it doesn’t throw exceptions. if you use assert(), you can’t easily force unwinding of the stack. like trigger_error() and die() before it, it belongs to the history pages of programming.

  13. Jason Rowe says:
    October 14th, 2011 at 11:32 am

    Have you considered making your own array object as an adapter? As in custom data types in php?

    You could then pass around the object instead of the array, quickly checking its type with is_a() and also implementing any checking, sub classing of array types in the constructors. Objects can be treated like arrays, and also you could force read only , read write per element.

    This would mean that the contract is forced by the object type, instead of trying to contract elements of an array.

    Other languages have object types of int, float, array etc. Why shouldnt we do this in PHP?