# Copy On Write and Assign By Reference perform different on PHP5 and PHP7

- Thread: https://externals.io/message/92050
- Original archive: https://news-web.php.net/php.internals/92050
- Started by Huqiu Liao on 2016-03-31T16:47:28+00:00
- Messages: 6
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #92050 — Huqiu Liao — 2016-03-31T16:47:28+00:00

URL: https://externals.io/message/92050#92050

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

---

## #92051 — Rowan Collins — 2016-03-31T17:23:32+00:00

URL: https://externals.io/message/92050#92051 · In reply to: #92050

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]

---

## #92055 — Rowan Collins — 2016-03-31T22:39:07+00:00

URL: https://externals.io/message/92050#92055 · In reply to: #92051

> 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]

---

## #92057 — Yasuo Ohgaki — 2016-04-01T01:36:32+00:00

URL: https://externals.io/message/92050#92057 · In reply to: #92050

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

---

## #92059 — Sara Golemon — 2016-04-01T01:52:24+00:00

URL: https://externals.io/message/92050#92059 · In reply to: #92050

> 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

---

## #92099 — Rowan Collins — 2016-04-05T12:06:19+00:00

URL: https://externals.io/message/92050#92099 · In reply to: #92059

Sara Golemon wrote on 01/04/2016 02:52:
> 
> > […]
> 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]
