I had to spend a few hours to figure out why a[3] is changing on each iteration. This is the explanation at which I arrived.
There are two types of variables in PHP: normal variables and reference variables. If we assign a reference of a variable to another variable, the variable becomes a reference variable.
for example in
$a = array('zero', 'one', 'two', 'three');
if we do
$v = &$a[0]
the 0th element ($a[0]) becomes a reference variable. $v points towards that variable; therefore, if we make any change to $v, it will be reflected in $a[0] and vice versa.
now if we do
$v = &$a[1]
$a[1] will become a reference variable and $a[0] will become a normal variable (Since no one else is pointing to $a[0] it is converted to a normal variable. PHP is smart enough to make it a normal variable when no one else is pointing towards it)
This is what happens in the first loop
foreach ($a as &$v) {
}
After the last iteration $a[3] is a reference variable.
Since $v is pointing to $a[3] any change to $v results in a change to $a[3]
in the second loop,
foreach ($a as $v) {
echo $v.'-'.$a[3].PHP_EOL;
}
in each iteration as $v changes, $a[3] changes. (because $v still points to $a[3]). This is the reason why $a[3] changes on each iteration.
In the iteration before the last iteration, $v is assigned the value 'two'. Since $v points to $a[3], $a[3] now gets the value 'two'. Keep this in mind.
In the last iteration, $v (which points to $a[3]) now has the value of 'two', because $a[3] was set to two in the previous iteration. two is printed. This explains why 'two' is repeated when $v is printed in the last iteration.