rfc:scalar_type_hinting_with_cast
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:scalar_type_hinting_with_cast [2014/07/14 15:18] – NULL default values ajf | rfc:scalar_type_hinting_with_cast [2014/09/15 13:06] – Withdrew RFC ajf | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Request for Comments: Scalar Type Hinting With Casts ====== | ====== Request for Comments: Scalar Type Hinting With Casts ====== | ||
- | * Version: 0.1.5 | + | * Version: 0.1.9.1 |
- | * Date: 2012-07-03 (latest update 2014-07-14) | + | * Date: 2012-07-03 (reopened 2014-07-13, |
* Author: Anthony Ferrara < | * Author: Anthony Ferrara < | ||
* Contributors: | * Contributors: | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
Line 15: | Line 15: | ||
This RFC discusses a method of adding scalar type hints to PHP while attempting to embrace the dynamic nature of PHP variables. | This RFC discusses a method of adding scalar type hints to PHP while attempting to embrace the dynamic nature of PHP variables. | ||
- | For consistency, | + | For consistency, |
+ | |||
+ | === Rationale for this proposal compared to others === | ||
+ | |||
+ | Other options for scalar type hints have been proposed. The most obvious other two are strict type hinting, where no casting is done and if the value is the wrong type, it errors (much like the non-scalar type hints), and type casting, where values are simply casted using the implicit casting rules with no failure cases (but sometimes emitting notices). | ||
+ | |||
+ | The problems with strict type hinting are twofold. Firstly, PHP's types can change somewhat unpredictably. For example, dividing one integer by another may result in a float when the the divisor is not a factor, and existing code may be poorly written and not guaranteed to return the types you'd expect. Secondly, while PHP's non-scalar types have always been quite strict and not been juggled, PHP's scalar types are routinely casted implictly, juggled, and are expected to do so, because of PHP's designed-for-the-web nature where numeric values are likely to start life in string form (from //$_GET// etc.). To break from this convention would be rather " | ||
+ | |||
+ | The other main option, simply type casting, also has problems. This way, there is no real error prevention (E_NOTICE is not a safeguard, it is a message in a log file, and in production it is not even that) and unsafe casts can happen with data being lost. Being able to pass " | ||
+ | |||
+ | On the other hand, this RFC tries to strike a balance between strict typing and mere type casting. It casts, making sure PHP's type shifting won't break things and keeping with PHP's traditional type casting and juggling nature, which is useful when dealing with the web, but prevents conversions causing data loss, reducing the likelihood of causing bugs and making it more likely bugs will be caught. In this way we feel the RFC combines the best parts of both proposals, providing both validation and type conversion. | ||
===== Proposal ===== | ===== Proposal ===== | ||
Line 21: | Line 31: | ||
==== Engine Changes ==== | ==== Engine Changes ==== | ||
- | The current implementation introduces | + | The current implementation introduces |
If this causes a problem, it would be possible to revert to the previous implementation, | If this causes a problem, it would be possible to revert to the previous implementation, | ||
Line 27: | Line 37: | ||
==== Syntax ==== | ==== Syntax ==== | ||
- | Four new type hints are introduced with this patch: | + | Five new type hints are introduced with this patch: |
* //int// - Matching integers only | * //int// - Matching integers only | ||
* //float// - Matching floating point numbers | * //float// - Matching floating point numbers | ||
+ | * //numeric// - Matching integers and floating point numbers (to allow polymorphic functions dealing with numbers) | ||
* //bool// - Matching boolean parameters only | * //bool// - Matching boolean parameters only | ||
* //string// - Matching strings only | * //string// - Matching strings only | ||
Line 36: | Line 47: | ||
==== Conversion Rules ==== | ==== Conversion Rules ==== | ||
- | Conversion is allowed only if data-loss does not happen. There are a few exceptions (objects using < | + | Conversion is allowed only if data-loss does not happen. There are a few exceptions (objects using < |
* //fail// indicates an E_RECOVERABLE_ERROR | * //fail// indicates an E_RECOVERABLE_ERROR | ||
Line 42: | Line 53: | ||
* //notice// indicates an E_NOTICE and a conversion | * //notice// indicates an E_NOTICE and a conversion | ||
- | ^ value ^ string ^ float ^ int ^ boolean‡^ array ^ | + | ^ value ^ string ^ float ^ int |
- | ^ true (boolean) | + | ^ true (boolean) |
- | ^ false (boolean) | + | ^ false (boolean) |
- | ^ 0 (integer) | + | ^ NULL (NULL) |
- | ^ 1 (integer) | + | ^ 0 (integer) |
- | ^ 12 (integer) | + | ^ 1 (integer) |
- | ^ 12 (double) | + | ^ 12 (integer) |
- | ^ 12.34 (double) | + | ^ 12 (double) |
- | ^ ' | + | ^ 12.34 (double) |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ ' | + | ^ ' |
- | ^ array () (array) | + | ^ ' |
- | ^ array (0 => 12) (array) | fail | fail | fail | fail | pass | | + | ^ array () (array) |
- | ^ NULL (NULL) | + | ^ array (0 => 12) (array) | fail | fail | fail | fail | fail |
- | ^ %%'' | + | ^ %%'' |
- | ^ 1 (resource) | + | ^ 1 (resource) |
- | ^ StdClass | + | ^ StdClass |
- | ^ implementing __toString | pass | fail* | fail* | fail† | + | ^ implementing __toString | pass | fail* | fail* |
< | < | ||
Line 77: | Line 88: | ||
==== Errors ==== | ==== Errors ==== | ||
- | If a provided hint does not match at all (" | + | If a provided hint does not match at all (" |
==== Defaults ==== | ==== Defaults ==== | ||
Line 85: | Line 96: | ||
This can lead to odd bugs, so in the future it would be good to validate the default in zend_compile.c (casting it where appropriate, | This can lead to odd bugs, so in the future it would be good to validate the default in zend_compile.c (casting it where appropriate, | ||
- | ===== NULL defaults (nullable hints) | + | === NULL defaults (nullable hints) === |
- | Normally, | + | The scalar |
- | + | ||
- | <code php> | + | |
- | function foo(int $a) { var_dump($a); | + | |
- | function bar(int $a = NULL) { var_dump($a); | + | |
- | + | ||
- | foo(NULL); // int(0) | + | |
- | bar(NULL); // NULL | + | |
- | </ | + | |
==== References ==== | ==== References ==== | ||
Line 108: | Line 111: | ||
* //int convert_to_{type}_safe_ex(zval < | * //int convert_to_{type}_safe_ex(zval < | ||
- | These functions pairs exist for //long//, //double//, // | + | These functions pairs exist for //long//, //double//, //string//, //boolean// and //numeric//. |
==== New Methods ==== | ==== New Methods ==== | ||
Line 118: | Line 121: | ||
* // | * // | ||
* // | * // | ||
+ | * // | ||
==== Patch ==== | ==== Patch ==== | ||
- | The modifications necessary to implement this feature exist on the [[https:// | + | The modifications necessary to implement this feature exist on the [[https:// |
===== Possible Changes ===== | ===== Possible Changes ===== | ||
+ | |||
+ | For points I'm unsure on, this section lists possible future changes to the RFC. | ||
==== Float to Int Casting Rules ==== | ==== Float to Int Casting Rules ==== | ||
Line 149: | Line 155: | ||
One option is simply to forget about being lossless and make the bool type hint accept any value, meaning any truthy value or any falsey value would yield what is expected without error. This would ensure that if someone has passed in a non-boolean truthy/ | One option is simply to forget about being lossless and make the bool type hint accept any value, meaning any truthy value or any falsey value would yield what is expected without error. This would ensure that if someone has passed in a non-boolean truthy/ | ||
- | Another option is go completely strict and allow only boolean values, failing everything else. This would be unlike the int, float and string hints, which are flexible and cast, but would be more helpful for catching bugs. However, not casting at all isn’t very “PHP-like”, | + | Another option is go completely strict and allow only boolean values, failing everything else, which is what the RFC current proposes since v0.1.6. This would be unlike the int, float and string hints, which are flexible and cast, but would be more helpful for catching bugs. It's worth noting that unlike for numbers, which can be losslessly transformed between string, int and float without any information lost at all, a string value casted to a boolean then back to a string will not be the same as the original string value. There aren't any sensible completely lossless bidirectional casts for booleans where the result of casting from boolean would obviously be boolean. However, not casting at all isn’t very “PHP-like”, |
The final option this section considers is a limited set of values. TRUE, FALSE and NULL would be accepted, along with the integer and float values 1 and 0 (which are the int/float values TRUE and FALSE cast to, respectively), | The final option this section considers is a limited set of values. TRUE, FALSE and NULL would be accepted, along with the integer and float values 1 and 0 (which are the int/float values TRUE and FALSE cast to, respectively), | ||
Both the author of this RFC (Anthony) and the current maintainer (Andrea) are yet to settle on one specific option. | Both the author of this RFC (Anthony) and the current maintainer (Andrea) are yet to settle on one specific option. | ||
- | ==== Handling of " | + | ==== Handling of " |
This has been changed to E_RECOVERABLE_ERROR, | This has been changed to E_RECOVERABLE_ERROR, | ||
Line 174: | Line 180: | ||
foo(" | foo(" | ||
foo(" | foo(" | ||
+ | foo("" | ||
foo(999999999999999999999999999999999999); | foo(999999999999999999999999999999999999); | ||
+ | foo(' | ||
foo(1.5); // E_RECOVERABLE_ERROR | foo(1.5); // E_RECOVERABLE_ERROR | ||
foo(array()); | foo(array()); | ||
Line 193: | Line 201: | ||
foo(" | foo(" | ||
foo(" | foo(" | ||
+ | foo("" | ||
+ | foo(1.5); // float(1.5) | ||
+ | foo(array()); | ||
+ | foo(new StdClass); // E_RECOVERABLE_ERROR | ||
+ | ?> | ||
+ | </ | ||
+ | |||
+ | ==== Numeric Hints ==== | ||
+ | |||
+ | <file php numeric_hint.php> | ||
+ | <?php | ||
+ | function foo(numeric $a) { | ||
+ | var_dump($a); | ||
+ | } | ||
+ | foo(1); // int(1) | ||
+ | foo(" | ||
+ | foo(1.0); // float(1) | ||
+ | foo(" | ||
+ | foo(" | ||
+ | foo("" | ||
foo(1.5); // float(1.5) | foo(1.5); // float(1.5) | ||
foo(array()); | foo(array()); | ||
Line 211: | Line 239: | ||
foo(" | foo(" | ||
foo(" | foo(" | ||
+ | foo("" | ||
foo(1.5); // string " | foo(1.5); // string " | ||
foo(array()); | foo(array()); | ||
Line 224: | Line 253: | ||
var_dump($a); | var_dump($a); | ||
} | } | ||
- | foo(1); // bool(true) | + | foo(1); // E_RECOVERABLE_ERROR |
- | foo(" | + | foo(" |
- | foo(1.0); // bool(true) | + | foo(1.0); // E_RECOVERABLE_ERROR |
- | foo(0); // bool(false) | + | foo(0); // E_RECOVERABLE_ERROR |
- | foo(" | + | foo(" |
- | foo(" | + | foo(" |
- | foo(" | + | foo(" |
- | foo(1.5); // bool(true) | + | foo("" |
+ | foo(1.5); // E_RECOVERABLE_ERROR | ||
foo(array()); | foo(array()); | ||
foo(new StdClass); // E_RECOVERABLE_ERROR | foo(new StdClass); // E_RECOVERABLE_ERROR | ||
+ | foo(true); // bool(true) | ||
+ | foo(false); // bool(false) | ||
+ | foo(null); // E_RECOVERABLE_ERROR | ||
?> | ?> | ||
</ | </ | ||
+ | |||
+ | ===== Proposed Voting Choices ===== | ||
+ | |||
+ | As this is a language change, a 2/3 majority is required. Voting started 2014-09-14 and ends 2014-09-21. | ||
+ | |||
+ | It will be a straight Yes/No vote. | ||
===== More Information ===== | ===== More Information ===== | ||
Line 254: | Line 293: | ||
* 0.1.4 - Removed // | * 0.1.4 - Removed // | ||
* 0.1.5 - Note on NULL default values | * 0.1.5 - Note on NULL default values | ||
+ | * 0.1.6 - Booleans are now strict | ||
+ | * 0.1.7 - Types are now not nullable by default | ||
+ | * 0.1.8 - Added numeric typehint | ||
+ | * 0.1.8.1 - Overflow prevention for int hints | ||
+ | * 0.1.9 - Booleans not accepted for int, float, numeric or string | ||
+ | * 0.1.9.1 - Added "" |
rfc/scalar_type_hinting_with_cast.txt · Last modified: 2021/09/09 06:07 by heiglandreas