引用传递

可以将一个变量通过引用传递给函数,这样该函数就可以修改其参数的值。语法如下:

<?php
function foo(&$var)
{
    
$var++;
}

$a=5;
foo($a);
// $a is 6 here
?>

注意在函数调用时没有引用符号――只有函数定义中有。光是函数定义就足够使参数通过引用来正确传递了。在最近版本的 PHP 中如果把 & 用在 foo(&$a); 中会得到一条警告说“Call-time pass-by-reference”已经过时了。

以下内容可以通过引用传递:

  • 变量,例如 foo($a)

  • New 语句,例如 foo(new foobar())

  • 从函数中返回的引用,例如:

    <?php
    function &bar()
    {
        
    $a = 5;
        return
    $a;
    }
    foo(bar());
    ?>

    详细解释见引用返回

任何其它表达式都不能通过引用传递,结果未定义。例如下面引用传递的例子是无效的:

<?php
function bar() // Note the missing &
{
    
$a = 5;
    return
$a;
}
foo(bar()); // 自 PHP 5.1.0 起导致致命错误
foo($a = 5) // 表达式,不是变量
foo(5) // 导致致命错误
?>

这些条件是 PHP 4.0.4 以及以后版本有的。


add a note add a note User Contributed Notes
Train Boy
07-Oct-2006 05:37
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."<br>\n";
if (isset($Ptr)) $Ptr = "set";
}

$Val = "unset";

foo(1);
echo "Val = ".$Val."<br>\n";  // outputs "unset"
foo(2, $Val);
echo "Val = ".$Val."<br>\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.
Train Boy
06-Oct-2006 01:35
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."<br>\n";
if (isset($Ptr)) $Ptr = "set";
}

$Val = "unset";

foo(1);
echo "Val = ".$Val."<br>\n";  // outputs "unset"
foo(2, $Val);
echo "Val = ".$Val."<br>\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.
petruzanautico at yahoo dot com dot ar
26-Sep-2006 04:30
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
?>
randallgirard at hotmail dot com
24-Aug-2006 12:32
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.
fdelizy at unfreeze dot net
13-Aug-2006 12:32
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
05-Nov-2005 10: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

?>
Michael Muryn
27-Sep-2005 06:24
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>';
?>
rmarscher
16-Sep-2005 06:42
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
andrzej dot martynowicz at gmail dot com
11-Sep-2005 03:15
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
?>
bobbykjack at hotmail dot com
03-Sep-2005 06:44
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.
Andy Morris
27-May-2005 05:10
"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.
mogmios at mlug dot missouri dot edu
12-May-2005 01:33
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 at yahoo dot de
14-Feb-2005 12:08
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!
obscvresovl at NOSPAM dot hotmail dot com
26-Dec-2004 02:51
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.
jbr at diasparsoftware dot com
29-Sep-2004 12:46
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() {
      
?>
        <p><?= count($this->tests) ?> run, <?= $this->passed ?> passed, <?= $this->failed ?> failed</p>       
       <?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 at tlaloc dot imta dot mx
10-Sep-2004 11:25
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 ------------ <br>\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.
ben at mekhaye dot net
02-Sep-2004 05:27
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" )
php at meKILLTHIStatoandthisols dot org
10-Jun-2004 03:33
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 ;-)
blistwon-php at designfridge dot com
18-Jan-2004 10:33
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.