rfc:dbc2

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
rfc:dbc2 [2015/02/09 22:29] yohgakirfc:dbc2 [2015/02/14 23:14] – fixed typo during review marcio
Line 2: Line 2:
   * Version: 0.1   * Version: 0.1
   * Date: 2015-02-10   * Date: 2015-02-10
-  * Author: Yasuo Ohgaki <yohgaki@ohgaki.net>+  * Author: Yasuo Ohgaki <yohgaki@ohgaki.net>, Dmitry Stogov <dmitry@zend.com>, Joe Watkins <pthreads@pthreads.org>
   * Status: Draft   * Status: Draft
   * First Published at: http://wiki.php.net/rfc/dbc2   * First Published at: http://wiki.php.net/rfc/dbc2
  
-===== Important Note====+===== Important Note =====
  
-This RFC is a part of "Native DbC support" RFC.+This RFC is an alternative approach to "Native DbC support" RFC.
 http://wiki.php.net/rfc/dbc http://wiki.php.net/rfc/dbc
  
-There are many way to achieve DbCThis RFC proposes DbC as function/method definition.+Contracts can be defined without modification to the language, however, the D documentation calls the resulting implementation "clumsy and inconsistent". 
 + 
 +Other advantages of a native implementation, also taken from the D manual: 
 + 
 +  * a consistent look and feel for the contracts 
 +  * tool support 
 +  * compiler optimizations 
 +  * easier management and enforcement of contracts 
 +  * handling of contract inheritance 
 + 
 +All of the above applies to PHP.
  
 ===== Introduction ===== ===== Introduction =====
  
-Design by Contract (DbC) or Contract Programming is powerful program design concept based on **Contracts** that+The D manual contains the following succinct definition of contracts:
  
-  Define **precondition** contract for methods/functionsi.e. Define Parameter value specifications/conditions. +> The idea of a contract is simple it's just an expression that must evaluate to true. If it does not, the contract is broken, and by definition, the program has a bug in itContracts form part of the specification for a program, moving it from the documentation to the code itselfAnd as every programmer knows, documentation tends to be incomplete, out of date, wrong, or non-existentMoving the contracts into the code makes them verifiable against the program.
-  - Define **postcondition** contract for methods/functionsi.e. Define Return value specifications/conditions. +
-  Define **invariant** contract for methods/functionsi.e. Define class state must be true always.+
  
-These contracts are evaluated development time only. Thereforethere is **no performance penalty** with DbC.+This should be easily comprehensibleit further validates the argument that the best implementation is a native one, so that the programmer really can "move the specification from the documentation to the code".
  
-====DbC changes the way program is designed====+===== Proposal =====
  
-  * 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. +Support for the following contracts will be introduced:
-  * With DbC, parameter checks are done as **precondition**. Passing valid parameter to a function is __calleR__ responsibility.+
  
-  * 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 func() must have assert() for every return. +  * precondition       "require"**precondition-expression** [,'Error msg']
-  * With native DbC, return value validity is checked as **postcondition** definition. It does not require scattered assert().+  * postcondition      "return"( **return_value**, **postcondition-expression** [,'Error msg']) 
 +  * invariant          "require"**invariant-expression** [,'Error msg'])
  
-  * 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+===== Pre and Post Condition =====
  
-**precondition** check can be done with assert(), but other feature requires native DbC.+These contracts should be defined after the function declaration, but before the function body.
  
-DbC reverses the responsibility caller and callee, ensures parameters, return value and class state validity without performance penaltyThis helps PHP to achieve more efficient development by contract errors, faster execution for production.+Multiple precondition and postcondition contracts may be used. The expressions are just a regular PHP expressions. They are evaluated in the function scope, and can access arguments and other scope variablesand return value (via a reserved name)Pre and post-conditions can't be defined for abstract methods and methods defined in interfaces.
  
-With proper use of DbC conceptit is known **programs to be more robust/secure and faster**. Languages design lately have native DbC support. e.g. D language. Almost all languages have some kind of DbC supports today. +<code> 
 +function add(int $aint $b) : int 
 + require($a > 0) 
 + require($b > 0) 
 + return($ret, $ret > 0, "something wrong"
 +
 + return $a + $b; 
 +
 +</code>
  
 +this code is going to be evaluated as
  
-===== Proposal =====+<code> 
 +function add(int $a, int $b) : int 
 +
 + assert($a > 0); 
 + assert($b > 0); 
 + $ret $a + $b; 
 + assert($ret > 0, "something wrong"); 
 + return $ret; 
 +
 +</code>
  
-Introduce native contracts "require"( **precondition** ), "return"( **postcondition** ) definition to function/method and special "<nowiki>__invaliant()</nowiki>" method to class.+===== Invariant =====
  
-**require****return** behave just like assert()(requirereturn accepts message. i.e. require($a > 0'$a must be positive') )+Invariant contracts are declared using **require** inside class body. Multiple invariant contracts may be used. They may access object or static properties through **$this** and **self**. Invariant contracts may be used for classesabstract classes and traitsbut not for interfaces.
  
-====Example without **invariant**==== +<code> 
-<code php+class Accumulator 
-class Product + private $sum = 0; 
-  protected $inventory;+  
 + function add(int $a) 
 + require($a > 0) 
 +
 + $this->sum += $a; 
 +
 + 
 + require($this->sum > 0, "overflow detected"); 
 +
 +</code>
  
-  // User is supposed to check inventory first +this code is going to be evaluated as
-  function haveEnoughInventory($quantity)  +
-    require($quantity > 0) +
-  { +
-    return (bool) ($this->inventory - $quantity) >= 0; +
-  }+
  
-  // Sell inventory +<code> 
-  function sell($quantity)  +class Accumulator { 
-    require($quantity > 0+ private $sum = 0; 
-    return($ret >0) // We have to think of how to specify $ret. Use block? return($ret) {assert($ret >0);} +  
-  { + function add(int $a
-    $ret = $this>amount - $quantity+
-    return $ret+ assert($> 0)
-  }+ assert($this->sum > 0, "overflow detected"); 
 + $this->sum += $a
 + assert($this->sum > 0, "overflow detected")
 + }
 } }
 </code> </code>
  
-====Example with **invariant**==== +**Invariant contracts are not evaluated when object properties are changed from outside the class scope.**
-<code php> +
-class Product { +
-  protected $inventory;+
  
-  // Invariants must be TRUE for this class always  - $inventory should not be negative +====Contracts Inheritance Rules====
-  function __invariant() { +
-    assert($this->inventory >0); +
-  }+
  
-  // User is supposed to check inventory first +Contracts are constant, this has the following implications:
-  function haveEnoughInventory($quantity)  +
-    require($quantity > 0) +
-  { +
-    return (bool) ($this->inventory - $quantity) >= 0; +
-  }+
  
-  // Sell inventory +  * a derived class must satisfy invariant contracts of it's parent 
-  function sell($quantity)  +  * a derived class overriding a method must satisfy the pre and post condition contracts of it's prototype. 
-    require($quantity > 0+ 
-  +Thus, given the following code: 
-    return $this>amount $quantity+ 
-  }+<code> 
 +class Child { 
 +    require ($this->age < 18); 
 +     
 +    public function someMethod($input)  
 +        require(somethingAbout($input)) { 
 +        /* ... */ 
 +    } 
 + 
 +    /* ... */ 
 +
 + 
 +class Monster extends Child 
 +    require ($this->odour == OBNOXIOUS)
 + 
 +    public function someMethod($input)  
 +        require(somethingElseAbout($input)) { 
 +        /* ... */ 
 +    } 
 + 
 +    /* ... */
 } }
 </code> </code>
 +
 +//Monster// must not break **any** contract in //Child//.
  
 ====Execution Control==== ====Execution Control====
  
-Introduce "dbc" INI switch, Off by default. No additional CLI option for DbC.+A new "dbc" INI switch is going to be introducedIt may get the following values:
  
-A method/function is executed as follows+  * 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().
  
-===Development mode: dbc=On ===+  The default value is "off".
  
-  - require() conditions +===Contracts Execution Order===
-  - __invariant() +
-  - method() +
-  - __invariant() +
-  - return() conditions+
  
-===Production mode: dbc=Off ===+If "dbc" is set to "on", the order of contracts validation is the following:
  
-  - method() +  - all require() contracts (precondition) defined for this function (and prototype where applicable) 
- +  - all require() contracts (invariant) for this class and parents 
 +  - method body 
 +  - all require() contracts (invariant) for this class and parents 
 +  - all return() contracts (postcondition) defined for this function (and prototype where applicable) 
 + 
 +**Invariant and Special Methods** 
 + 
 +  * <nowiki>__constructs()/__wakeup()/__set_state()</nowiki> will NOT execute invariant before method body. 
 +  * <nowiki>__destruct()/__sleep()</nowiki> will NOT execute invariant after method body. 
 + 
 +**Static Call** 
 + 
 +  * Only pre and post conditions are executed.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 119: Line 173:
  
   * No additional keyword   * No additional keyword
-  * __invariant() is a reserved method name 
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
Line 133: Line 186:
  
 ==== To Opcache ==== ==== To Opcache ====
- +Opcache will have to be extended to support contracts and store them in shared memory.
-Dmitry, could you write impact when discussion is finished? +
  
 ==== New Constants ==== ==== New Constants ====
Line 141: Line 192:
  
 ==== php.ini Defaults ==== ==== php.ini Defaults ====
- +dbc=Off for all (INI_ALL)
-dbc=Off for all+
  
   * hardcoded default values   * hardcoded default values
Line 149: Line 199:
  
 ===== Open Issues ===== ===== Open Issues =====
 +  * Contracts inheritance rules 
 +  * Consider introduction of **static require()** as class invariant for static methods
   * Need to discuss syntax   * Need to discuss syntax
   * How to manage votes for 2 RFCs   * How to manage votes for 2 RFCs
- 
  
 ===== Unaffected PHP Functionality ===== ===== Unaffected PHP Functionality =====
Line 162: Line 212:
 Documentation systems may adopt native DbC syntax for documentation purpose. Documentation systems may adopt native DbC syntax for documentation purpose.
  
-===== Proposed Voting Choices ===== +===== Vote ===== 
-Include these so readers know where you are heading and can discuss the proposed voting options.+This project requires a 2/3 majority 
 + 
 +  * Option would be YES/NO only
  
-State whether this project requires a 2/3 or 50%+1 majority (see [[voting]]) 
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
rfc/dbc2.txt · Last modified: 2018/03/01 23:19 by carusogabriel