Passing parameters by reference using func_get_arg(s)

July 27, 2008 by PHP  

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


Leave a Comment


January 2, 2014 by Darth Killer

Found this page during a search, and i'd like to mention something i found during said research : the debug_backtrace() approach was depreciated, and since PHP 5.4.0 no longer allowed at all. :/ http://stackoverflow.com/questions/7026872/how-to-pass-unlimited-arguments-by-reference-in-php/7035378#7035378 Meanwhile, i think i'll bookmark your blog for later exploring. ;)

Awesome April 14, 2010 by Christoff Truter

Thank you for sharing :) I must just point out (to others), that one will call the function like e.g. escapeString(&$a); I will be sure to update my post

My version // richmp.de April 13, 2010 by RMP

if found something in a post on php.net and changed it a little bit. this is the result // this function is should prevent from sql-injections public function escapeString(){ // get arguments by reference $stack = debug_backtrace(); if (isset($stack[0]["args"])) for($i=0; $i < count($stack[0]["args"]); $i++) $stack[0]["args"][$i] = str_replace("'","''",$stack[0]["args"][$i]); }

By Reference February 19, 2010 by Christoff Truter

Hi there, its all about references, if we pass a value by reference to our function, it is expected that any changes we make to the value should be updated. But unfortunately this isn't the case, hence if we're "faking" it, e.g. list($a, $b, $c, $d) = increment4(). Make sense?

??? February 19, 2010 by Fran

I do not catch the point. This seems for me like: <?php $a = 11; function increment4() { $a = func_get_arg(0); $a++; return $a; } $a = increment4($a); echo $a; // Value correctly echoed as 12 ?> So it has not much sense... In which point m I wrong?

Thank you January 28, 2009 by Christoff Truter

Hey Chris Your solution does make a LOT more sense, thank you for sharing it. I will be sure to update my blog Thank you

January 28, 2009 by Chris PF

Also, here's the speedfreak edition of the function, I didn't want to post it first since it is harder to read so it wasn't as good for illustrating the example (but faster! it passes by reference where it can and loops in a more efficient way). =0; $i--) // for anyone wondering, this is a common optimization trick which makes it so that the size of the array only has to be checked ONCE, by setting the counter to it and then iterating downwards until we reach 0 (the first element) { TextEscape::escape_string($aVariables[$i]); } /* return the escaped values */ return $aVariables; } } ?> Enjoy!

BETTER WAY January 28, 2009 by Chris PF

Hi, I came across your site while researching this problem myself. You had some interesting findings there, regarding how objects are passed by reference while non-objects are not. However, your workarounds are plain BAD. We're meant to devote MASSIVE AMOUNTS OF LINES to convert small variables like INTEGERS, STRINGS and so on to OBJECTS? That's crazy, not to mention insane! Not only does it take up loads of space, it actually changes the variable types to objects! Here's a much better example that shows how you can escape multiple variables in one fell swoop by leveraging some features of PHP. The usage is: "list($a, $b, $c, $d, $e) = TextEscape::escape_multi($a, $b, $c, $d, $e);" If PHP had ACTUALLY been capable of passing variables by reference when using variable-length argument lists, that could have been reduced to: "TextEscape::escape_multi($a, $b, $c, $d, $e);" It's extremely close though, both in simplicity and speed. Just be VERY sure to ALWAYS have the SAME NUMBER AND ORDER of arguments on both sides of that statement. Now, onto the code, enjoy the sheer brilliance and enjoy processing multiple variables at once! ;-) $sValue) { $aVariables[$iKey] = TextEscape::escape_string($sValue); } /* return the escaped values */ return $aVariables; } } // Create test variables $a = "A A B>bar // [2] => C D>bar // [4] => E A<bar // [1] => B>bar // [2] => C<bar // [3] => D>bar // [4] => E<bar // ) print_r(array($a, $b, $c, $d, $e)); ?>

Nice Blog July 29, 2008 by Nasir Ghaznavi

Got through reading around 12 pages over your blog, after your ticket :) Nice stuff, dunno why is it not that popular? Anyway South Africa! is it really so scary i mean the mafia and all that.

July 29, 2008 by Christoff Truter

South Africa is a bit scary not a place to raise, children, thats why we're planning to immigrate to New Zealand one of these days