Передача по ссылке

Вы можете передавать переменные в функцию по ссылке, и функция сможет изменять свои аргументы. Синтаксис таков:

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

$a=5;
foo($a);
// $a здесь равно 6
?>

Заметьте, что в вызове функции отсутствует знак ссылки - он есть только в определении функции. Этого достаточно для корректной передачи аргументов по ссылке.

По ссылке можно передавать:

  • Переменные, например foo($a)

  • Оператор new, например foo(new foobar())

  • Ссылки, возвращаемые функцией, например:

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

    См. также объяснение возвращения по ссылке.

Любое другое выражение не должно передаваться по ссылке, так как результат не определён. Например, следующая передача по ссылке является неправильной:

<?php
function bar() // Операция & отсутствует
{
  
$a = 5;
   return
$a;
}
foo(bar());

foo($a = 5); // Выражение, а не переменная
foo(5); // Константа, а не переменная
?>

Эти требования для PHP 4.0.4 и позже.



Передача по ссылке
04-Nov-2005 06: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
26-Sep-2005 03: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 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
15-Sep-2005 03: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
10-Sep-2005 12: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
02-Sep-2005 03: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 02: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
11-May-2005 10: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
13-Feb-2005 08: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
25-Dec-2004 10: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
28-Sep-2004 09: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 08: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
01-Sep-2004 02: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
09-Jun-2004 12: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
17-Jan-2004 06: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.

<Чем ссылки не являютсяВозвращение по ссылке>
 Last updated: Tue, 15 Nov 2005