I encountered strange behavior in callback function. Following is the code that should replace every letter of a string to it’s serial number while creating the map of replacements.
$i = 2;
$map = [];
$token = preg_replace_callback(
'~w~',
function($matches) use($map, $i) {
$i += 1;
$map[$i] = $matches[0];
print "$i ";
return $i;
},
'$token'
);
print_r($map);
print "n$token";
Weirdly it prints
3 3 3 3 3 Array
(
)
$33333
i.e. $i value is taken but it’s changing not used in next callback calls. What’s going on here? And how to fix it?
Sandbox below
https://onlinephp.io/c/a4b1c0
2
Answers
Passing
use
parameters by reference, as in :Produces:
You’d have to clarify your question to determine if this is what is desired besides the obvious value-vs-reference issue.
By default variable capture in PHP is by value only – when the closure is created, the current value of each variable in the
use
list is examined, and assigned to a local variable with the same name inside the closure. Assigning to the local variable inside the closure does not write back to the variable which was originally captured, and every time you execute the closure, the captured variables start with their originally captured value.This is different from some other languages, like JavaScript, where closures carry writeable references to their captured variables. Capturing by value is generally easier to understand, for instance if you want to create a list of closures in a loop:
If
$number
was captured by reference, all five closures would end up identical, all referring to the same variable; because it’s captured by value, you get five different closures, each with a different value of$number
.In cases where you do want a reference, you can use the
&
modifier in theuse
list:Now, the local name
$map
inside the closure refers to the same variable as the name$map
outside it – any modification to one will be visible in the other.This is similar to the difference between
$myMap = $yourMap
(assignment by value) and$myMap =& $yourMap
(linking two variables as references).(Note that, in all the above cases, the "value" of an object is a pointer to something mutable – so if
$map
was an object rather than array, calling$map->setSomething(42)
would modify the same object that was visible inside and outside the closure. This is not the same as capturing or assigning by reference.)