rfc:typecheckingweak

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:typecheckingweak [2009/07/12 13:43] – Change to Type Enforcement, add feedback from Guillaume Rossolini zeevrfc:typecheckingweak [2017/09/22 13:28] (current) – external edit 127.0.0.1
Line 32: Line 32:
  
 <code php> <code php>
-function foo(int x) {} +function foo(int $x) {} 
-function bar(x, string y) {} +function bar($x, string $y) {} 
-function baz(int x, float y, string z) {} +function baz(int $x, float $y, string $z) {} 
-function foobar(int &x) {}+function foobar(int &$x) {}
 </code> </code>
  
Line 48: Line 48:
 bar('whatever', 17.5);  // argument will be converted to a string '17.5' before being passed to bar() bar('whatever', 17.5);  // argument will be converted to a string '17.5' before being passed to bar()
 foobar(17.5);           // will fail (scalar value cannot be passed by reference) foobar(17.5);           // will fail (scalar value cannot be passed by reference)
-$x=17.5;  foobar($x);   // $x will be converted to 17(int), and then passed to foobar()+$x=17.5;  foobar($x);   // $x will be converted to 17(int), and then passed to foobar(); $x remains 17(int) after the call to foobar()
 </code> </code>
  
Line 57: Line 57:
   - Emit an error or throw an exception.   - Emit an error or throw an exception.
  
-**Note:**  In step 2, if the argument is designated as a pass-by-reference argument - the conversion will apply to the variable being passed.  This is consistent with the expectation that arguments passed by reference may be modified by the function they're sent to. 
  
  
 ===== Conversion Logic ===== ===== Conversion Logic =====
  
-^ value                   ^ string       ^ float      ^ int       ^ numeric   ^ bool      +^ value                   ^ string       ^ float       ^ int        ^ numeric    ^ bool       
-^ true (boolean)          | //fail//     | 1.0        | 1         | 1         | //as-is// | +^ true (boolean)          | //fail//     | 1.0         | 1          | 1          | //as-is//  
-^ false (boolean)         | //fail//     | 0.0        | 0         | 0         | //as-is// | +^ false (boolean)         | //fail//     | 0.0         | 0          | 0          | //as-is//  
-^ 0 (integer)             | '0'          | 0.0        | //as-is// | //as-is// | false     +^ 0 (integer)             | '0'          | 0.0         | //as-is//  | //as-is//  | false      
-^ 1 (integer)             | '1'          | 1.0        | //as-is// | //as-is// | true      +^ 1 (integer)             | '1'          | 1.0         | //as-is//  | //as-is//  | true       
-^ 12 (integer)            | '12'         | 12.0       | //as-is// | //as-is// | true      +^ 12 (integer)            | '12'         | 12.0        | //as-is//  | //as-is//  | true       
-^ 12.0 (double)           | '12.0'       | //as-is//  | 12        | //as-is// | true      +^ 12.0 (double)           | '12.0'       | //as-is//   | 12         | //as-is//  | true       
-^ 12.34 (double)          | '12.34'      | //as-is//  | 12        | //as-is// | true      +^ 12.34 (double)          | '12.34'      | //as-is//   | 12         | //as-is//  | true       
-^ 'true' (string)         | //as-is//    | //fail//   | //fail//  | //fail//  | //fail//  +^ 'true' (string)         | //as-is//    | //fail//    | //fail//   | //fail//   | //fail//   
-^ 'false' (string)        | //as-is//    | //fail//   | //fail//  | //fail//  | //fail//  +^ 'false' (string)        | //as-is//    | //fail//    | //fail//   | //fail//   | //fail//   
-^ '0' (string)            | //as-is//    | 0.0        | 0         | 0         | false     +^ '0' (string)            | //as-is//    | 0.0         | 0          | 0          | false      
-^ '1' (string)            | //as-is//    | 1.0        | 1         | 1         | true      +^ '1' (string)            | //as-is//    | 1.0         | 1          | 1          | true       
-^ '12' (string)           | //as-is//    | 12.0       | 12        | 12        | true      +^ '12' (string)           | //as-is//    | 12.0        | 12         | 12         | true       | 
-^ '12abc' (string)        | //as-is//    | //fail//   | //fail//  | //fail//  | //fail//  +^ '0xA' (string)          | //as-is//    | 10.0        | 10         | 10         | true       
-^ '12.0' (string)         | //as-is//    | 12.0       | 12        | 12.0      | true      +^ '12abc' (string)        | //as-is//    | //fail//    | //fail//   | //fail//   | //fail//   
-^ '12.34' (string)        | //as-is//    | 12.34      | 12        | 12.34     | true      +^ '12.0' (string)         | //as-is//    | 12.0        | 12         | 12.0       | true       
-^ 'foo' (string)          | //as-is//    | //fail//   | //fail//  | //fail//  | //fail//  +^ '12.34' (string)        | //as-is//    | 12.34       | 12         | 12.34      | true       
-^ empty string (TBD)      | //as-is//    | //fail//   | //fail//  | //fail//  | //fail//  +^ 'foo' (string)          | //as-is//    | //fail//    | //fail//   | //fail//   | //fail//   
-^ array () (array)        | //fail//     | //fail//   | //fail//  | //fail//  | //fail//  +^ empty string (TBD)      | //as-is//    | //fail//    | //fail//   | //fail//   | //fail//   
-^ array (0 => 12) (array) | //fail//     | //fail//   | //fail//  | //fail//  | //fail//  +^ array () (array)        | //fail//     | //fail//    | //fail//   | //fail//   | //fail//   
-^ NULL (NULL)             | empty string | 0.0        | 0         | 0         | false     |+^ array (0 => 12) (array) | //fail//     | //fail//    | //fail//   | //fail//   | //fail//   
 +^ NULL (NULL)             | empty string | 0.0         | 0          | 0          | false      | 
 +^ object                  | //fail++//   | //fail++//  | //fail++// | //fail++// | //fail++// |
  
-//fail//  - designates failureeither emitting an error or throwing an exception+//as-is//  - designates that the value is passed as-iswithout conversion
  
-//as-is// - designates that the value is passed as-iswithout conversion+//fail//   - designates failure, either emitting an error or throwing an exception 
 + 
 +//fail++// failunless a matching conversion function exists (e.g. __toString()) - in which case it will be called and used 
 + 
 + 
 +**Note:**  'scalar' and 'array' type hints remain unchanged - an array typed argument will only accept arrays, and will otherwise fail;  A scalar typed argument will accept any kind of scalar argument, but will fail on objects and arrays.
  
 In a nutshell, the conversion logic is quite similar to the one employed by internal functions, with one key difference - it is designed to fail in case of a conversion that is unlikely to 'make sense' Specifically, it breaks away from PHP's internal function behavior in two key places: In a nutshell, the conversion logic is quite similar to the one employed by internal functions, with one key difference - it is designed to fail in case of a conversion that is unlikely to 'make sense' Specifically, it breaks away from PHP's internal function behavior in two key places:
Line 108: Line 114:
 ===== Comparison with Strict Typing ===== ===== Comparison with Strict Typing =====
  
-The main 'contender' to this RFC is the Strict Typing RFC.  Unlike Type Enforcement, Strict Typing is based on a strict comparison of the zval.type value.  As such, it introduces an entirely new semantics to PHP, especially around parameter passing.  Today, the zval.type is used only by a handful of functions (is_int() et al, gettype()), and the identity operator.  The former are much more rarely than their more 'lax' siblings (is_numeric()) which are typically more appropriate;  While the latter is typically used for specialized cases, e.g. when dealing with a function returning an integer, and having to tell boolean false apart.  It is therefore argued that extending a zval.type-based checks into parameter passing - a center-piece of the language - will inadvertently change the theme of the language, and the expected 'lax' type checking behavior expected from it today.+The main 'contender' to this RFC is the Strict Typing RFC.  Unlike Type Enforcement, Strict Typing is based on a strict comparison of the zval.type value.  As such, it introduces an entirely new semantics to PHP, especially around parameter passing.  Today, the zval.type is used only by a handful of functions (is_int() et al, gettype()), and the identity operator.  These functions are much more rarely used than their more 'lax' siblings (is_numeric()) which are typically more appropriate;  While the identity operator is typically used for specialized cases, e.g. when dealing with a function returning an integer, and having to tell boolean false apart.  It is therefore argued that extending a zval.type-based checks into parameter passing - a center-piece of the language - will inadvertently change the theme of the language, and the expected 'lax' type checking behavior expected from it today.
  
-In that context, it's important to mention that the two most common sources for data going into PHP - input data (_GET, _POST, etc.) and data coming from the database - are almost exclusively typed as strings.  While some do type conversion during the input sanitizing phase - that is not always the case, especially with data coming from the database.  Strict Typing is inherently incompatible with this concept, in the sense that it assumes the underlying data type (zval.type) is identical to the semantics of the value.  It does not come to say that the two cannot be used together - but they are a pretty bad fit.+In that context, it's important to mention that the two most common sources for data going into PHP - input data (_GET, _POST, etc.) and data coming from external resources (e.g. databases, config files, memcached, etc.) - are almost exclusively typed as strings.  While some do type conversion during the input sanitizing phase - that is not always the case, especially with data coming from the database.  Strict Typing is inherently incompatible with this concept, in the sense that it assumes the underlying data type (zval.type) is identical to the semantics of the value.  It does not come to say that the two cannot be used together - but they are a pretty bad fit.
  
 Furthermore - it is important to notice that the sole difference between Strict Typing and this proposed solution has to do with what happens **outside** the scope of the type-argumented function.  In other words - all the benefits for the function code itself (readability, code reduction, optimization, etc.) is 100.0% identical.  The semantics of what happens during the parameter-passing stage is what's different. Furthermore - it is important to notice that the sole difference between Strict Typing and this proposed solution has to do with what happens **outside** the scope of the type-argumented function.  In other words - all the benefits for the function code itself (readability, code reduction, optimization, etc.) is 100.0% identical.  The semantics of what happens during the parameter-passing stage is what's different.
Line 117: Line 123:
  
 <code php> <code php>
-function baz(int x, float y, string z) {}+function baz(int $x, float $y, string $z) {}
  
 // Strict type checking // Strict type checking
rfc/typecheckingweak.1247406204.txt.gz · Last modified: 2017/09/22 13:28 (external edit)