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
rfc:dbc2 [2015/02/10 06:17] yohgakirfc:dbc2 [2018/03/01 23:19] (current) – Typo "Under Discussion" carusogabriel
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: Under Discussion
   * 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 part of "Native DbC support" RFC. +This RFC is part of "Design by Contract Introduction" RFC 
-http://wiki.php.net/rfc/dbc+  * https://wiki.php.net/rfc/introduce_design_by_contract
  
-There are many way to achieve DbC. This RFC proposes DbC as function/method definition.+This RFC is an alternative approach to "Native DbC support" RFC. 
 +  * http://wiki.php.net/rfc/dbc
  
-===== Introduction =====+Contracts can be defined without modification to the language, however, the D documentation calls the resulting implementation "clumsy and inconsistent".
  
-Design by Contract (DbC) or Contract Programming is powerful program design concept based on **Contracts** that+Other advantages of a native implementation, also taken from the D manual:
  
-  - Define **precondition** contract for methods/functions. i.e. Define Parameter value specifications/conditions. +  * a consistent look and feel for the contracts 
-  - Define **postcondition** contract for methods/functions. i.e. Define Return value specifications/conditions. +  * tool support 
-  - Define **invariant** contract for methods/functions. i.e. Define class state must be true always.+  compiler optimizations 
 +  easier management and enforcement of contracts 
 +  * handling of contract inheritance
  
-These contracts are evaluated development time only. Therefore, there is **no performance penalty** with DbC.+All of the above applies to PHP.
  
-  * http://dlang.org/contracts.html +===== Introduction =====
-  * http://en.wikipedia.org/wiki/Design_by_contract+
  
-====DbC Changes the Way Program is Designed/Developed/Tested====+The D manual contains the following succinct definition of contracts:
  
-==CalleR and CalleE responsibility and validation:== +> 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 programmoving it from the documentation to the code itselfAnd as every programmer knows, documentation tends to be incomplete, out of date, wrong, or non-existent. Moving the contracts into the code makes them verifiable against the program.
-  * With modularized design without DbC, the more code is consolidated, the better code is\\ Thereforeparameter 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:== +This should be easily comprehensible, it further validates the argument that the best implementation is a native oneso that the programmer really can "move the specification from the documentation to the code".
-  * Without native DbCreturn 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. +
-  * With native DbC, return value validity is checked as **postcondition**. It does not require scattered assert().+
  
-==Class state validation:== +===== Proposal =====
-  * 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.+Support for the following contracts will be introduced:
  
-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 errorsfaster execution for production.+  * precondition       "require"( **precondition-expression** [,'Error msg']) 
 +  * postcondition      "return"( **return_value**, **postcondition-expression** [,'Error msg']) 
 +  * invariant          "require"( **invariant-expression** [,'Error msg'])
  
-With proper use of DbC concept, it is known **programs to be more robust, secure and faster**. Languages designed lately have native DbC support. e.g. D language. Almost all languages have some kind of DbC supports today. +===== Pre and Post Condition =====
  
-====Readability of Code / Testing Code====+These contracts should be defined after the function declaration, but before the function body.
  
-Since contracts are written in function/method definitionit's much easier to find function/method spec compared to conditions written in UnitTestsIt also enables contract check for running applications operated by humans.+Multiple precondition and postcondition contracts may be used. The expressions are just a regular PHP expressions. They are evaluated in the function scopeand can access arguments and other scope variables, and return value (via a reserved name)Pre and post-conditions can't be defined for abstract methods and methods defined in interfaces.
  
-DbC is __NOT__ complement of UnitTestbut it provide additional way of testing programs. Programs should be tested by UnitTest with and without DbC. With this procedureUnitTest condition coverage is improved and/or simplified.+<code> 
 +function add(int $a, int $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
  
-====General Concern of Design by Contract====+<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>
  
-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. +===== Invariant =====
  
-This is basic principle of secure programsUsers should not DbC everywhereUser should have proper defense in depth as security measure. Security standard like "ISO 27000" or security guideline like "[[http://cwe.mitre.org/top25/#Mitigations|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.**+Invariant contracts are declared using **require** inside class bodyMultiple invariant contracts may be usedThey may access object or static properties through **$this** and **self**. Invariant contracts may be used for classes, abstract classes and traits, but not for interfaces.
  
 +<code>
 +class Accumulator {
 + private $sum = 0;
 +
 + function add(int $a)
 + require($a > 0)
 + {
 + $this->sum += $a;
 + }
  
-===== Proposal =====+ require($this->sum > 0, "overflow detected"); 
 +
 +</code>
  
-Introduce native contracts "require"( **precondition** ), "return"( **postcondition** ) definition to function/method and special "<nowiki>__invaliant()</nowiki>" method to class.+this code is going to be evaluated as
  
-While new usage of requirereturn may seem strange at firstusers can use familiar PHP syntax for DbC definitions.+<code> 
 +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"); 
 +
 +
 +</code>
  
-  require(assert-expr [,'Error msg']); Precondition in function/method and invariant in class +**Invariant contracts are not evaluated when object properties are changed from outside the class scope.**
-  return(assert-expr [,'Error msg']); Postcondition in function/method+
  
-**require**, **return** behave just like assert(). +====Contracts Inheritance Rules====
  
 +Contracts are constant, this has the following implications:
  
-====Example==== +  * a derived class must satisfy invariant contracts of it's parent 
-<code php> +  * a derived class overriding a method must satisfy the pre and post condition contracts of it's prototype.
-class Product { +
-  protected $inventory; +
-  require($this->$inventory >= 0); // Invariant - Should be always TRUE+
  
-  // User is supposed to check inventory first +Thus, given the following code:
-  function haveEnoughInventory($quantity)  +
-    require($quantity > 0)  // Precondition +
-  { +
-    return (bool) ($this->inventory - $quantity) >= 0; +
-  }+
  
-  // Sell inventory +<code> 
-  function sell($quantity)  +class Child 
-    require($quantity > 0) // Precondition +    require ($this->age < 18); 
-    return($> >= 0) // Postcondition. $> is return value +     
-  { +    public function someMethod($input)  
-    return $this>inventory - $quantity; +        require(somethingAbout($input)) { 
-  } +        /* ... */
-+
-</code> +
- +
- +
-====Longer Class Definition Example==== +
-<code php+
-class MySession2 extends SessionHandler { +
-    public $path; +
-    require(is_string($this->path)) && !empty($this->path)); +
- +
-    public function open($path, $name+
-        require(strlen($path> 3 && strlen($name> 3); +
-        return(is_bool($>)); +
-    +
-        if (!$path) { +
-            $path = sys_get_temp_dir(); +
-        } +
-        $this->path = $path '/u_sess_' . $name; +
-        return true;+
     }     }
  
-    public function close() +    /* ... */ 
-        return(is_bool($>)) +}
-    { +
-        return true; +
-    }+
  
-    public function read($id)  +class Monster extends Child { 
-        return(is_string($>) && strlen($id) > 10) +    require ($this->odour == OBNOXIOUS);
-    +
-        return @file_get_contents($this->path . $id); +
-    }+
  
-    public function write($id, $data)  +    public function someMethod($input)  
-        require(strlen($id> 10 && strlen($data> 10) +        require(somethingElseAbout($input)) { 
-        return(is_bool($>)); +        /* ... */
-    +
-        return file_put_contents($this->path $id, $data);+
     }     }
  
-    public function destroy($id)  +    /* ... */
-        require(strlen($id) > 10) +
-        return(is_bool($>)) +
-    { +
-        @unlink($this->path . $id); +
-    } +
- +
-    public function gc($maxlifetime)  +
-        require($maxfiletime > 60) +
-        return($> > -1) // GC supposed to return number of deleted recordsCode is wrong +
-    { +
-        foreach (glob($this->path '*') as $filename) { +
-            if (filemtime($filename) + $maxlifetime < time()) { +
-                @unlink($filename); +
-            } +
-        } +
-        return true; +
-    }+
 } }
 </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 177: Line 176:
  
   * No additional keyword   * No additional keyword
-  * __invariant() is a reserved method name 
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
Line 191: Line 189:
  
 ==== 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 199: Line 195:
  
 ==== php.ini Defaults ==== ==== php.ini Defaults ====
- +dbc=Off for all (INI_ALL)
-dbc=Off for all+
  
   * hardcoded default values   * hardcoded default values
Line 207: Line 202:
  
 ===== 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 =====
rfc/dbc2.1423549068.txt.gz · Last modified: 2017/09/22 13:28 (external edit)