rfc:increment_decrement_fixes
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:increment_decrement_fixes [2020/03/01 21:07] – imsop | rfc:increment_decrement_fixes [2022/02/18 14:38] (current) – abandoned, because it brought all the trolls out of the woodwork, and life's too short imsop | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Increment/ | ====== PHP RFC: Increment/ | ||
- | * Version: | + | * Version: |
- | * Date: 2020-03-01 | + | * Date: 2020-03-08 |
- | * Author: Rowan Tommins, rowan.collins@gmail.com | + | * Author: Rowan Tommins |
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | The increment and decrement operators (< | + | Intuitively, |
- | Intuitively, the <php>++</ | + | Most arithmetic operators, including |
- | This is currently not the case, because | + | On top of this, the < |
- | For null and boolean, addition and subtraction are based on casting to integer; for arrays, they throw an error; this RFC aims to make increment and decrement consistent in all three cases. | ||
+ | ===== Fix or remove? ===== | ||
- | ===== Proposal 1: Decrementing Null ===== | + | A common concern is that fixing these inconsistencies would imply " |
- | The behaviour | + | The position |
- | This behaviour has been raised as a bug at least three times ([[https:// | + | The balance of these factors |
- | Discrepancies | + | * For arrays, throw an error in 8.0, because most operators already do |
+ | * For objects, resources, and booleans, assume that an error will be proposed in a future RFC | ||
+ | * For null, continue to allow arithmetic operators, and fix the missing implementation of < | ||
- | This RFC proposes to change the behaviour, so that < | + | ===== Strings ===== |
- | ^ ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | + | Strings overload the <php>++</ |
- | ^ Current ^ null | 1 | 1 | 1 | -1 | -1 | null | | + | |
- | ^ Proposed ^ null | 1 | 1 | 1 | -1 | -1 | -1 | | + | |
- | ===== Proposal 2: Incrementing and Decrementing Booleans ====== | + | ===== Arrays |
- | The behaviour of booleans with most mathematical operations is to treat false as 0 and true as 1. However, | + | Adding an integer |
- | This RFC proposes to change the behaviour so that it is in line with other mathematical contexts, | + | This RFC proposes to change the behaviour so that < |
^ ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | ^ ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | ||
- | ^ Current ^ true | 2 | 2 | true | 0 | 0 | true | | + | ^ Current ^ any array | Error | Error | no effect |
- | ^ Proposed ^ true | 2 | 2 | 2 | 0 | 0 | 0 | | + | ^ Proposed ^ any array | Error | Error | Error | Error | Error | Error | |
- | ^ Current ^ false | 1 | 1 | false | -1 | -1 | false | | + | |
- | ^ Proposed ^ false | 1 | 1 | 1 | -1 | -1 | -1 | | + | |
- | ===== Proposal 3: Error when incrementing or decrementing an array ===== | + | ===== Objects, resources, and booleans |
- | Adding an integer to an array throws an Error with the message " | + | Most arithmetic operations will implicitly cast non-numeric |
- | This RFC proposes | + | * Objects (other than internal objects with operator overloading) are cast to 1, and an E_NOTICE is raised |
+ | * Resources are cast to their internal ID | ||
+ | * false is cast to 0 | ||
+ | * true is cast to 1 | ||
+ | |||
+ | The < | ||
+ | |||
+ | ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | ||
+ | ^ any object | 2 (with E_NOTICE) | 2 (with E_NOTICE) | no effect | 0 (with E_NOTICE) | 0 (with E_NOTICE) | no effect | | ||
+ | ^ resource#1 | 2 | 2 | no effect | 0 | 0 | no effect | | ||
+ | ^ resource#5 | 6 | 6 | no effect | 4 | 4 | no effect | | ||
+ | ^ false | 1 | 1 | false | -1 | -1 | false | | ||
+ | ^ true | 2 | 2 | true | 0 | 0 | true | | ||
+ | |||
+ | Possible options: | ||
+ | |||
+ | * A: Implement < | ||
+ | * B: Raise an error for < | ||
+ | * C: Raise an error for all arithmetic operators. | ||
+ | |||
+ | The position of this RFC is that option A brings very little benefit for these types, because the resulting values aren't particularly intuitive or useful. On the other hand, option B simply swaps one inconsistency for another. Option C has the added benefit of bringing the behaviour of these types in line with the behaviour of arrays. | ||
+ | |||
+ | The implications of introducing such an error deserve their own RFC. For instance, it was mentioned in [[rfc/ | ||
+ | |||
+ | The position of this RFC is to leave these types unchanged, in the expectation of a further RFC implementing option C. | ||
+ | |||
+ | ===== Null ===== | ||
+ | |||
+ | Nulls deserve special consideration for two reasons: | ||
+ | |||
+ | * The implicit coercion to int is significantly more useful than for other types | ||
+ | * The < | ||
+ | |||
+ | ==== Current inconsistency ==== | ||
+ | |||
+ | For nearly all arithmetic operators, < | ||
+ | |||
+ | This behaviour has been raised as a bug at least three times ([[https:// | ||
+ | |||
+ | Consider the following patch, which looks like a straight-forward simplification: | ||
+ | |||
+ | <code diff> | ||
+ | function foo($bar) { | ||
+ | - $bar -= 1; | ||
+ | + $bar--; | ||
+ | // more code here | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The old code always results in an integer in < | ||
+ | |||
+ | The asymmetry of <php>++</ | ||
+ | |||
+ | <code diff> | ||
+ | function repeatThing($extraTimes) { | ||
+ | - for ( $i = 0; $i <= $extraTimes; | ||
+ | + for ( $i = $extraTimes; | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Again, this looks like a safe change, reversing the order of the loop; but if passed a value of NULL, the old code will call doThing() once, and the new code will enter an infinite loop. | ||
+ | |||
+ | ==== Options ==== | ||
+ | |||
+ | There are four main options to improve the situation: | ||
+ | |||
+ | * A: Implement < | ||
+ | * B1: Raise an error for < | ||
+ | * B2: Raise an error for both < | ||
+ | * C: Raise an error for all arithmetic operators. | ||
+ | |||
+ | Option B1 is in some ways the safest, because it makes the change immediately visible, and most cases affected are probably already buggy. However, it is hard to justify raising an error for < | ||
+ | |||
+ | ==== Removing null to int coercion ==== | ||
+ | |||
+ | Should we then go with Option C, as proposed for other types? That is, should we introduce, either immediately, | ||
+ | |||
+ | An important consideration is that undefined variables, array items, and object properties are currently treated as having a value of null. While it has been suggested to change this to an error in a future version, there is no clear consensus to do so. This means operations on null are more common than the other types we have discussed, and are likely to remain so. | ||
+ | |||
+ | Furthermore, | ||
+ | |||
+ | Even the increment operator itself can be useful with null / unitialised values, for instance in this common implementation of "count items in each category": | ||
+ | |||
+ | <code php> | ||
+ | $counts = []; | ||
+ | foreach ( $items as $item ) { | ||
+ | $counts[$item-> | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | If there was no implicit default of zero, an extra line such as <php>$counts[$item-> | ||
+ | |||
+ | Since the case for removing null to int coercion is much less decisive than for other types, this RFC makes the assumption that no such removal is likely in the short term. As such, waiting for that removal does not solve the immediate problem of inconsistency between < | ||
+ | |||
+ | ==== Fixing the decrement case ==== | ||
+ | |||
+ | As mentioned above, no evidence has been put forward that the missing definition of decrement for null is intentional. | ||
+ | |||
+ | This RFC therefore proposes to fix this bug, so that <php>$a=null; $a--;</ | ||
^ ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | ^ ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | ||
- | ^ Current ^ any array | Error | Error | no effect | + | ^ Current ^ null | 1 | 1 | 1 | -1 | -1 | null | |
- | ^ Proposed ^ any array | Error | Error | Error | Error | Error | Error | | + | ^ Proposed ^ null | 1 | 1 | 1 | -1 | -1 | -1 | |
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | All of the proposed changes are explicit breaks in compatibility. | + | The additional error on increment |
+ | The fix to decrement on nulls is harder to detect. While it's unlikely that any code is deliberately relying on the current behaviour, it may inadvertently be doing so. It would be sensible to provide links in official upgrading documentation to static analysis tools which can point out potentially affected code. | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 64: | Line 163: | ||
===== RFC Impact ===== | ===== RFC Impact ===== | ||
+ | |||
+ | ==== On OpCache ==== | ||
+ | |||
+ | If there is currently any optimisation based on the non-implementation of decrement for nulls, or the behaviour of increment and decrement for arrays, this will need to be amended if the respective votes pass. | ||
===== Open Issues ===== | ===== Open Issues ===== | ||
Line 72: | Line 175: | ||
===== Unaffected PHP Functionality ===== | ===== Unaffected PHP Functionality ===== | ||
- | **Undefined variables, array items, and object properties** are currently treated as null for most purposes, including the < | + | As mentioned above, this RFC proposed no changes to handling |
- | Similarly, most mathematical contexts will coerce null and false to 0, and true to 1; this RFC does not seek to change | + | It also does not directly propose |
- | + | ||
- | **Strings** overload the < | + | |
- | + | ||
- | **Objects and resources** are also left unchanged with < | + | |
- | + | ||
- | For reference, the current behaviour for most operations is that objects are coerced to int(1), and resources to their internal ID, giving this table: | + | |
- | + | ||
- | ^ Initial Value ^ %%$a = $a + 1%% ^ %%$a += 1%% ^ %%++$a%%, %%$a++%% ^ %%$a = $a - 1%% ^ %%$a -= 1%% ^ %%--$a%%, %%$a--%% ^ | + | |
- | ^ any object | 2 (with E_NOTICE) | 2 (with E_NOTICE) | no effect | 0 (with E_NOTICE) | 0 (with E_NOTICE) | no effect | | + | |
- | ^ resource#1 | 2 | 2 | no effect | 0 | 0 | no effect | | + | |
- | ^ resource#5 | 6 | 6 | no effect | 4 | 4 | no effect | | + | |
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | The three proposed changes can be implemented independently, | + | The two proposed changes can be implemented independently, |
- | * Should decrementing null result | + | * Should decrementing null be fixed in line with incrementing and with subtraction, |
- | * Should incrementing and decrementing booleans act the same as addition and subtraction? (Yes / No) | + | * Should incrementing or decrementing an array throw an " |
- | * Should incrementing or decrementing an array throw an " | + | |
Line 113: | Line 204: | ||
===== Rejected Features ===== | ===== Rejected Features ===== | ||
- | TODO | + | A previous version of the RFC proposed fixing the implementation for booleans to match integer coercion followed by adding or substracting 1. The current draft instead groups booleans with objects and resources, as warranting a separate RFC proposing an error. |
rfc/increment_decrement_fixes.1583096864.txt.gz · Last modified: 2020/03/01 21:07 by imsop