Hi, internals,
I have a question about Assign By Reference and I posted on StackOverflow,
I'd like to know the reason behind it, and I did not get any this kind of
answer, can anyone give me some clues.
We have a piece of simple code:
1 <?php
2 $i = 2;
3 $j = &$i;
4 echo (++$i) + (++$i);
On PHP5, it outputs 8, because:
$i is a reference, when we increase $i by ++i, it will change the zval
rather than make a copy, so line 4 will be 4 + 4 = 8. This is Assign By
Reference.
If we comment line 3, it will output 7, every time we change the value by
increasing it, PHP will make a copy, line 4 will be 3 + 4 = 7. This is Copy
On Write.
But in PHP7, it always outputs 7.
I've checked the changes in PHP7:
http://php.net/manual/en/migration70.incompatible.php, but I did not get
any clue.
Here is the result of the code on PHP5 / PHP7: https://3v4l.org/USTHR
And there is the link on Stackoverflow:
http://stackoverflow.com/questions/36338661/php-copy-on-write-and-assign-by-reference-perform-different-on-php5-and-php7
Thanks guys.
Best,
--
Huqiu Liao
http://www.liaohuqiu.net
Huqiu Liao wrote on 31/03/2016 17:47:
Hi, internals,
I have a question about Assign By Reference and I posted on StackOverflow,
I'd like to know the reason behind it, and I did not get any this kind of
answer, can anyone give me some clues.
We have a piece of simple code:
1 <?php
2 $i = 2;
3 $j = &$i;
4 echo (++$i) + (++$i);
On PHP5, it outputs 8, because:$i is a reference, when we increase $i by ++i, it will change the zval
rather than make a copy, so line 4 will be 4 + 4 = 8. This is Assign By
Reference.
To me, that sounds like a fixed bug. It's noteworthy in the 3v4l results
that HHVM also gives 7 as the answer, and I can't see any logical reason
for the answer to be 8 - regardless of execution order, the result of
the two calls to ++$i should never be the same.
It would be interesting to see the VLD dump in PHP 5, with and without
reference, because looking at the PHP 7 one, it's clear that the two
increments result in two temp vars so it shouldn't make any difference
what type of zval was given as input:
4 2 PRE_INC $2 !0 # !0 is compiled $i, $2 is a
new value returned
3 PRE_INC $3 !0 # !0 is referenced again, but
$3 is unrelated to $2
4 ADD ~4 $2, $3 # the two values are added,
with no reference to !0
Maybe in PHP 5 the opcodes are the same, but $2 and $3 somehow end up as
references to !0, rather than new zvals, as though the code were:
$i = 2;
$i++; $temp1 =& $i;
$i++; $temp2 =& $i;
echo $temp1 + $temp2;
rather than, as seems logical to me, HHVM, and PHP7:
$i = 2;
$i++; $temp1 = $i;
$i++; $temp2 = $i;
echo $temp1 + $temp2;
Regards,
Rowan Collins
[IMSoP]
Maybe in PHP 5 the opcodes are the same, but $2 and $3 somehow end up
as references to !0, rather than new zvals
So, it turns out, this is exactly what happens. Specifically, there is a
call to SEPARATE_ZVAL_IF_NOT_REF(var_ptr); in the definition of the
ZEND_PRE_INC opcode
[http://lxr.php.net/xref/PHP_5_6/Zend/zend_vm_def.h#814] If the zval
is a reference, the code just ends up incrementing its reference
count, returning a reference to it rather than the calculated value.
In PHP 7, references are very different, and integers aren't refcounted,
so it's all a different story.
I got a bit curious and did some deep investigation, which I've written
up on StackOverflow: http://stackoverflow.com/a/36344524/157957 I've
about reached my limit now, though, so I'll leave it to others to
correct or fill in any further info. :)
Regards,
--
Rowan Collins
[IMSoP]
Hi Huqui,
4 echo (++$i) + (++$i);
I brought up this topic before and the conclusion is
This kind of operation result is undefined and user shouldn't write
such expression.
There are undefined behaviors for ++ and --.
C/C++ has undefined behavior for ++/--
https://en.wikipedia.org/wiki/Undefined_behavior#Examples_in_C_and_C.2B.2B
Some languages do not have these operators for this reason.
PHP may define behavior for these, but it is undefined for future
improvements. (And behavior is changed in PHP 7.0)
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net
I have a question about Assign By Reference and I posted on StackOverflow,
I'd like to know the reason behind it, and I did not get any this kind of
answer, can anyone give me some clues.
Are you asking out of curiosity? Or because you think a new bug has
been introduced in PHP7?
If the latter, I'd respond by pointing out that the behavior of using
multiple operations with side-effects (like pre/post inc/dec) is
defined as undefined. That is, while the PHP reference implementation
may give you a particular result, you should not rely on that result.
If the former, and you're just curious why, it comes down to a very
fundamental change in the way PHP7 deals with references. I'd
recommend reading Nikita's writeup of the differences between PHP 5
and PHP 7 variables at:
https://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html
-Sara
Sara Golemon wrote on 01/04/2016 02:52:
I have a question about Assign By Reference and I posted on StackOverflow,
I'd like to know the reason behind it, and I did not get any this kind of
answer, can anyone give me some clues.Are you asking out of curiosity? Or because you think a new bug has
been introduced in PHP7?If the latter, I'd respond by pointing out that the behavior of using
multiple operations with side-effects (like pre/post inc/dec) is
defined as undefined. That is, while the PHP reference implementation
may give you a particular result, you should not rely on that result.
I just dug into the spec out of curiosity, and I have a nit-pick: there
is relevant language under "Section 10: Expressions" [1]:
Unless stated explicitly in this specification, the order in which
the operands in an expression are evaluated relative to each other is
unspecified. [...] (For example,[...] in the full expression $j = $i +
$i++, whether the value of $i is the old or new $i, is unspecified.)
But arguably this doesn't go far enough to justify the PHP 5 engine's
behaviour in this case, because it only mentions the order of
evaluation being unspecified, not the actual semantics. The definition
of the pre-increment operator [2] states:
The result is the value of the operand after it has been incremented.
That is, the operator does not return the operand itself, but its value
at a particular time. However, there is no order of execution which
allows the value of the variable to be 4 on both sides of the addition;
to get this requires the actual behaviour of the ++ operator to be
re-defined as "return a reference to the incremented operand". (You
could make an argument for the two increments happening "simultaneously"
and giving a result of 6, but to get 8, they have to both happen
simultaneously and see each other's results.)
I'm not seriously suggesting that the PHP 5 behaviour is problematic,
but unless there are additional caveats somewhere else, it does seem to
violate the letter of the current spec.
[1] https://github.com/php/php-langspec/blob/master/spec/10-expressions.md
[2]
https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#prefix-increment-and-decrement-operators
Regards,
Rowan Collins
[IMSoP]