|
Passing by ReferenceYou can pass variable to function by reference, so that function could modify its arguments. The syntax is as follows: <?php
Note that there's no reference sign on function call - only on
function definition. Function definition alone is enough to
correctly pass the argument by reference. In recent versions of PHP
you will get a warning saying that "Call-time pass-by-reference" is
deprecated when you use a & in The following things can be passed by reference:
Any other expression should not be passed by reference, as the result is undefined. For example, the following examples of passing by reference are invalid: <?php These requirements are for PHP 4.0.4 and later. Code Examples / Notes » language.references.passjbr
Strangely enough, I had to put "&" on the call site, but not on the function parameter list, in order to get this to work. Here is the code: <?php class TestRunner { var $passed; var $failed; var $tests; function TestRunner() { $this->passed = 0; $this->failed = 0; $this->tests = array(); } function addTest($test) { $this->tests[] = $test; } function signalFailed() { $this->failed++; } function runTests() { foreach ($this->tests as $i => $eachName) { call_user_func($eachName, &$this); } } function report() { ?> <?= count($this->tests) ?> run, <?= $this->passed ?> passed, <?= $this->failed ?> failed <?php } } function testNullStringIsEmpty($testRunner) { $testRunner->signalFailed(); } $testRunner = new TestRunner; $testRunner->addTest("testNullStringIsEmpty"); $testRunner->runTests(); $testRunner->report(); ?> Notice that testNullStringIsEmpty() does not declare that it wants a reference to a test runner, whereas on the function call site, we pass in &$this. When I run this, I get "1 run, 0 passed, 1 failed"; but when I switch the location of the & to the function parameter list, I get "1 run, 0 passed, 0 failed". This is the exact opposite to what the manual claims. I have no idea why that is. sergio santana: ssantana
Sometimes we need functions for building or modifying arrays whose elements are to be references to other variables (arrays or objects for instance). In this example, I wrote two functions 'tst' and 'tst1' that perform this task. Note how the functions are written, and how they are used. <? function tst(&$arr, $r) { // The argument '$arr' is declared to be passed by reference, // but '$r' is not; // however, in the function's body, we use a reference to // the '$r' argument array_push($arr, &$r); // Alternatively, this also could be $arr[] = &$r (in this case) } $arr0 = array(); // an empty array $arr1 = array(1,2,3); // the array to be referenced in $arr0 // Note how we call the function: tst($arr0, &$arr1); // We are passing a reference to '$arr1' in the call ! print_r($arr0); // Contains just the reference to $arr1 array_push($arr0, 5); // we add another element to $arr0 array_push($arr1, 18); // we add another element to $arr1 as well print_r($arr1); print_r($arr0); // Changes in $arr1 are reflected in $arr0 // ----------------------------------------- // A simpler way to do this: function tst1(&$arr, &$r) { // Both arguments '$arr' and '$r" are declared to be passed by // reference, // again, in the function's body, we use a reference to // the '$r' argument array_push($arr, &$r); // Alternatively, this also could be $arr[] = &$r (in this case) } $arr0 = array(); // an empty array $arr1 = array(1,2,3); // the array to be referenced in $arr0 // Note how we call the function: tst1($arr0, $arr1); // 'tst1' understands '$r' is a reference to '$arr1' echo "-------- 2nd. alternative ------------ \n"; print_r($arr0); // Contains just the reference to $arr1 array_push($arr0, 5); // we add another element to $arr0 array_push($arr1, 18); print_r($arr1); print_r($arr0); // Changes in $arr1 are reflected in $arr0 ?> // This outputs: // X-Powered-By: PHP/4.1.2 // Content-type: text/html // // Array // ( // [0] => Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // ) // // ) // Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // [3] => 18 // ) // Array // ( // [0] => Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // [3] => 18 // ) // // [1] => 5 // ) // -------- 2nd. alternative ------------ // Array // ( // [0] => Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // ) // // ) // Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // [3] => 18 // ) // Array // ( // [0] => Array // ( // [0] => 1 // [1] => 2 // [2] => 3 // [3] => 18 // ) // // [1] => 5 // ) In both cases we get the same result. I hope this is somehow useful Sergio. fdelizy
Some have noticed that reference parameters can not be assigned a default value. It's actually wrong, they can be assigned a value as the other variables, but can't have a "default reference value", for instance this code won't compile : <?php function use_reference( $someParam, &$param =& $POST ) { ... } ?> But this one will work : <?php function use_reference( $someParam, &$param = null ) ?> So here is a workaround to have a default value for reference parameters : <?php $array1 = array ( 'test', 'test2' ); function AddTo( $key, $val, &$array = null) { if ( $array == null ) { $array =& $_POST; } $array[ $key ] = $val ; } AddTo( "indirect test", "test", $array1 ); AddTo( "indirect POST test", "test" ); echo "Array 1 " ; print_r ( $array1); echo "_POST "; print_r( $_POST ); ?> And this scripts output is : Array 1 Array ( [0] => test [1] => test2 [indirect test] => test ) _POST Array ( [indirect POST test] => test ) Of course that means you can only assign default reference to globals or super globals variables. Have fun randallgirard
Side note to post by: fdelizy at unfreeze dot net function &CopyObject(&$object, &$target = null) { $obj =& $this->_getObjIs($object); if ( is_object($obj) ) { if ( ! $parent =& $this->_getObjIs($target) ) { $parent =& $obj->that; } if ( $parent && $objs =& $parent->objs ) { $copied = $obj; $objs[ $parent->index++ ] =& $copied; return $copied; } } } (directly copied from my code) Does not work. I then also tried: function &CopyObject(&$object, &$target = $default=null) { And again, same problem. PHP Error: "Parse error: parse error, unexpected '=', expecting ')' in /home/fragitup/public_html/template.php on line 457" Hence, it does not even compile. Clearly, it doesn't like that = identifier there when it is passing by reference. I cannot find a way to pass a default value except for: function &CopyObject(&$object, &$target) { ... } And: $obj->CopyObject($object, $null=null); How irritating... I'm a Perl programmer myself, and PHP is just pissing me off with all of it's limitations lol. mogmios
References don't seem to work correctly with objects. I don't seem to be able to nest objects such that each child has a reference to it's parent. The below seems to not work. class tree { var $parentNode; var $childNodes; function tree ( &$parentNode = None ) { $this->parentNode =& $parentNode; } } $parent = new tree (); $child = new tree ( $parent ); pillepop2003
PHP has a strange behavior when passing a part of an array by reference, that does not yet exist. <?php function func(&$a) { // void(); } $a['one'] =1; func($a['two']); ?> var_dump($a) returns array(2) { ["one"]=> int(1) ["two"]=> NULL } ...which seems to be not intentional! ben
Passing arrays by reference doesn't work as I expected from within call_user_func. I had: <? $arr = Array(); call_user_func(Array("ClassName","functionName"), $param1, $param2, $arr); print_r($arr); class ClassName { functionName($param1, $param2, &$arr) { $arr[0] = "apple"; $arr[1] = "banana"; print_r($arr); } } ?> I expected the output to be like: Array ( [0] => "apple" [1] => "banana" ) Array ( [0] => "apple" [1] => "banana" ) but instead it was only: Array ( [0] => "apple" [1] => "banana" ) However, when I changed the function call to plain old: <? $arr = Array(); ClassName::functionName($param1,$param2,$arr); print_r($arr); ?> Output was the expected: Array ( [0] => "apple" [1] => "banana" ) Array ( [0] => "apple" [1] => "banana" ) blistwon-php
One thing to note about passing by reference. If you plan on assigning the reference to a new variable in your function, you must use the reference operator in the function declaration as well as in the assignment. (The same holds true for classes.) ------------------------------------------------------- CODE ------------------------------------------------------- function f1(&$num) { $num++; } function f2(&$num) { $num1 = $num; $num1++; } function f3(&$num) { $num1 = &$num; $num1++; } $myNum = 0; print("Declare myNum: " . $myNum . "<br />\n"); f1($myNum); print("Pass myNum as ref 1: " . $myNum . "<br />\n"); f2($myNum); print("Pass myNum as ref 2: " . $myNum . "<br />\n"); f3($myNum); print("Pass myNum as ref 3: " . $myNum . "<br />\n"); ------------------------------------------------------- ------------------------------------------------------- OUTPUT ------------------------------------------------------- Declare myNum: 0 Pass myNum as ref 1: 1 Pass myNum as ref 2: 1 Pass myNum as ref 3: 2 ------------------------------------------------------- Hope this helps people trying to detangle any problems with pass-by-ref. riseofthethorax
Okay got it.. Why can't you guys just weed this out in the preprocessor? It's nothing more than a syntactical difference.. function foo(&$a) { $a = ($a++) * 6; } $x = 10; foo($x); print $x ; // 66 versus foo(&$x); which probably gets interpretted as pass a reference to x, then once in foo, a reference is made to a reference of x.. I suppose this would be a memory leak because $a is now a reference to $x that was declared when calling the function, is not being used by anyone.. I suppose it is like this.. we call foo(&$x); A reference (I'll call it $*) is created that links to $x. Inside foo, $a is assigned a reference to $*, which is $x. $x is modified with $a is modified.. Coming out of foo.. $a is destroyed.. reference $* still exists because it was created outside of foo.. But there is no way of referring to it because it has no name.. Is this the reason, is this why call-time pass-by-reference was deprecated?? I don't see why it was just weeded out by the preprocessor.. Nobody would make use of this artifact.. However, I need pass by reference, there is no way I can get stuff done without it.. rmarscher
Not sure if this is obvious to everyone, but if you pass an object by reference into a function and then set that variable to the reference of a new object, the initial variable outside the function is still pointing to the original object. It became obvious to me why this is the case when I made a little diagram of what's actually happening in the system's memory, but before that when I was just looking at the code, it took me a while of testing before I realized what was going on. Here's an example (I haven't tried in PHP5, but I would guess it works the same): <?php class Foo {} class Bar {} function & createBar() { return new Bar; } function setBar(& $obj) { echo get_class($obj) . "\n"; $obj = & createBar(); echo get_class($obj) . "\n"; } $test = new Foo; setBar($test); echo get_class($test); ?> Outputs: foo bar foo sony-santos
Just another workaround for default values to parameters by ref in response to mogmios, bobbykjack, andrzej, fdelizy, petruzanautico, Train Boy, and php community. <?php # php 4.3.10 # note there's no & in function declaration: function f($a = array('default value')) { $a[0] .= " processed<br/>"; echo $a[0]; } f(); # default value processed $b = 'foo'; f(array(&$b)); # foo processed (note the & here) echo $b; # foo processed ?> obscvresovl
Just a simple note... <? $num = 1; function blah(&$var) { $var++; } blah($num); echo $num; #2 ?> <? $num = 1; function blah() { $var =& $GLOBALS["num"]; $var++; } blah(); echo $num; #2 ?> Both codes do the same thing! The second code "explains" how passage of parameters by reference works. joel from purerave.com
In the example where it states: <?php foo(bar()); // Produces fatal error since PHP 5.0.5 ?> Is probably a mistake. It doesn't seem to do anything and doesn't produce any error whatsoever. bobbykjack
In response to mogmios, I think the problem may be similar to one I experienced. It seems that reference parameters cannot be assigned a default value and, taking into account what can be passed by reference, the only way I could achieve this functionality was with the following code: <?php class tree { var $parentNode; var $childNodes; function tree(&$parentNode) { if ($parentNode) $this->parentNode =& $parentNode; } } $null = ''; $parent = new tree($null); $child = new tree($parent); echo '<pre>';print_r($child);echo '</pre>'; ?> It's a nasty hack, but I cannot find a good alternative. The above script produces the following output: tree Object ( [parentNode] => tree Object ( [parentNode] => [childNodes] => ) [childNodes] => ) which is sparse, but technically correct (you can test this by expanding the implementation and making use of $childNodes). Note that this was tested under PHP 4.3.10 and PHP 5 has almost certainly changed this behaviour. chuckie
If you intend to pass a copy of an object to a function, then you should use 'clone' to create a copy explicity. In PHP5, objects appear to always be passed by reference (unlike PHP4), but this is not strictly true. The way I think of it is that if you use '=&' (or you explicitly pass to a function by reference) the variable behaves like a C++ reference, except that you can re-assign the reference to something else (which is not possible in C++). When you use '=' with an object, it is more like you are dealing with a pointer (if you think in this way, the '->' access element through pointer operator has the same behaviour in C++). <?php class z { public $var = ''; } function f(&$obj1, $obj2, $obj3, $obj4) { $obj1->var = null; $obj2->var = null; $obj3 = new z(); $obj3->var = null; $obj4 = clone $obj4; $obj4->var = null; } $a = new z(); $b = new z(); $c = new z(); $d = new z(); f($a, $b, $c, $d); var_dump($a); // object(z)#1 (1) { ["var"] => NULL } var_dump($b); // object(z)#2 (1) { ["var"] => NULL } var_dump($c); // object(z)#3 (1) { ["var"] => string(0) "" } var_dump($d); // object(z)#4 (1) { ["var"] => string(0) "" } ?> Stephen 08-Jul-2007 04:54 jcastromail at yahoo dot es stated: **** in php 5.2.0 for classes $obj1 = $obj2; is equal to $obj1 = &$obj2;" **** However, that is not completely true. While both = and =& will make a variable refer to the same object as the variable being assigned to it, the explicit reference assignment (=&) will keep the two variables joined to each other, whereas the assignment reference (=) will make the assigned variable an independent pointer to the object. An example should make this clearer: <?php class z { public $var = ''; } $a = new z(); $b =& $a; $c = $a; $a->var = null; var_dump($a); print ' '; var_dump($b); print ' '; var_dump($c); print ' '; $a = 2; var_dump($a); print ' '; var_dump($b); print ' '; var_dump($c); print ' '; ?> This outputs: object(z)#1 (1) { ["var"]=> NULL } object(z)#1 (1) { ["var"]=> NULL } object(z)#1 (1) { ["var"]=> NULL } int(2) int(2) object(z)#1 (1) { ["var"]=> NULL } So although all 3 variables reflect changes in the object, if you reassign one of the variables that were previously joined by reference to a different value, BOTH of those variables will adopt the new value. Perhaps this is because =& statements join the 2 variable names in the symbol table, whereas = statements applied to objects simply create a new independent entry in the symbol table that simply points to the same location as other entries. I don't know for sure - I don't think this behavior is documented in the PHP manual, so perhaps somebody with more knowledge of PHP's internals can clarify what is going on. michael muryn
I was tempted to refactor bobbykjack's sample by 1st changing $null to null (which I think is better semantiquely than '' even if equivalent). However I noticed that this could produce the same error (well behavior that lead to think there is an error) that make me come here in the first place. Just follow the code and notice how I reuse the $null and then set the parent node to the grand_parent. Yes, I have not done it by reference intentionnally. However, the solution will either be to keep the if and never reference the $null else always set a variable by reference when it need to. But there is case where you want to affect a variable directly and do not forget that it reference the same memory than maybe a lot of variable. However, always be aware that if you pass by reference. If you modify the variable (and not affect a new reference to the variable name) that you are affecting all variable that reference the same data. <?php /** * To test the discussion at http://www.php.net/manual/en/language.references.pass.php */ class tree { var $parentNode; var $childNodes; function tree(&$parentNode) { //if (isset($parentNode)) //{ $this->parentNode =& $parentNode; //} } } $null = null; $parent = new tree($null); $child = new tree($parent); echo '<pre>'; print_r($child); echo '</pre>'; $grand_parent = new tree($null); $parent->parentNode = $grand_parent; // if I was doing it by reference here, all would work fine echo '<pre>'; print_r($null); echo '</pre>'; ?> doron
I found that f1(&$par=0); behaves differently in 5.2.4 thatn in 5.0.4 in 5.2.4 the par is assined to zero AFTER function call and thus, par is allways ZERO ! in 5.0.4 in is INITIALIZED to zero, BEFORE function call! php
Ever been in a situation where you have to write: <?php $t = 1 ; $x =& $t ; ?> or <?php $t = 1 ; f( $t ) ; ?> because you cannot pass constants by value. The function <?php function& pclone( $v ) { return( $v ) ; } ?> lets you get ridd of the temporary variable. You can write: <?php $x =& pclone( 1 ) ; ?> or <?php f( pclone( 1 ) ) ; ?> Alternatively you can use the other alternative ;-) train boy
Be nice to put the argument about whether reference parameters can have default values or not to rest. First, a little reminder from the section on function arguments, "The default value must be a constant expression, not (for example) a variable, a class member or a function call". This means that you can't assign a default value that references another variable, to the argument, but you should be able to assign a constant such as NULL. Having said that, the following code works pretty much as expected in both PHP 5.1.4 and 5.1.2: function foo($Inv, &$Ptr=NULL) { echo "In foo, Inv = ".$Inv.", Ptr = ".$Ptr." \n"; if (isset($Ptr)) $Ptr = "set"; } $Val = "unset"; foo(1); echo "Val = ".$Val." \n"; // outputs "unset" foo(2, $Val); echo "Val = ".$Val." \n"; // outputs "set" However PHP 4.2.2 is a whole 'nother story. The function prototype gets a parse error right at the equal sign. So it would appear that both camps are right, depending on which version of PHP you're using. If you can control which one you run under and can pick one the V5 versions, it looks like you're in good shape. If you have one of the V4 versions, you're probably not a happy camper. andrzej dot martynowicz
As bobbykjack at hotmail dot com noticed 'reference parameters cannot be assigned a default value'. Yes, but I've found a workaround to this problem. The idea is to create object that will hold the reference and pass the instance of this 'Reference object' as a desired parameter (which is not preceded by & operator). One can then extract the refernce from passed 'Reference object'. See below how this works.. <? class Ref { var $ref; function Ref( &$reference ) { $this->ref = &$reference; } } function myFunc( $param = null ) { $param = &$param->ref; // notice & operator here $param = 'php'; } $foo = 'foo'; myFunc( new Ref( $foo )); echo $foo; // outputs: php ?> petruzanautico
Actually there is a way to give a default value to a reference argument, if you need it badly. It fires a warning, but you can prefix the function call with an @. Of course, you cannot pass literals by reference. <?php function byref( &$ref ) { if( !isset( $ref ) ) $ref = "default value"; print $ref; // default or given value? who knows! } $hi = "some value "; byref( $hi ); // works fine @byref(); // missing argument warning, but works byref( "hey!" ); // this will raise a fatal error ?> 05-nov-2005 02:16
<?php function stripzeros($var) { if($var < '10') { $var = substr($var, 1); } return $var; } $day = '04'; $day = stripzeros($day); echo $day."\n"; // 4 -- as expected $day = '04'; stripzeros(&$day); echo $day."\n"; // 4 -- another way to do the above, $day is altered $day = '04'; $newday = stripzeros(&$day); echo $newday."\n"; // 4 echo $day."\n"; // 4 here the reference effectively set $day = $newday $day = '04'; $newday = stripzeros($day); echo $newday."\n"; // 4 echo $day."\n"; // 04 -- with this method you can apply the function to the string and keep the original string ?> andy morris
"jbr at diasparsoftware dot com" spotted something strange when calling call_user_func() where the user function was declared as expecting a reference and seemed to contradict the manual. Actually, the manual says that parameters to call_user_func() aren't passed as references, which I take to mean that if you declare your user function as expecting a parameter by reference, this won't work. In jbr's test code, this doesn't work - so this part isn't a surprise. However, the manual goes on to say that the way out of the problem is to use call_user_func_array() and pass the parameter by reference in an array instead. For example, see this code (from the call_user_func manual page)... <?php function increment(&$var) { $var++; } $a = 0; call_user_func('increment', $a); echo $a; // 0 call_user_func_array('increment', array(&$a)); // You can use this instead echo $a; // 1 // I've added this call to illustrate something strange... call_user_func('increment', &$a); // but look - just adding & works too! echo $a; // 2 ?> The strange thing that "jbr at diasparsoftware dot com" discovered is that you don't actually have to use call_user_func_array() at all, so long as you explicitly specify the variable to be passed by reference when actually calling your function via call_user_func(). Is this just a lucky coincidence? I'm still hunting in the manual to see whether this really is just lucky, or a simpler correct way to do it. |