Hi all,
There is currently an odd inconsistency when using the decrement
operator on a null variable:
$a = null; $a=$a+1; // int(1)
$a = null; $a+=1; // int(1)
$a = null; ++$a; // int(1)
$a = null; $a=$a-1; // int(-1)
$a = null; $a-=1; // int(-1)
$a = null; --$a; // null
I would like to propose changing this behaviour for PHP 8, so that --$a
would give int(-1), as I believe it is simply a long-standing bug.
This has been raised as a bug at least three times [1][2][3] but closed
as documented behaviour / too much of a BC break. It is documented in
the manual, but with no explanation of why it should work that way. [4]
I would be interested in any explanations of why it might be intended
behaviour, or ways in which people might be relying on the current
behaviour.
A proposal to change the behaviour was included in a wider RFC about
standardising increment and decrement behaviour, but it never got beyond
draft status. [5] I would prefer not to reopen that wider debate, but
focus on this single issue.
As far as I can see, the change would be to add a "case IS_NULL" branch
to decrement_function in zend_operators.c to match the one in
increment_function. [6]
I will happily write up an RFC to formalise this, but wanted to gather
people's immediate thoughts first.
Links:
[1] https://bugs.php.net/bug.php?id=20548
[2] https://bugs.php.net/bug.php?id=25674
[3] https://bugs.php.net/bug.php?id=41690
[4] https://www.php.net/manual/en/language.operators.increment.php
[5] https://wiki.php.net/rfc/normalize_inc_dec
[6]
https://php-lxr.adamharvey.name/source/xref/master/Zend/zend_operators.c#2359
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
My opinion is that it'd be more consistent for --
to work like -= 1
(e.g. become -1
).
It might break some code, but that code was probably incorrect.
Out of scope of the proposed RFC, but this reminds me of a similar issue:
Currently, the ++
and --
operators do nothing to arrays or objects,
not even emitting a notice or changing the value.
I see that ++
on typed properties/references can already throw a TypeError for integer overflow,
so it might make sense to also start throwing a TypeError for ++
on objects (without numeric operation handlers) and arrays.
I'd thought earlier that emitting a notice instead of throwing a TypeError for arrays/objects might negatively limit the optimizations opcache can do
because error handlers can have side effects.
But it looks like I'd just have to allow inferring that MAY_BE_OBJECT and MAY_BE_ARRAY
could have side effects for the INC/DEC opcodes in may_have_side_effects()
in ext/opcache/Optimizer/dce.c.
(If your RFC ends up emitting warnings/notices, it would need to check for MAY_BE_NULL|MAY_BE_UNDEF in dce.c, I think.)
Would there be any interest in emitting a warning or deprecation warning for objects/arrays starting in php 8.0?
- Tyson
My opinion is that it'd be more consistent for
--
to work like-= 1
(e.g. become-1
).
It might break some code, but that code was probably incorrect.
Yep, this is precisely the way I see it.
--
Rowan Tommins (né Collins)
[IMSoP]
There is currently an odd inconsistency when using the decrement
operator on a null variable:
I'm not so sure...
That incrementing a null works at all is a painful part of the language
spec that I would argue needs flushing down the toilet, rather than
further reinforcing.
I recon the required precursor to doing so is erroring on undefined
variables, which I suspect accounts for about 99% of increment-on-nulls.
The majority are in favour (56%) but not the supermajority necessary -
Athough there's an argument to be made that a supermajority may exist in
a straight up or down vote rather than a 3-way
(https://wiki.php.net/rfc/engine_warnings).
So on one hand, consistency is good.
On the other hand, being consistently bad is still being bad.
--
Mark Randall
marandall@php.net
On Sat, Feb 15, 2020 at 6:44 PM Rowan Tommins rowan.collins@gmail.com
wrote:
Hi all,
There is currently an odd inconsistency when using the decrement
operator on a null variable:$a = null; $a=$a+1; // int(1)
$a = null; $a+=1; // int(1)
$a = null; ++$a; // int(1)$a = null; $a=$a-1; // int(-1)
$a = null; $a-=1; // int(-1)
$a = null; --$a; // nullI would like to propose changing this behaviour for PHP 8, so that --$a
would give int(-1), as I believe it is simply a long-standing bug.This has been raised as a bug at least three times [1][2][3] but closed
as documented behaviour / too much of a BC break. It is documented in
the manual, but with no explanation of why it should work that way. [4]I would be interested in any explanations of why it might be intended
behaviour, or ways in which people might be relying on the current
behaviour.A proposal to change the behaviour was included in a wider RFC about
standardising increment and decrement behaviour, but it never got beyond
draft status. [5] I would prefer not to reopen that wider debate, but
focus on this single issue.As far as I can see, the change would be to add a "case IS_NULL" branch
to decrement_function in zend_operators.c to match the one in
increment_function. [6]I will happily write up an RFC to formalise this, but wanted to gather
people's immediate thoughts first.
Principally in favor of this change, I do think that ++ and -- should
behave consistently if nothing else. We might want to consider giving the
same treatment to false/true as well, which should be interpreted as 0/1.
That is $foo++ / $foo-- should behave the same ways as $foo+=1, $foo-=1 for
null, true, false. It seems odd to single out only "null" here.
Additionally I would suggest a notice when trying to increment arrays,
resources and objects, rather than just silently doing nothing. As long as
it's just a notice, this should have minimal BC implications.
Nikita
Principally in favor of this change, I do think that ++ and -- should
behave consistently if nothing else. We might want to consider giving
the same treatment to false/true as well, which should be interpreted
as 0/1. That is $foo++ / $foo-- should behave the same ways as
$foo+=1, $foo-=1 for null, true, false. It seems odd to single out
only "null" here.Additionally I would suggest a notice when trying to increment arrays,
resources and objects, rather than just silently doing nothing. As
long as it's just a notice, this should have minimal BC implications.
Thanks. That seems a reasonably modest expansion, without getting into
the deeper questions of string increments or fatal errors.
For the record, the reason I singled out null is that it's the only type
where ++ and -- behave differently from each other.
With booleans, there is at least a consistency between those two
operators, even though it's consistently weird. There's definitely a
strong case for making them match +=1 and -=1 though.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Le mardi 18 février 2020, 20:27:37 CET Rowan Tommins a écrit :
With booleans, there is at least a consistency between those two
operators, even though it's consistently weird. There's definitely a
strong case for making them match +=1 and -=1 though.
Is there any reason the engine is not running the same code or even compiling to the same opcodes $a++ and $a+=1?
If it should never differ, why is it not the same operation?
--
Côme Chilliet
FusionDirectory - https://www.fusiondirectory.org
Am 19.02.2020 um 15:52 schrieb Côme Chilliet come.chilliet@fusiondirectory.org:
Le mardi 18 février 2020, 20:27:37 CET Rowan Tommins a écrit :
With booleans, there is at least a consistency between those two
operators, even though it's consistently weird. There's definitely a
strong case for making them match +=1 and -=1 though.Is there any reason the engine is not running the same code or even compiling to the same opcodes $a++ and $a+=1?
If it should never differ, why is it not the same operation?
$a++ is magic, see example #1 at https://www.php.net/manual/en/language.operators.increment.php
And no, this should not be changed as it would be a major BC.
- Chris
Le mercredi 19 février 2020, 15:59:24 CET Christian Schneider a écrit :
Am 19.02.2020 um 15:52 schrieb Côme Chilliet come.chilliet@fusiondirectory.org:
Is there any reason the engine is not running the same code or even compiling to the same opcodes $a++ and $a+=1?
If it should never differ, why is it not the same operation?$a++ is magic, see example #1 at https://www.php.net/manual/en/language.operators.increment.php
And no, this should not be changed as it would be a major BC.
Right, $a+=1 is the same as ++$a, not $a++, my bad, I forgot about this.
Is ++$a behaving differently than $a++ for NULL/FALSE/TRUE?
--
Côme Chilliet
FusionDirectory - https://www.fusiondirectory.org
On Wed, 19 Feb 2020 at 15:05, Côme Chilliet <
come.chilliet@fusiondirectory.org> wrote:
Is ++$a behaving differently than $a++ for NULL/FALSE/TRUE?
The actual implementation of incrementing and decrementing is the same
whichever operator you use, it's just wrapped in code to return the old/new
value as appropriate.
So right now, for false and true (and for null with --), you can't tell the
difference, because the variable is the same before and after. For null
(with ++), they evaluate to the old or new value, as expected:
$a = null; var_dump( $a++, $a ); // NULL, int(1)
$a = null; var_dump( ++$a, $a ); // int(1), int(1)
As far as I can see, null and bool are simply missing from the switch
statement that special cases each type of variable:
- increment_function (has null but no bool):
https://github.com/php/php-src/blob/26327bcd3b6375a4883f00a289ba129e5b23717d/Zend/zend_operators.c#L2299 - decrement_function (has neither):
https://github.com/php/php-src/blob/26327bcd3b6375a4883f00a289ba129e5b23717d/Zend/zend_operators.c#L2359
Regards,
Rowan Tommins
[IMSoP]