rfc:proper-range-semantics
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:proper-range-semantics [2023/03/22 15:22] – Created girgias | rfc:proper-range-semantics [2023/06/01 16:16] – In voting girgias | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Define proper semantics for range() function | ====== PHP RFC: Define proper semantics for range() function | ||
- | * Version: 0.1 | + | * Version: 0.3 |
* Date: 2023-03-13 | * Date: 2023-03-13 | ||
* Author: George Peter Banyard, < | * Author: George Peter Banyard, < | ||
- | * Status: | + | * Status: |
* Target Version: PHP 8.3 | * Target Version: PHP 8.3 | ||
* Implementation: | * Implementation: | ||
Line 11: | Line 11: | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | PHP's standard library implements the < | + | PHP's standard library implements the < |
+ | By default | ||
In principle, the < | In principle, the < | ||
Line 18: | Line 19: | ||
The current behaviour is quite complex, and it might be easier to just read the implementation, | The current behaviour is quite complex, and it might be easier to just read the implementation, | ||
- | First, check if the < | + | First, check if the < |
Then check the boundary arguments: | Then check the boundary arguments: | ||
* If both start and end values are strings with at least one byte (e.g. < | * If both start and end values are strings with at least one byte (e.g. < | ||
- | * If one of the inputs is a float numeric string, or the < | + | * If one of the inputs is a float numeric string, or the < |
- | * If one of the inputs is an integer numeric string: go to the generic handling branch | + | * If one of the inputs is an integer numeric string: go to the generic handling branch. |
* Otherwise: discard every byte after the first one and return an array of ASCII characters going from the start ASCII code point to the end ASCII code point. | * Otherwise: discard every byte after the first one and return an array of ASCII characters going from the start ASCII code point to the end ASCII code point. | ||
- | * If one of the start or end value is a float or the < | + | * If the start or end value is a float or the < |
- | * Otherwise (generic handling): cast start and end values to int and return an array of int. | + | * Otherwise (generic handling): cast start and end values to int and return an array of integers. |
| | ||
The generic case will accept //any// type. | The generic case will accept //any// type. | ||
Line 132: | Line 133: | ||
string(1) " | string(1) " | ||
} | } | ||
- | </ | ||
- | Example showing the ASCII code point range: | + | |
- | <PHP> | + | var_dump(range(' |
- | var_dump(range(' | + | array(3) { |
- | /* | + | |
- | array(8) { | + | |
[0]=> | [0]=> | ||
- | | + | |
[1]=> | [1]=> | ||
- | | + | |
[2]=> | [2]=> | ||
- | | + | |
- | [3]=> | + | |
- | string(1) " | + | |
- | [4]=> | + | |
- | string(1) " | + | |
- | [5]=> | + | |
- | string(1) " | + | |
- | [6]=> | + | |
- | string(1) " | + | |
- | [7]=> | + | |
- | string(1) " | + | |
} | } | ||
- | */ | ||
</ | </ | ||
Line 176: | Line 163: | ||
</ | </ | ||
- | Example showing how negative steps are multiplied by '' | + | Example showing how negative steps for increasing ranges |
<PHP> | <PHP> | ||
var_dump(range(0, | var_dump(range(0, | ||
Line 193: | Line 180: | ||
int(10) | int(10) | ||
} | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | Example showing the ASCII code point range: | ||
+ | <PHP> | ||
+ | var_dump( range(" | ||
+ | /* | ||
+ | array(15) { | ||
+ | [0]=> | ||
+ | string(1) " | ||
+ | [1]=> | ||
+ | string(1) """ | ||
+ | [2]=> | ||
+ | string(1) "#" | ||
+ | [3]=> | ||
+ | string(1) " | ||
+ | [4]=> | ||
+ | string(1) " | ||
+ | [5]=> | ||
+ | string(1) "&" | ||
+ | [6]=> | ||
+ | string(1) "'" | ||
+ | [7]=> | ||
+ | string(1) " | ||
+ | [8]=> | ||
+ | string(1) " | ||
+ | [9]=> | ||
+ | string(1) " | ||
+ | [10]=> | ||
+ | string(1) " | ||
+ | [11]=> | ||
+ | string(1) "," | ||
+ | [12]=> | ||
+ | string(1) " | ||
+ | [13]=> | ||
+ | string(1) " | ||
+ | [14]=> | ||
+ | string(1) "/" | ||
+ | } | ||
+ | */ | ||
+ | |||
+ | var_dump(range(' | ||
+ | /* | ||
+ | array(8) { | ||
+ | [0]=> | ||
+ | string(1) " | ||
+ | [1]=> | ||
+ | string(1) " | ||
+ | [2]=> | ||
+ | string(1) " | ||
+ | [3]=> | ||
+ | string(1) " | ||
+ | [4]=> | ||
+ | string(1) " | ||
+ | [5]=> | ||
+ | string(1) " | ||
+ | [6]=> | ||
+ | string(1) " | ||
+ | [7]=> | ||
+ | string(1) " | ||
+ | } | ||
+ | */ | ||
</ | </ | ||
Line 212: | Line 261: | ||
Examples with unexpected types: | Examples with unexpected types: | ||
<PHP> | <PHP> | ||
+ | /* null */ | ||
+ | var_dump(range(null, | ||
+ | array(3) { | ||
+ | [0]=> | ||
+ | int(0) | ||
+ | [1]=> | ||
+ | int(1) | ||
+ | [2]=> | ||
+ | int(2) | ||
+ | } | ||
+ | |||
+ | var_dump(range(null, | ||
+ | array(1) { | ||
+ | [0]=> | ||
+ | int(1) | ||
+ | } | ||
+ | |||
/* Array */ | /* Array */ | ||
var_dump(range([5], | var_dump(range([5], | ||
Line 267: | Line 333: | ||
==== Issues surrounding usage of INF and NAN values ==== | ==== Issues surrounding usage of INF and NAN values ==== | ||
- | Infinite values are handles | + | Infinite values are handled |
However, NAN values are not specifically handled and result in nonsensical ranges: | However, NAN values are not specifically handled and result in nonsensical ranges: | ||
Line 284: | Line 350: | ||
</ | </ | ||
- | Where using a NAN values | + | Where using a NAN value as a step even breaks the expectation that < |
+ | ==== Issues surrounding usage of string digits ==== | ||
+ | |||
+ | If one of the boundary inputs is a string digit (e.g. ''" | ||
+ | This doesn' | ||
+ | |||
+ | However, if the other input is a non-numeric string the expected behaviour of generating a list of ASCII characters is not upheld anymore: | ||
+ | <PHP> | ||
+ | var_dump( range(" | ||
+ | array(10) { | ||
+ | [0]=> | ||
+ | int(9) | ||
+ | [1]=> | ||
+ | int(8) | ||
+ | [2]=> | ||
+ | int(7) | ||
+ | [3]=> | ||
+ | int(6) | ||
+ | [4]=> | ||
+ | int(5) | ||
+ | [5]=> | ||
+ | int(4) | ||
+ | [6]=> | ||
+ | int(3) | ||
+ | [7]=> | ||
+ | int(2) | ||
+ | [8]=> | ||
+ | int(1) | ||
+ | [9]=> | ||
+ | int(0) | ||
+ | } | ||
+ | </ | ||
+ | instead of the expected: | ||
+ | <PHP> | ||
+ | var_dump( range(" | ||
+ | array(9) { | ||
+ | [0]=> | ||
+ | string(1) " | ||
+ | [1]=> | ||
+ | string(1) ":" | ||
+ | [2]=> | ||
+ | string(1) ";" | ||
+ | [3]=> | ||
+ | string(1) "<" | ||
+ | [4]=> | ||
+ | string(1) " | ||
+ | [5]=> | ||
+ | string(1) ">" | ||
+ | [6]=> | ||
+ | string(1) "?" | ||
+ | [7]=> | ||
+ | string(1) " | ||
+ | [8]=> | ||
+ | string(1) " | ||
+ | } | ||
+ | </ | ||
===== Proposal ===== | ===== Proposal ===== | ||
Line 293: | Line 414: | ||
The changes are as follows: | The changes are as follows: | ||
- | * If < | + | * If < |
- | * Introduce and use a proper ZPP check for '' | + | * Introduce and use a proper ZPP check for '' |
* Throw value errors if < | * Throw value errors if < | ||
* Throw a more descriptive < | * Throw a more descriptive < | ||
- | * Emit an <php>E_WARNING</ | + | * Throw a <php>ValueError</ |
- | * Throw a <php>ValueError</ | + | * Emit an <php>E_WARNING</ |
- | * Emit an < | + | * Emit an < |
- | * Emit an < | + | * Emit an < |
- | * Emit an < | + | * Produce a list of characters if one of the boundary inputs is a string digit instead of casting the other input to int (e.g. < |
+ | * Emit an < | ||
- | Therefore, the behaviour of some of the previous would result in the following behaviour: | + | Therefore, the behaviour of some of the previous |
<PHP> | <PHP> | ||
- | var_dump(range('', | ||
- | /* | ||
- | Warning: range(): Argument #1 ($start) must not be empty, casted to 0 in %s on line %d | ||
- | |||
- | Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 in %s on line %d | ||
- | array(1) { | ||
- | [0]=> | ||
- | int(0) | ||
- | } | ||
- | */ | ||
- | |||
var_dump(range(' | var_dump(range(' | ||
array(5) { | array(5) { | ||
Line 332: | Line 443: | ||
string(1) " | string(1) " | ||
} | } | ||
+ | |||
+ | var_dump( range(" | ||
+ | array(9) { | ||
+ | [0]=> | ||
+ | string(1) " | ||
+ | [1]=> | ||
+ | string(1) ":" | ||
+ | [2]=> | ||
+ | string(1) ";" | ||
+ | [3]=> | ||
+ | string(1) "<" | ||
+ | [4]=> | ||
+ | string(1) " | ||
+ | [5]=> | ||
+ | string(1) ">" | ||
+ | [6]=> | ||
+ | string(1) "?" | ||
+ | [7]=> | ||
+ | string(1) " | ||
+ | [8]=> | ||
+ | string(1) " | ||
+ | } | ||
+ | |||
+ | var_dump(range('', | ||
+ | /* | ||
+ | Warning: range(): Argument #1 ($start) must not be empty, casted to 0 | ||
+ | |||
+ | Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 | ||
+ | */ | ||
+ | |||
+ | |||
+ | var_dump(range(null, | ||
+ | /* | ||
+ | Deprecated: range(): Passing null to parameter #1 ($start) of type string|int|float is deprecated | ||
+ | array(3) { | ||
+ | [0]=> | ||
+ | int(0) | ||
+ | [1]=> | ||
+ | int(1) | ||
+ | [2]=> | ||
+ | int(2) | ||
+ | } | ||
+ | */ | ||
+ | |||
+ | var_dump(range(null, | ||
+ | /* | ||
+ | Deprecated: range(): Passing null to parameter #1 ($start) of type string|int|float is deprecated in %s on line %d | ||
+ | |||
+ | Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 in %s on line %d | ||
+ | array(1) { | ||
+ | [0]=> | ||
+ | int(1) | ||
+ | } | ||
+ | */ | ||
+ | |||
+ | var_dump(range(0, | ||
+ | /* | ||
+ | range(): Argument #3 ($step) must be greater than 0 for increasing ranges | ||
+ | */ | ||
</ | </ | ||
Line 338: | Line 508: | ||
Using Nikita Popov' | Using Nikita Popov' | ||
- | - 154 calls are made out with literal number arguments | + | - 154 calls are made with literal number arguments |
- 18 calls are made with literal string arguments | - 18 calls are made with literal string arguments | ||
- 140 calls have at least one argument be the result of a plus ('' | - 140 calls have at least one argument be the result of a plus ('' | ||
Line 346: | Line 516: | ||
| | ||
The calls that are non-trivial were manually checked and seem all valid. | The calls that are non-trivial were manually checked and seem all valid. | ||
- | |||
- | Only one example, a test case in Drupal, would have triggered an < | ||
- | <PHP> | ||
- | drupal/ | ||
- | Negative step is pointless | ||
- | range(5, 1, -1) | ||
- | </ | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
Line 358: | Line 521: | ||
< | < | ||
- | < | + | < |
< | < | ||
- | Calls to < | + | Calls to < |
<PHP> | <PHP> | ||
var_dump( range(1, 5, 2.0) ); | var_dump( range(1, 5, 2.0) ); | ||
Line 393: | Line 556: | ||
As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. | As per the voting RFC a yes/no vote with a 2/3 majority is needed for this proposal to be accepted. | ||
- | Voting started on 2023-XX-XX and will end on 2023-XX-XX. | + | Voting started on 2023-06-01 and will end on 2023-06-15. |
- | <doodle title=" | + | <doodle title=" |
* Yes | * Yes | ||
* No | * No |
rfc/proper-range-semantics.txt · Last modified: 2023/06/19 13:41 by girgias