rfc:nullable-casting

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:nullable-casting [2019/03/17 15:17] – Revamp draft 0.2, add settype and example guilliamxavierrfc:nullable-casting [2019/04/21 09:03] (current) – Add Discussion section guilliamxavier
Line 4: Line 4:
   * Date: 2019-03-17   * Date: 2019-03-17
   * Author: David Rodrigues (david.proweb@gmail.com), Guilliam Xavier (guilliam.xavier@gmail.com)   * Author: David Rodrigues (david.proweb@gmail.com), Guilliam Xavier (guilliam.xavier@gmail.com)
-  * Status: Draft+  * Status: Under Discussion
   * First Published at: http://wiki.php.net/rfc/nullable-casting   * First Published at: http://wiki.php.net/rfc/nullable-casting
  
Line 12: Line 12:
  
 Due to the lack of support for nullable casting, it is necessary to write additional code to preserve a possible ''null'' value through the type conversion. This feature would also bring more consistency and completeness to the existing language. Due to the lack of support for nullable casting, it is necessary to write additional code to preserve a possible ''null'' value through the type conversion. This feature would also bring more consistency and completeness to the existing language.
- 
-===== Proposal ===== 
- 
-The proposal is to add support of nullable types to the current casting feature. Basically, ''(int)'' is the "plain" int cast, and ''(?int)'' will be a nullable int cast. Generally speaking, what changes is the possibility to use a leading question mark symbol (''?'') before the type of a cast, turning it into a nullable cast. 
- 
-The only difference of nullable casting compared to plain casting is that if the expression value is ''null'', it will be kept as ''null'' instead of being forced to the destination plain type: 
- 
-^                //type//: int    bool    float  ^  string  ^  array    object  ^ 
-^   ''(''//type//'')''null|  0      false  |  0.0    |  %%""%%  |  %%[]%%  |  %%{}%%  | 
-^  ''(?''//type//'')''null|  null  |  null    null    null    |  null    |  null    | 
- 
-**Notes:** 
-  * The ''(unset)'' cast will not be affected (see the "Unaffected PHP Functionality" section). 
-  * The PHP parser is not sensitive to spaces in e.g. "''( int )''" cast and "''? int''" type declaration, so e.g. "''( ? int )''" will be identical to "''(?int)''". 
-  * The PHP parser does not distinguish between ''(integer)'' and ''(int)'' casts, so ''(?integer)'' will be identical to ''(?int)''. Likewise for ''(?boolean)'' vs ''(?bool)'', ''(?double)'' or ''(?real)'' vs ''(?float)'', and ''(?binary)'' vs ''(?string)''. 
- 
-If the expression value is not ''null'', nullable casting will give the same result as plain casting: e.g. (?int)false will give 0, (?array)%%""%% will give %%[""]%%. 
- 
-==== Additional proposal for settype() ==== 
- 
-Additionally, it was requested on the mailing list to consider adding support of nullable types to the ''settype()'' function, e.g. <php>settype($variable, "?int")</php>, which here would be the same as <php>$variable = (?int)$variable;</php> and return <php>true</php> (but in general <php>"?int"</php> could be a dynamic string). 
- 
-In short, for a currently valid <php>$type</php> argument to <php>settype($variable, $type)</php>, it would enable to use <php>'?'.$type</php> to preserve nullability. 
- 
-=== "?null" === 
- 
-In <php>"?null"</php>, the "''?''" is redundant ("nullable null"), but it could happen in dynamic code, e.g. <php>settype($x, '?' . gettype($y))</php> when <php>$y</php> is <php>null</php>. 
- 
-Possible options: 
-  - Allow it as equivalent to plain <php>"null"</php> silently. 
-  - Allow it as equivalent to plain <php>"null"</php> but emit a specific Notice. 
-  - Disallow it and emit a specific Warning (like the existing "Cannot convert to resource type"). 
-  - Disallow it and emit the existing generic Warning "Invalid type". 
- 
-For demonstration, the current patch uses option 2. 
  
 ===== Motivating Example ===== ===== Motivating Example =====
  
-In strict type-checking mode (<php><?php declare(strict_types=1);</php>), given two functions like the following:+In strict type-checking mode (<php><?php declare(strict_types=1);</php>), given two functions like the following (disclaimer: dummy implementation):
  
 <PHP> <PHP>
Line 134: Line 99:
  
 but currently <php>$type</php> cannot contain a nullable type like <php>"?string"</php> ("Warning: settype(): Invalid type", <php>$x</php> not converted). but currently <php>$type</php> cannot contain a nullable type like <php>"?string"</php> ("Warning: settype(): Invalid type", <php>$x</php> not converted).
 +
 +===== Proposal =====
 +
 +The proposal is to add support of nullable types to the current casting feature. Basically, ''(int)'' is the "plain" int cast, and ''(?int)'' will be a nullable int cast. Generally speaking, what changes is the possibility to use a leading question mark symbol (''?'') before the type of a cast, turning it into a nullable cast.
 +
 +The only difference of nullable casting compared to plain casting is that if the expression value is ''null'', it will be kept as ''null'' instead of being forced to the destination plain type:
 +
 +^                //type//: int    bool    float  ^  string  ^  array    object  ^
 +^   ''(''//type//'')''null|  0      false  |  0.0    |  %%""%%  |  %%[]%%  |  %%{}%%  |
 +^  ''(?''//type//'')''null|  null  |  null    null    null    |  null    |  null    |
 +
 +**Notes:**
 +  * The ''(unset)'' cast will not be affected (see the "Unaffected PHP Functionality" section).
 +  * The PHP parser is not sensitive to spaces in e.g. "''( int )''" cast and "''? int''" type declaration, so e.g. "''( ? int )''" will be identical to "''(?int)''".
 +  * The PHP parser does not distinguish between ''(integer)'' and ''(int)'' casts, so ''(?integer)'' will be identical to ''(?int)''. Likewise for ''(?boolean)'' vs ''(?bool)'', ''(?double)'' or ''(?real)'' vs ''(?float)'', and ''(?binary)'' vs ''(?string)''.
 +
 +If the expression value is not ''null'', nullable casting will give the same result as plain casting: e.g. (?int)false will give 0, (?array)%%""%% will give %%[""]%%.
 +
 +==== Additional proposal for settype() ====
 +
 +Additionally, it was requested on the mailing list to consider adding support of nullable types to the ''settype()'' function, e.g. <php>settype($variable, "?int")</php>, which here would be the same as <php>$variable = (?int)$variable;</php> and return <php>true</php> (but in general <php>"?int"</php> could be a dynamic string).
 +
 +In short, for a currently valid <php>$type</php> argument to <php>settype($variable, $type)</php>, it would enable to use <php>'?'.$type</php> to preserve nullability.
 +
 +=== "?null" ===
 +
 +In <php>"?null"</php>, the "''?''" is redundant ("nullable null"), but it could happen in dynamic code, e.g. <php>settype($x, '?' . gettype($y))</php> when <php>$y</php> is <php>null</php>.
 +
 +Possible options:
 +  - Allow it as equivalent to plain <php>"null"</php> silently.
 +  - Allow it as equivalent to plain <php>"null"</php> but emit a specific Notice.
 +  - Disallow it and emit a specific Warning (like the existing "Cannot convert to resource type").
 +  - Disallow it and emit the existing generic Warning "Invalid type".
 +
 +For demonstration, the current patch uses option 2.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 164: Line 164:
 ===== Proposed Voting Choices ===== ===== Proposed Voting Choices =====
  
-  * **Accept nullable casting**: Simple vote (Yes / No), requiring a 2/3 majority to pass. +(Each child vote result will be considered only if its parent vote passes.)
-    * **Additionally accept nullable settype()**: Simple vote (Yes / No), requiring a 2/3 majority to pass. +
-      * **How to handle settype($x, "?null")**: Multi-options vote (Allow silently / Allow but Notice / Disallow with specific Warning / Disallow with the generic Warning), the option with more votes will win.+
  
-(Each secondary vote result will be considered only if its parent vote passes.)+  * **Accept nullable casting?**: Simple vote (Yes / No), requiring a 2/3 majority to pass. 
 +    * **Additionally accept nullable settype()?**: Simple vote (Yes / No), also requiring a 2/3 majority to pass. 
 +      * **How to handle settype($x, "?null")?**: Multi-options vote (Allow silently / Allow but Notice / Disallow with specific Warning / Disallow with the generic Warning), the option with more votes will win. 
 + 
 +(The voting period would be two weeks)
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
  
   * Working prototype: https://github.com/php/php-src/pull/3764   * Working prototype: https://github.com/php/php-src/pull/3764
 +
 +===== Discussion =====
 +
 +==== "Not 100% needed" ====
 +
 +Current alternatives:
 +  * Use a test (ternary conditional operator or ''if'' statement), possibly with a temporary variable
 +  * Write (and [auto]load) custom casting functions
 +  * Disable (i.e. do not enable) strict typing mode in the concerned file //(not strictly equivalent, e.g. for ''%%"foo"%%'' to ''int'')//
 +
 +==== "A cast where you can't be sure of what you'll get back" ====
 +
 +"I understand the use-case for when you want to pass something to a nullable parameter, but if you think about this cast in isolation, it hardly makes sense."
 +
 +==== "What about e.g. nullable_intval()?" ====
 +
 +But we're missing "arrayval()" and "objectval()"... And we might use short closure ''%%fn($x) => (?int)$x%%''
 +
 +==== Fallible Casting ====
 +
 +One might expect to also have e.g. ''%%(?int)"foo"%%'' and ''%%(?int)""%%'' give ''null'' rather than ''0'', ''(?string)[42]'' give ''null'' rather than ''%%"Array"%%''... and to be able to use ''(?int)$value ?? $default'', ''%%(?string)$_GET["input"] ?? ""%%''...
 +
 +==== Alternative syntax ====
 +
 +E.g. "''(null|int) $x''"
  
 ===== References ===== ===== References =====
  
-  * [[http://php.net/manual/en/language.types.type-juggling.php|PHP Manual: Type Juggling]] +  * PHP Manual: [[http://php.net/manual/en/language.types.type-juggling.php|Type Juggling]][[http://php.net/manual/en/function.settype.php|settype() function]] 
-  * [[http://php.net/manual/en/function.settype.php|PHP Manual: settype() function]] +  * PHP RFC: [[rfc:scalar_type_hints_v5|Scalar Type Declarations]][[rfc:nullable_types|Nullable Types]] 
-  * [[rfc:scalar_type_hints_v5|PHP RFC: Scalar Type Declarations]] +  * Initial idea and discussion: https://externals.io/message/102997 
-  * [[rfc:nullable_types|PHP RFC: Nullable Types]] +  * Annoucement and discussion: https://externals.io/message/105122
-  * Initial discussion: https://externals.io/message/102997+
rfc/nullable-casting.txt · Last modified: 2019/04/21 09:03 by guilliamxavier