Back to Home

Contract programming in PHP

php · contract programming · dbc · AOP · contracts

Contract programming in PHP

    Contract programmingIn real life, we are everywhere faced with various contracts: when applying for a job, when performing work, when signing mutual agreements and many others. The legal force of the contracts guarantees us protection of interests and does not allow their violation without consequences, which gives us confidence that the items described in the contract will be fulfilled. This confidence helps us plan time, plan expenses, and plan the necessary resources. But what if the program code is described by contracts? Interesting? Then welcome to kat!

    Introduction


    The very idea of ​​contract programming arose in the 90s with Bertrand Meyer when developing the object-oriented programming language Eiffel. The essence of Bertrand’s idea was to have a tool to describe formal verification and formal specification of code. Such a tool would give concrete answers: “the method commits itself to do its job if you fulfill the conditions necessary for calling it”. And the contracts were the best suited for this role, because they allowed us to describe what would be received from the system (specification) if the preconditions were met (verification). Since then, many implementations of this programming technique have appeared both at the level of a specific language, and in the form of separate libraries that allow you to specify contracts and carry out their verification using external code. Unfortunately, in PHP there is no support for contract programming at the level of the language itself, so the implementation can only be performed using third-party libraries.

    Code Contracts


    Since contract programming was developed for an object-oriented language, it is not difficult to guess that the main working elements for contracts are classes, methods and properties.

    Preconditions

    The simplest version of the contract is preconditions - requirements that must be met before a specific action. Within the framework of OOP, all actions are described by methods in classes, therefore preconditions are applied to methods, and their verification occurs at the time the method is called, but before the method body itself is executed. The obvious use is to check the validity of the passed parameters to the method, their structure and correctness. That is, with the help of preconditions we describe in the contract everything that we definitely won’t work with. This is great!

    In order not to be unfounded, let's look at an example:

    class BankAccount
    {
        protected $balance = 0.0;
        /**
         * Deposits fixed amount of money to the account
         *
         * @param float $amount
         */
        public function deposit($amount)
        {
            if ($amount <= 0 || !is_numeric($amount)) {
                throw new \InvalidArgumentException("Invalid amount of money");
            }
            $this->balance += $amount;
        }
    }
    


    We see that the method of replenishing the balance in an implicit form requires a numerical value of the amount of replenishment, which must also be strictly greater than zero, otherwise an exception will be thrown. This is a typical precondition in code. However, it has several drawbacks: we are forced to look for these checks with our eyes and, being in another class, we cannot quickly assess the presence / absence of such checks. Also, without an explicit contract, we will have to remember that the class code has the necessary checks for incoming arguments and we don’t need to worry about them. Another factor: these checks are always performed, both in the development mode and in the combat mode of the application, which slightly affects the speed of the application in a negative direction.

    In terms of the implementation of preconditions, in PHP there is a special construction for checking claims - assert () . Its great advantage is that checks can be turned off in combat mode, replacing the entire command code with a single NOP . Let's look at how to describe a precondition using this construct:

    class BankAccount
    {
        protected $balance = 0.0;
        /**
         * Deposits fixed amount of money to the account
         *
         * @param float $amount
         */
        public function deposit($amount)
        {
            assert('$amount>0 && is_numeric($amount); /* Invalid amount of money /*');
            $this->balance += $amount;
        }
    }
    

    I want to draw attention to the fact that preconditions within the framework of contracts are used to check the program operation logic and are not responsible for the validity of the parameters transmitted from the client. Contracts are only responsible for the interaction within the system itself. Therefore, user input should always be filtered with filters, since claims can be disabled.

    Postconditions

    The next category of contracts is postconditions . As the name suggests, this type of check is performed after the method body has been executed, but until the control returns to the calling code. For our method deposit from an example, we can form the following postcondition: the account balance after calling the method must equal the previous balance value plus the replenishment value. The only thing left is to describe all this in the form of a statement in the code. But here we are faced with the first disappointment: how to formulate this requirement in the code, because we will first change the balance in the body of the method itself, and then try to check the statement where the old balance value is needed. Cloning an object before executing the code and checking post-conditions can help here:

    class BankAccount
    {
        protected $balance = 0.0;
        /**
         * Deposits fixed amount of money to the account
         *
         * @param float $amount
         */
        public function deposit($amount)
        {
            $__old = clone $this;
            assert('$amount>0 && is_numeric($amount); /* Invalid amount of money /*');
            $this->balance += $amount;
            assert('$this->balance == $__old->balance+$amount; /* Contract violation /*');
        }
    }
    


    Another disappointment awaits us when describing the postconditions for methods that return a value:

    class BankAccount
    {
        protected $balance = 0.0;
        /**
         * Returns current balance
         */
        public function getBalance()
        {
            return $this->balance;
        }
    }
    

    How to describe the contract condition here that the method should return the current balance? Since the post-condition is satisfied after the body of the method, we will stumble upon returnearlier than our check will work. Therefore, you will have to change the method code to save the result in a variable $__resultand then compare it with $this->balance:

    class BankAccount
    {
        protected $balance = 0.0;
        /**
         * Returns current balance
         */
        public function getBalance()
        {
            $__result = $this->balance;
            assert('$__result == $this->balance; /* Contract violation /*');
            return $__result;
        }
    }
    


    And this is for a simple method, not to mention the case when the method is large and has several return points. As you might have guessed, at this stage, ideas about using contract programming in a PHP project quickly die, because the language does not support the necessary control structures. But there is a solution! And about it will be written below, have a little patience.

    Invariants

    It remains for us to consider another important type of contract: invariants. Invariants are special conditions that describe the integral state of an object. An important feature of invariants is that they are always checked after calling any public method in the class and after calling the constructor. Since the contract determines the state of the object, and public methods are the only way to change the state from the outside, we get the full specification of the object. For our example, a good invariant may be a condition: the account balance should never be less than zero. However, with invariants in PHP, the situation is even worse than with postconditions: there is no way to easily add verification to all public methods of a class, so that after calling any public method, you can check the necessary condition in the invariant. Also there is no way to access the previous state of the object$__oldand return result $__result. Without invariants, there are no contracts, so for a long time there were no tools and techniques for implementing this functionality.

    New opportunities


    Meet, PhpDeal - experimental DBC - framework for contract programming in the PHP .
    After the Go! Framework was developed AOP for aspect-oriented programming in PHP , my mind was spinning about automatic parameter validation, checking conditions, and much, much more. The trigger for creating a project for contract programming was a discussion on PHP.Internals . Surprisingly, with the help of AOP, the problem was solved in just a couple of actions: it was necessary to describe an aspect that would intercept the execution of methods marked with contract annotations and perform the necessary checks before or after the method was called.

    Let's take a look at how contracts can be used with this framework:

    use PhpDeal\Annotation as Contract;
    /**
     * Simple trade account class
     * @Contract\Invariant("$this->balance > 0")
     */
    class Account implements AccountContract
    {
        /**
         * Current balance
         *
         * @var float
         */
        protected $balance = 0.0;
        /**
         * Deposits fixed amount of money to the account
         *
         * @param float $amount
         *
         * @Contract\Verify("$amount>0 && is_numeric($amount)")
         * @Contract\Ensure("$this->balance == $__old->balance+$amount")
         */
        public function deposit($amount)
        {
            $this->balance += $amount;
        }
        /**
         * Returns current balance
         *
         * @Contract\Ensure("$__result == $this->balance")
         * @return float
         */
        public function getBalance()
        {
            return $this->balance;
        }
    }
    


    As you noticed, all contracts are described as annotations inside the dock blocks and contain the necessary conditions inside the annotation itself. No need to change the original executable code of the class, it remains as clean as the code without contracts.

    Preconditions are specified using annotations Verifyand determine those checks that will be performed at the time the method is called, but before the method body itself is executed. Preconditions work in the scope of the class method, therefore they have access to all properties, including private ones, and also have access to the method parameters.

    Postconditions are defined by an annotation that has a standard name Ensurein terms of contract programming. The code has a similar scope as the method itself; in addition, variables are available$__oldwith the state of the object before the method was executed, and a variable $__resultcontaining the value that was returned from this method.

    Thanks to the use of AOP, it became possible to implement even invariants - they are elegantly described in the form of annotations Invariantin the docking block of the class and behave similarly to the postconditions, but for all methods.

    While experimenting with code, I discovered an amazing similarity of contracts with interfaces in PHP. If the standard interface defines the requirements for the standard for interacting with the class, then the contracts allow you to describe the requirements for the state of the class instance. Applying the description of the contract in the interface, it is possible to describe the requirements for both interaction with the object and the state of the object, which will then be implemented in the class:

    use PhpDeal\Annotation as Contract;
    /**
     * Simple trade account contract
     */
    interface AccountContract
    {
        /**
         * Deposits fixed amount of money to the account
         *
         * @param float $amount
         *
         * @Contract\Verify("$amount>0 && is_numeric($amount)")
         * @Contract\Ensure("$this->balance == $__old->balance+$amount")
         */
        public function deposit($amount);
        /**
         * Returns current balance
         *
         * @Contract\Ensure("$__result == $this->balance")
         *
         * @return float
         */
        public function getBalance();
    }
    


    Then the fun part begins: when creating a class and defining the required method, any modern IDE transfers all annotations from the method description in the interface to the class itself. And this allows the PhpDeal engine to find them and provide automatic verification of contracts in each specific class that implements this interface. For those who want to feel everything with their own hands - you can download the project from the github, install all the dependencies using the composer, configure the local web server on this folder and then simply open the code from the demo folder in the browser

    Conclusion


    Contract programming in PHP is a completely new paradigm that can be used for defensive programming, to improve the quality of code and to ensure the readability of contracts, defined in the form of requirements and specifications. The big plus of this implementation is that the class code remains readable, the annotations themselves are read as documentation, and the fact that in combat mode the verification can be completely disabled and requires absolutely no time for additional unnecessary checks in the code. An interesting fact: the framework itself contains only a couple of annotations and one aspect class that connects these annotations with specific logic.

    Thank you for attention!

    Related links:
    1. Wikipedia - contract programming
    2. PhpDeal framework for contract programming in PHP
    3. Go framework! AOP for aspect-oriented programming in PHP

    Read Next