PHP Puzzler: Forgetful Variables

Today I present to you a PHP puzzler that in fact had me stumped for a day or two. I do enjoy when a language throws a curve ball; it presents an opportunity to gain insight on the mechanics and become acquainted with the nuances of the language in question. After all, better understanding leads to better code.

The Puzzler

<?php
//function that adds 1 to the argument
function addOne(&$value) {
  ++$value;
}

//lets try it out
addOne($variable = 5);

//print the $value now
echo($variable);
?>

And now we vote! What is the output of this example?

  1. compile error
  2. emtpy string
  3. 6
  4. 5

#1: compile error [FALSE] The code is indeed proper PHP syntax.

#2: empty string [FALSE] PHP variable scoping places variables defined within function calls in the same scope that the function is being called from – $variable is indeed defined and initialized to 5.

#3: 6 [FALSE] Ok so it compiles and the variable is defined as expected, everything should be working nicely, right? We passed it by reference – what’s going on? Did $variable forget that it has been changed?

#4: 5 [CORRECT] Strangely enough, the value of $variable remains set to 5. This can be verified – try running the code snippet at http://codepad.org.

The Spiel

Intuitively, we expect from the code that a variable named ‘variable’ is defined, initialized to 5 and then passed-by-reference to the function call. Perhaps surprisingly, this is not the case with PHP – what is in fact passed is an expression that evaluates to the same value as that of $variable. To protect $variable PHP’s copy-on-write mechanism kicks in when we try to modify this value. $variable did not forget that is was changed; the reference was never to $variable to begin with, but to the anonymous expression. To achieve what was really intended, we must pass a variable instead of an expression to the function.

<?php
function addOne(&$value) {
  ++$value;
}

$variable = 5;
addOne($variable);
echo($variable); //now it will be 6
?>