This is an old revision of the document!
PHP RFC: Type parsing modifications
- Version: 0.2
- Date: 2015-02-19
- Author: François Laupretre, francois@php.net
- Status: Draft
- First Published at: https://wiki.php.net/rfc/zpp-conversion-rules
Introduction
This RFC proposes changes in the mechanism used to parse internal function arguments.
The main change proposed here is a modification of the ruleset used to filter and implicitly convert input arguments to internal functions.
Then, minor additions to be used by the userland type hinting system are proposed.
History
The changes proposed here are based on concerns that arose during the discussion about scalar type hints.
While the discussion turned to an opposition between 'weak' and 'strict' mode proponents, each camp showing use cases to prove it was right, we decided to explore another way : find a single-mode compromise that would satisfy both camps.
It was soon clear that the strongest argument of strict-typing proponents was that 'weak' mode was way too permissive, allowing, for instance, true or '7 years' as integer value.
As the 'weak' mode of the proposed type hinting mechanism is aligned on the behavior of the ZPP layer, and as everyone seemed to agree it should remain so, we decide to propose a new more restrictive ruleset to be implemented in ZPP and zend_parse_parameters().
So, this RFC presents a new ruleset for parameter parsing and implicit conversions.
Note: In the rest of the document, 'ZPP' means 'Z_PARAM macros and zend_parse_parameters()' as, whatever the final implementation, both mechanisms must remain in sync.
Proposal
Current ruleset
The ZPP ruleset authorizes implicit conversions for IS_NULL, IS_FALSE, IS_TRUE, IS_LONG, IS_DOUBLE, and IS_STRING zval types only.
Actually, it also implement a mechanism for (object -> string) but, as this one will remain unchanged, it will be ignored here.
The following table shows the current rules used to accept and convert an input zval through the ZPP layer :
Zval type | ||||||||
---|---|---|---|---|---|---|---|---|
ZPP type | IS_NULL | IS_FALSE | IS_TRUE | IS_LONG | IS_DOUBLE | IS_STRING | ||
bool | Yes (-> false) | <native> | <native> | Yes (1) | Yes (1) | Yes (2) | ||
long | Yes (-> 0) | Yes (-> 0) | Yes (-> 1) | <native> | Yes | (3) | ||
double | Yes (-> 0.0) | Yes (-> 0.0) | Yes (-> 1.0) | Yes | <native> | (4) | ||
string | Yes (-> “”) | Yes (-> “”) | Yes (-> “1”) | Yes | Yes | <native> |
- (1) false if null, true if non null
- (2) “” and “0” -> false, other values -> true
- (3) Run string through is_numeric_str_function() and detect error. If double returned, convert it to long
- (4) Run string through is_numeric_str_function() and detect error. If int returned, convert it to double
The conversion of IS_STRING to int/float is done through _is_numeric_string_ex(). Among others, this function has the following behavior :
- Stop conversion at first non digit character with no error (this is what authorizes '7 years' as integer).
Proposed changes
- Disable IS_FALSE/IS_NULL to anything but bool (native).
- IS_STRING -> bool: Convert every string that would be converted to 0 or 0.0 to false, instead of just “0”.
- Disable IS_NULL conversion to every ZPP type. It causes IS_NULL to be systematically rejected by every Z_PARAM macro except Z_PARAM_ZVAL.
- Disable (IS_DOUBLE -> long) if the fractional part of the input value is non null.
- When converting from IS_STRING to long, reject conversion if string contains the representation of a number with a non null fractional part.
In _is_numeric_string_ex() :
- Add a check for trailing characters : trailing blanks are accepted, the first non-blank character encountered raises an error (note that blanks are supported as leading or trailing chars only).
Future ruleset
This table shows a synthetic view of the resulting ruleset ('-' means 'Disabled'):
Zval type | ||||||||
---|---|---|---|---|---|---|---|---|
ZPP type | IS_NULL | IS_FALSE | IS_TRUE | IS_LONG | IS_DOUBLE | IS_STRING | ||
bool | - | <native> | <native> | Yes (1) | Yes (1) | Yes (5) | ||
long | - | - | - | <native> | (2) | (3) | ||
double | - | - | - | Yes | <native> | (4) | ||
string | - | - | - | Yes | Yes | <native> |
- (1) false if null, true if non null
- (2) error if fractional part is non null
- (3) Run string through is_numeric_str_function() and detect error. If double with null fractional part returned, convert to long, else error
- (4) Run string through is_numeric_str_function() and detect error. If int returned, convert to double
- (5) If string is numeric and contains a representation of a null number (anything that would convert to 0 or 0.0), or if string is empty, -> false, otherwise -> true.
and the new behavior of _is_numeric_string_ex() :
- Ignore trailing blanks
- Error on any non-blank trailing char
Other changes
Add a check-only mode in ZPP
TODO
Implement PHP 5 optional compatibility
TODO
Backward Incompatible Changes
Every restriction of the ruleset causes a BC break, except in PHP 5 compatibility mode.
Proposed PHP Version(s)
7.0.
RFC Impact
Every internal functions are potentially impacted.
Any code converting a string to a number is potentially impacted. If this cause an unacceptable BC break, we'll create a private copy of this function specific for argument parsing. As long as it is not needed, it is better if every string to numeric conversion uses the same code.
To Opcache
None
New Constants
None
php.ini Defaults
Name-to-define : Allows to raise newly-introduced restrictions as E_DEPRECATED instead of E_RECOVERABLE_ERROR.
Default value: Off
Open Issues
Coming soon...
Unaffected PHP Functionality
Future Scope
The set of supported numeric strings can be extended.
String to numeric conversion can be made smarter and accept more.
Proposed Voting Choices
Requires a 2/3 majority.
Vote will be a single-choice yes/no vote.
Voting date is not planned yet.
Patches and Tests
Work in progress.
As soon as patch is available, extensive testing must be performed to evaluate overall BC breaks.
Implementation
After the project is implemented, this section should contain
- the version(s) it was merged to
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
References
Rejected Features
Keep this updated with features that were discussed on the mail lists.