Passing parameters by reference using func_get_arg(s)
I wanted to write a blog about func_get_arg(s) for quite a while now. Since its
a very handy function in PHP, but never got around doing it.
Until I ran into an interesting issue last week, while working on a new article
for my website.
Lets have a look at the function before continuing, so that everyone knows what
I am talking about. What we've got here is functionality which allows developers
to pass parameters to a function, without needing to literally define them.
function demonstrate()
{
$num_args = func_num_args();
for ($i = 0; $i < $num_args; $i++)
{
echo func_get_arg($i);
}
}
demonstrate("a","b","c"); // outputs abc
The issue comes in when we want to pass parameters by reference, observe the following
code.
function increment($a)
{
$a++;
}
$value = 10;
increment(&$value);
echo $value."<br/>"; // Value correctly echoed as 11
function increment2()
{
$a = func_get_arg(0);
$a++;
}
increment2(&$value);
echo $value."<br/>"; // Value still 11
At first glance one would think that this is a bug, and as a matter of fact has
been reported to be one:
http://bugs.php.net/bug.php?id=6427
http://bugs.php.net/bug.php?id=14705
http://bugs.php.net/bug.php?id=15098
http://bugs.php.net/bug.php?id=16464
In the PHP documentation, it does however state that it only returns a copy of the
parameters - which isnt entirely true in concept anymore. Mainly because PHP 5 introduced
a new object model, objects are passed by reference (or by handle), non-objects
are copied.
So even if we are to "copy" an object it will simply be a copy of its handle, meaning
it will still point to the orignal object. One would need to clone it if you need
a copy.
In context of the func_get_arg(s) function, objects will automatically be by reference.
As for non-objects we will need to "box" them if we want to pass it by reference
using this function.
$value = 11;
function increment3()
{
$a = func_get_arg(0);
$a->scalar++;
}
$value = (object)$value; // Similar to Boxing
increment3($value);
$value = $value->scalar; // Essentially our unboxing
echo $value."<br/>"; // Value correctly echoed as 12
Which is a bit of a tedious
(but interesting) solution as pointed out by one of my vistors (Chris) from
www.pulseforce.com
The following solution he suggested makes a LOT more sense (you can read his comments further down):
<?php
$a = 11;
function increment4()
{
$a = func_get_arg(0);
$a++;
return array($a);
}
list($a) = increment4($a);
echo $a; // Value correctly echoed as 12
?>
There is however even a better solution
(also pointed out by one of the visitors), by using the debug_backtrace function, we're able to pass values by reference.
$value = 11;
function increment5()
{
$stack = debug_backtrace();
$stack[0]["args"][0]++;
}
increment5(&$value);
echo $value."<br/>"; // Value correctly echoed as 12
Posted by - Christoff Truter
Date - 2008-07-27 12:38:24
Comments
Post comment