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 :)

Comments are closed.