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
 








Comments



Awesome

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

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

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?


???

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

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


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

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

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.


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


Post comment

Name *
Email
Title
Body *
Security Code
*
* Required fields

Latest Posts

Be the best stalker you can be


2011-12-13 22:33:54

Syntactic sugar (C#): Enum


2011-08-04 16:50:18

Top 5 posts

Simple WYSIWYG Editor


Creating a WYSIWYG textbox for your website is actually quite simple.
2007-02-01 12:00:00

Moving items between listboxes in ASP.net/PHP example


Move items between two listboxes in ASP.net(C#, VB.NET) and PHP
2008-06-12 17:07:43

Cross Browser Issues: Firefox Word Wrapping


Firefox word wrapping issues
2008-06-09 09:51:21

Populate a TreeView Control C#


Populate a TreeView control in a windows application.
2009-08-27 16:01:03

C# YouTube : Google API


Post on how to integrate with YouTube using the Google Data API
2011-03-12 08:37:51