rfc:dbc2

This is an old revision of the document!


PHP RFC: Native DbC support as definition

Important Note

This RFC is an alternative approach to “Native DbC support” RFC. http://wiki.php.net/rfc/dbc

This RFC proposes PHP syntax extension for contracts declaration.

Introduction

Design by Contract (DbC) or Contract Programming is powerful program design concept based on Contracts that

  1. Define precondition contract for methods/functions. i.e. Define Parameter value specifications/conditions.
  2. Define postcondition contract for methods/functions. i.e. Define Return value specifications/conditions.
  3. Define invariant contract for methods/functions. i.e. Define class state must be true always.

These contracts are evaluated at run-time, usually durunf development and testing only. It should be possible to switch off contracts validation for production.

DbC Changes the Way Program is Designed/Developed/Tested

CalleR and CalleE responsibility and validation:
  • With modularized design without DbC, the more code is consolidated, the better code is.
    Therefore, parameter validity is checked deep into the code. Parameter validity check is calleE responsibility.
  • With DbC, parameter checks are done as precondition. Passing valid parameter to a function is calleR responsibility.
Exit value validation:
  • Without native DbC, return value validity cannot be checked without scattered assert() for every place function is used.
    i.e. Programmers has to write “$ret = func(); assert($ret > 0);” everywhere func() is used or code must have assert() for each func().
  • With native DbC, return value validity is checked as postcondition. It does not require scattered assert().
Class state validation:
  • With native DbC, invariant condition may be defined. Invariant allows programmers consolidate class state which must be TRUE for always.
    e.g. $account_balance >= 0

precondition check can be done with assert(), but other feature requires native DbC.

DbC reverses the responsibility caller and callee, ensures parameters, return value and class state validity without performance penalty. This helps PHP to achieve more efficient development by contract errors, faster execution for production.

DbC encourages users to control inputs/output/class state precisely. This helps application efficient/precise development. Languages designed lately have native DbC support. e.g. D language. Almost all languages have some kind of DbC supports today.

Readability of Code / Testing Code

Since contracts are declared together with function/method definition, it's much easier to find function/method spec compared to conditions written in UnitTests. It also enables contract check for running applications operated by humans.

DbC is NOT a complement of UnitTest, but it provide additional way of testing programs. Programs should be tested by UnitTest with and without DbC. With this procedure, UnitTest condition coverage is improved and/or simplified.

General Concern of Design by Contract

Some may concern that lack of checks under production environment. However, all inputs to programs should validate input parameter validity right after input parameters are passed to program.

This is basic principle of secure programs. Users should not DbC everywhere. User should have proper defense in depth as security measure. Security standard like “ISO 27000” or security guideline like “SANS TOP 25 Monster Mitigations” suggests to have proper output controls, as well as input controls. This applies with or without DbC. DbC encourages proper input/output controls compare to program design without DbC.

Proposal

We are going to introduce native PHP syntax for contracts declaration for functions and methods

  • “require”( precondition-expression [,'Error msg'])
  • “return”( return_variable, postcondition-expression [,'Error msg'])

These contracts should be defined after the function declaration, but before the function body. It's possible to have few requre and return constraints. The expressions are just a regular PHP expressions. They should validate input arguments (accessed through their names) or return value (accessed through special name $ret) and shouldn't make any side effects. Pre and post-conditions can't be defined for abstract methods and methods defined in interfaces.

function add(int $a, int $b) : int
	require($a > 0)
	require($b > 0)
	return($ret, $ret > 0, "something wrong")
{
	return $a + $b;
}

this code is going to be evaluated as

function add(int $a, int $b) : int
{
	assert($a > 0);
	assert($b > 0);
	$ret = $a + $b;
	assert($ret > 0, "something wrong");
	return $ret;
}

Invariant contracts for objects may be declared using requre inside a slass declaration statement. There are possible to have few invaiant constraints. They may access object or static properties through $this and self variables. Invariant contracts may be defined for classes, abstract classes and traits, but not for interfaces.

class Accumulator {
	private $sum = 0;
	
	function add(int $a)
		require($a > 0)
	{
		$this->sum += $a;
	}

	requre($this->sum > 0, "overflow detected");
}

this code is going to be evaluated as

class Accumulator {
	private $sum = 0;
	
	function add(int $a)
	{
		assert($a > 0);
		assert($this->sum > 0, "overflow detected");
		$this->sum += $a;
		assert($this->sum > 0, "overflow detected");
	}
}

Invariant contracts are not validated when public object properties are changed not from the class scope.

Contracts Inheritance Rules

TODO

Execution Control

A new “dbc” INI switch is going to be introduced. It may get the following values:

  • dbc=on - generate code for contracts and check them at run-time. Program, at any time, may change this settion to dbc=off through ini_set().
  • dbc=off - generate code for contracts but don't check them at run-time. Program, at any time, may change this settion to dbc=on through ini_set().
  • dbc=zero_cost - don't generate code for contracts. This may be set only in php.ini and can't be changed through ini_set().
The default value if "off".

Contracts Evaluation Order

If “dbc” is set to “on”, the order of contracts validation is the following:

  1. all require() contracts (preconditions) defined for this methods (in defined order)
  2. all require() contracts (invariants) defined for this class (in defined oredrer)
  3. method body
  4. all require() contracts (invariants) defined for this class (in defined oredrer)
  5. all return() contracts (postconditions) defined for this methods (in defined order)

Invariant and Special Methods

  • __constructs()/__wakeup()/__set_state() will NOT execute invariant before method body.
  • __destruct()/__sleep() will NOT execute invariant after method body.

Class Inheritance

  • When parent class methods are called, DbC conditions are executed
  • Special methods execution exception is the same

Static Call

  • Only pre/post contracts are executed

Interface

  • Cannot define DbC contracts.

Backward Incompatible Changes

None

  • No additional keyword

Proposed PHP Version(s)

  • PHP7

RFC Impact

To SAPIs

None

To Existing Extensions

None

To Opcache

Opcache will have to be extended to support contracts and store them in shared memory.

New Constants

None

php.ini Defaults

dbc=Off for all (INI_ALL)

  • hardcoded default values
  • php.ini-development values
  • php.ini-production values

Open Issues

  • Contracts inheritance rules
  • Consider introduction of static require() as class invariant for static methods
  • Need to discuss syntax
  • How to manage votes for 2 RFCs

Unaffected PHP Functionality

This RFC does not affect any existing features

Future Scope

Documentation systems may adopt native DbC syntax for documentation purpose.

Vote

This project requires a 2/3 majority

  • Option would be YES/NO only

Patches and Tests

Links to any external patches and tests go here.

If there is no patch, make it clear who will create a patch, or whether a volunteer to help with implementation is needed.

Make it clear if the patch is intended to be the final patch, or is just a prototype.

Implementation

After the project is implemented, this section should contain

  1. the version(s) it was merged to
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/dbc2.1423568951.txt.gz · Last modified: 2017/09/22 13:28 (external edit)