PHP 8.4.0 RC4 available for testing

引用做什么

使用引用执行有三种基本操作: 引用赋值传引用引用返回 。 本节介绍这些操作,并提供进一步阅读的链接。

引用赋值

PHP 的引用允许用两个变量来指向同一个内容。意思是,当这样做时:

<?php

$a
=& $b;

?>
这意味着 $a$b 指向了同一个变量。

注意:

$a$b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a$b 指向了同一个地方。

注意:

如果对一个未定义的变量进行引用赋值、引用参数传递或引用返回,则会自动创建该变量。

示例 #1 对未定义的变量使用引用

<?php

function foo(&$var) {}

foo($a); // 创建 $a 并赋值为 null

$b = array();
foo($b['b']);
var_dump(array_key_exists('b', $b)); // bool(true)

$c = new stdClass();
foo($c->d);
var_dump(property_exists($c, 'd')); // bool(true)

?>

同样的语法可以用在返回引用的函数中:

<?php

$foo
=& find_var($bar);

?>

在函数内使用相同的语法 通过引用返回将会产生错误, 就像将它与 new 运算符的结果一起使用一样。 正如 对象和引用 中所述, 尽管对象是通过指针传递的,但是它们与引用不同。

警告

如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点。

示例 #2 在函数内引用全局变量

<?php

$var1
= "Example variable";
$var2 = "";

function
global_references($use_globals)
{
global
$var1, $var2;

if (!
$use_globals) {
$var2 =& $var1; // 仅在函数内部可见
} else {
$GLOBALS["var2"] =& $var1; // 全局上下文可见
}
}

global_references(false);
echo
"var2 is set to '$var2'\n"; // var2 is set to ''

global_references(true);
echo
"var2 is set to '$var2'\n"; // var2 is set to 'Example variable'

?>
global $var; 当成是 $var =& $GLOBALS['var']; 的简写。从而将其它引用赋给 $var 只改变了本地变量的引用。

注意:

如果在 foreach 语句中给一个具有引用的变量赋值,被引用的对象也被改变。

示例 #3 引用与 foreach 语句

<?php

$ref
= 0;
$row =& $ref;

foreach (array(
1, 2, 3) as $row) {
// 做点什么
}

echo
$ref; // 3 - 迭代数组的最后一个元素

?>

虽然不是严格意义上的引用赋值,但使用语言构造 array() 创建的表达式也可以通过在要添加的数组元素加上 & 前缀来表现为这样。例如:

<?php

$a
= 1;
$b = array(2, 3);

$arr = array(&$a, &$b[0], &$b[1]);
$arr[0]++;
$arr[1]++;
$arr[2]++;
/* $a == 2, $b == array(3, 4); */

?>

但是请注意,数组内的引用具有潜在的危险。 在右侧引用正常的赋值(不是通过引用)不会将左侧变为引用, 但是数组内部的引用会保留在这些正常赋值中。 这也适用于调用函数时按值传递数组的情况。例如:

<?php

/* 标量变量赋值 */
$a = 1;
$b =& $a;
$c = $b;
$c = 7; // $c 不是引用;不会改变 $a 或者 $b

/* 数组变量赋值 */
$arr = array(1);
$a =& $arr[0]; // $a 和 $arr[0] 设置了相同的引用
$arr2 = $arr; // 不是引用赋值!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* 尽量 $arr 不是引用,但是它的内容已经更改! */

?>
换句话说,数组的引用行为是在逐个元素的基础上定义; 单个元素的引用行为与数组容器的引用状态分离。

传引用

引用做的第二件事是用引用传递变量。这是通过在函数内建立一个本地变量并且该变量在呼叫范围内引用了同一个内容来实现的。例如:

<?php

function foo(&$var)
{
$var++;
}

$a=5;
foo($a);

?>
将使 $a 变成 6。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容。更多详细解释见引用传递

引用返回

引用做的第三件事是引用返回

添加备注

用户贡献的备注 23 notes

up
69
ladoo at gmx dot at
19 years ago
I ran into something when using an expanded version of the example of pbaltz at NO_SPAM dot cs dot NO_SPAM dot wisc dot edu below.
This could be somewhat confusing although it is perfectly clear if you have read the manual carfully. It makes the fact that references always point to the content of a variable perfectly clear (at least to me).

<?php
$a
= 1;
$c = 2;
$b =& $a; // $b points to 1
$a =& $c; // $a points now to 2, but $b still to 1;
echo $a, " ", $b;
// Output: 2 1
?>
up
3
dexant9t at gmail dot com
3 years ago
It matters if you are playing with a reference or with a value

Here we are working with values so working on a reference updates original variable too;

$a = 1;
$c = 22;

$b = & $a;
echo "$a, $b"; //Output: 1, 1

$b++;
echo "$a, $b";//Output: 2, 2 both values are updated

$b = 10;
echo "$a, $b";//Output: 10, 10 both values are updated

$b =$c; //This assigns value 2 to $b which also updates $a
echo "$a, $b";//Output: 22, 22

But, if instead of $b=$c you do
$b = &$c; //Only value of $b is updated, $a still points to 10, $b serves now reference to variable $c

echo "$a, $b"//Output: 10, 22
up
36
Hlavac
17 years ago
Watch out for this:

foreach ($somearray as &$i) {
// update some $i...
}
...
foreach ($somearray as $i) {
// last element of $somearray is mysteriously overwritten!
}

Problem is $i contians reference to last element of $somearray after the first foreach, and the second foreach happily assigns to it!
up
26
elrah [] polyptych [dot] com
13 years ago
It appears that references can have side-effects. Below are two examples. Both are simply copying one array to another. In the second example, a reference is made to a value in the first array before the copy. In the first example the value at index 0 points to two separate memory locations. In the second example, the value at index 0 points to the same memory location.

I won't say this is a bug, because I don't know what the designed behavior of PHP is, but I don't think ANY developers would expect this behavior, so look out.

An example of where this could cause problems is if you do an array copy in a script and expect on type of behavior, but then later add a reference to a value in the array earlier in the script, and then find that the array copy behavior has unexpectedly changed.

<?php
// Example one
$arr1 = array(1);
echo
"\nbefore:\n";
echo
"\$arr1[0] == {$arr1[0]}\n";
$arr2 = $arr1;
$arr2[0]++;
echo
"\nafter:\n";
echo
"\$arr1[0] == {$arr1[0]}\n";
echo
"\$arr2[0] == {$arr2[0]}\n";

// Example two
$arr3 = array(1);
$a =& $arr3[0];
echo
"\nbefore:\n";
echo
"\$a == $a\n";
echo
"\$arr3[0] == {$arr3[0]}\n";
$arr4 = $arr3;
$arr4[0]++;
echo
"\nafter:\n";
echo
"\$a == $a\n";
echo
"\$arr3[0] == {$arr3[0]}\n";
echo
"\$arr4[0] == {$arr4[0]}\n";
?>
up
11
amp at gmx dot info
17 years ago
Something that might not be obvious on the first look:
If you want to cycle through an array with references, you must not use a simple value assigning foreach control structure. You have to use an extended key-value assigning foreach or a for control structure.

A simple value assigning foreach control structure produces a copy of an object or value. The following code

$v1=0;
$arrV=array(&$v1,&$v1);
foreach ($arrV as $v)
{
$v1++;
echo $v."\n";
}

yields

0
1

which means $v in foreach is not a reference to $v1 but a copy of the object the actual element in the array was referencing to.

The codes

$v1=0;
$arrV=array(&$v1,&$v1);
foreach ($arrV as $k=>$v)
{
$v1++;
echo $arrV[$k]."\n";
}

and

$v1=0;
$arrV=array(&$v1,&$v1);
$c=count($arrV);
for ($i=0; $i<$c;$i++)
{
$v1++;
echo $arrV[$i]."\n";
}

both yield

1
2

and therefor cycle through the original objects (both $v1), which is, in terms of our aim, what we have been looking for.

(tested with php 4.1.3)
up
4
Anonymous
8 years ago
to reply to ' elrah [] polyptych [dot] com ', one thing to keep in mind is that array (or similar large data holders) are by default passed by reference. So the behaviour is not side effect. And for array copy and passing array inside function always done by 'pass by reference'...
up
9
nay at woodcraftsrus dot com
13 years ago
in PHP you don't really need pointer anymore if you want to share an object across your program

<?php
class foo{
protected
$name;
function
__construct($str){
$this->name = $str;
}
function
__toString(){
return
'my name is "'. $this->name .'" and I live in "' . __CLASS__ . '".' . "\n";
}
function
setName($str){
$this->name = $str;
}
}

class
MasterOne{
protected
$foo;
function
__construct($f){
$this->foo = $f;
}
function
__toString(){
return
'Master: ' . __CLASS__ . ' | foo: ' . $this->foo . "\n";
}
function
setFooName($str){
$this->foo->setName( $str );
}
}

class
MasterTwo{
protected
$foo;
function
__construct($f){
$this->foo = $f;
}
function
__toString(){
return
'Master: ' . __CLASS__ . ' | foo: ' . $this->foo . "\n";
}
function
setFooName($str){
$this->foo->setName( $str );
}
}

$bar = new foo('bar');

print(
"\n");
print(
"Only Created \$bar and printing \$bar\n");
print(
$bar );

print(
"\n");
print(
"Now \$baz is referenced to \$bar and printing \$bar and \$baz\n");
$baz =& $bar;
print(
$bar );

print(
"\n");
print(
"Now Creating MasterOne and Two and passing \$bar to both constructors\n");
$m1 = new MasterOne( $bar );
$m2 = new MasterTwo( $bar );
print(
$m1 );
print(
$m2 );

print(
"\n");
print(
"Now changing value of \$bar and printing \$bar and \$baz\n");
$bar->setName('baz');
print(
$bar );
print(
$baz );

print(
"\n");
print(
"Now printing again MasterOne and Two\n");
print(
$m1 );
print(
$m2 );

print(
"\n");
print(
"Now changing MasterTwo's foo name and printing again MasterOne and Two\n");
$m2->setFooName( 'MasterTwo\'s Foo' );
print(
$m1 );
print(
$m2 );

print(
"Also printing \$bar and \$baz\n");
print(
$bar );
print(
$baz );
?>
up
5
charles at org oo dot com
17 years ago
points to post below me.
When you're doing the references with loops, you need to unset($var).

for example
<?php
foreach($var as &$value)
{
...
}
unset(
$value);
?>
up
4
Oddant
11 years ago
About the example on array references.
I think this should be written in the array chapter as well.
Indeed if you are new to programming language in some way, you should beware that arrays are pointers to a vector of Byte(s).

<?php $arr = array(1); ?>
$arr here contains a reference to which the array is located.
Writing :
<?php echo $arr[0]; ?>
dereferences the array to access its very first element.

Now something that you should also be aware of (even you are not new to programming languages) is that PHP use references to contains the different values of an array. And that makes sense because the type of the elements of a PHP array can be different.

Consider the following example :

<?php

$arr
= array(1, 'test');

$point_to_test =& $arr[1];

$new_ref = 'new';

$arr[1] =& $new_ref;

echo
$arr[1]; // echo 'new';
echo $point_to_test; // echo 'test' ! (still pointed somewhere in the memory)

?>
up
3
php.devel at homelinkcs dot com
19 years ago
In reply to lars at riisgaardribe dot dk,

When a variable is copied, a reference is used internally until the copy is modified. Therefore you shouldn't use references at all in your situation as it doesn't save any memory usage and increases the chance of logic bugs, as you discoved.
up
3
dovbysh at gmail dot com
17 years ago
Solution to post "php at hood dot id dot au 04-Mar-2007 10:56":

<?php
$a1
= array('a'=>'a');
$a2 = array('a'=>'b');

foreach (
$a1 as $k=>&$v)
$v = 'x';

echo
$a1['a']; // will echo x

unset($GLOBALS['v']);

foreach (
$a2 as $k=>$v)
{}

echo
$a1['a']; // will echo x

?>
up
4
Amaroq
14 years ago
I think a correction to my last post is in order.

When there is a constructor, the strange behavior mentioned in my last post doesn't occur. My guess is that php was treating reftest() as a constructor (maybe because it was the first function?) and running it upon instantiation.

<?php
class reftest
{
public
$a = 1;
public
$c = 1;

public function
__construct()
{
return
0;
}

public function
reftest()
{
$b =& $this->a;
$b++;
}

public function
reftest2()
{
$d =& $this->c;
$d++;
}
}

$reference = new reftest();

$reference->reftest();
$reference->reftest2();

echo
$reference->a; //Echoes 2.
echo $reference->c; //Echoes 2.
?>
up
2
akinaslan at gmail dot com
13 years ago
In this example class name is different from its first function and however there is no construction function. In the end as you guess "a" and "c" are equal. So if there is no construction function at same time class and its first function names are the same, "a" and "c" doesn't equal forever. In my opinion php doesn't seek any function for the construction as long as their names differ from each others.

<?php
class reftest_new
{
public
$a = 1;
public
$c = 1;

public function
reftest()
{
$b =& $this->a;
$b++;
}

public function
reftest2()
{
$d =& $this->c;
$d++;
}
}

$reference = new reftest_new();

$reference->reftest();
$reference->reftest2();

echo
$reference->a; //Echoes 2.
echo $reference->c; //Echoes 2.
?>
up
2
Amaroq
16 years ago
The order in which you reference your variables matters.

<?php
$a1
= "One";
$a2 = "Two";
$b1 = "Three";
$b2 = "Four";

$b1 =& $a1;
$a2 =& $b2;

echo
$a1; //Echoes "One"
echo $b1; //Echoes "One"

echo $a2; //Echoes "Four"
echo $b2; //Echoes "Four"
?>
up
2
Drewseph
16 years ago
If you set a variable before passing it to a function that takes a variable as a reference, it is much harder (if not impossible) to edit the variable within the function.

Example:
<?php
function foo(&$bar) {
$bar = "hello\n";
}

foo($unset);
echo(
$unset);
foo($set = "set\n");
echo(
$set);

?>

Output:
hello
set

It baffles me, but there you have it.
up
1
dnhuff at acm dot org
16 years ago
In reply to Drewseph using foo($a = 'set'); where $a is a reference formal parameter.

$a = 'set' is an expression. Expressions cannot be passed by reference, don't you just hate that, I do. If you turn on error reporting for E_NOTICE, you will be told about it.

Resolution: $a = 'set'; foo($a); this does what you want.
up
1
firespade at gmail dot com
17 years ago
Here's a good little example of referencing. It was the best way for me to understand, hopefully it can help others.

$b = 2;
$a =& $b;
$c = $a;
echo $c;

// Then... $c = 2
up
0
Amaroq
14 years ago
When using references in a class, you can reference $this-> variables.

<?php
class reftest
{
public
$a = 1;
public
$c = 1;

public function
reftest()
{
$b =& $this->a;
$b = 2;
}

public function
reftest2()
{
$d =& $this->c;
$d++;
}
}

$reference = new reftest();

$reference->reftest();
$reference->reftest2();

echo
$reference->a; //Echoes 2.
echo $reference->c; //Echoes 2.
?>

However, this doesn't appear to be completely trustworthy. In some cases, it can act strangely.

<?php
class reftest
{
public
$a = 1;
public
$c = 1;

public function
reftest()
{
$b =& $this->a;
$b++;
}

public function
reftest2()
{
$d =& $this->c;
$d++;
}
}

$reference = new reftest();

$reference->reftest();
$reference->reftest2();

echo
$reference->a; //Echoes 3.
echo $reference->c; //Echoes 2.
?>

In this second code block, I've changed reftest() so that $b increments instead of just gets changed to 2. Somehow, it winds up equaling 3 instead of 2 as it should.
up
0
php at hood dot id dot au
17 years ago
I discovered something today using references in a foreach

<?php
$a1
= array('a'=>'a');
$a2 = array('a'=>'b');

foreach (
$a1 as $k=>&$v)
$v = 'x';

echo
$a1['a']; // will echo x

foreach ($a2 as $k=>$v)
{}

echo
$a1['a']; // will echo b (!)
?>

After reading the manual this looks like it is meant to happen. But it confused me for a few days!

(The solution I used was to turn the second foreach into a reference too)
up
-2
strata_ranger at hotmail dot com
15 years ago
An interesting if offbeat use for references: Creating an array with an arbitrary number of dimensions.

For example, a function that takes the result set from a database and produces a multidimensional array keyed according to one (or more) columns, which might be useful if you want your result set to be accessible in a hierarchial manner, or even if you just want your results keyed by the values of each row's primary/unique key fields.

<?php
function array_key_by($data, $keys, $dupl = false)
/*
* $data - Multidimensional array to be keyed
* $keys - List containing the index/key(s) to use.
* $dupl - How to handle rows containing the same values. TRUE stores it as an Array, FALSE overwrites the previous row.
*
* Returns a multidimensional array indexed by $keys, or NULL if error.
* The number of dimensions is equal to the number of $keys provided (+1 if $dupl=TRUE).
*/
{
// Sanity check
if (!is_array($data)) return null;

// Allow passing single key as a scalar
if (is_string($keys) or is_integer($keys)) $keys = Array($keys);
elseif (!
is_array($keys)) return null;

// Our output array
$out = Array();

// Loop through each row of our input $data
foreach($data as $cx => $row) if (is_array($row))
{

// Loop through our $keys
foreach($keys as $key)
{
$value = $row[$key];

if (!isset(
$last)) // First $key only
{
if (!isset(
$out[$value])) $out[$value] = Array();
$last =& $out; // Bind $last to $out
}
else
// Second and subsequent $key....
{
if (!isset(
$last[$value])) $last[$value] = Array();
}

// Bind $last to one dimension 'deeper'.
// First lap: was &$out, now &$out[...]
// Second lap: was &$out[...], now &$out[...][...]
// Third lap: was &$out[...][...], now &$out[...][...][...]
// (etc.)
$last =& $last[$value];
}

if (isset(
$last))
{
// At this point, copy the $row into our output array
if ($dupl) $last[$cx] = $row; // Keep previous
else $last = $row; // Overwrite previous
}
unset(
$last); // Break the reference
}
else return
NULL;

// Done
return $out;
}

// A sample result set to test the function with
$data = Array(Array('name' => 'row 1', 'foo' => 'foo_a', 'bar' => 'bar_a', 'baz' => 'baz_a'),
Array(
'name' => 'row 2', 'foo' => 'foo_a', 'bar' => 'bar_a', 'baz' => 'baz_b'),
Array(
'name' => 'row 3', 'foo' => 'foo_a', 'bar' => 'bar_b', 'baz' => 'baz_c'),
Array(
'name' => 'row 4', 'foo' => 'foo_b', 'bar' => 'bar_c', 'baz' => 'baz_d')
);

// First, let's key it by one column (result: two-dimensional array)
print_r(array_key_by($data, 'baz'));

// Or, key it by two columns (result: 3-dimensional array)
print_r(array_key_by($data, Array('baz', 'bar')));

// We could also key it by three columns (result: 4-dimensional array)
print_r(array_key_by($data, Array('baz', 'bar', 'foo')));

?>
up
-2
joachim at lous dot org
21 years ago
So to make a by-reference setter function, you need to specify reference semantics _both_ in the parameter list _and_ the assignment, like this:

class foo{
var $bar;
function setBar(&$newBar){
$this->bar =& newBar;
}
}

Forget any of the two '&'s, and $foo->bar will end up being a copy after the call to setBar.
up
-3
butshuti at smartrwanda dot org
11 years ago
This appears to be the hidden behavior: When a class function has the same name as the class, it seems to be implicitly called when an object of the class is created.
For instance, you may take a look at the naming of the function "reftest()": it is in the class "reftest". The behavior can be tested as follows:

<?php
class reftest
{
public
$a = 1;
public
$c = 1;

public function
reftest1()
{
$b =& $this->a;
$b++;
}

public function
reftest2()
{
$d =& $this->c;
$d++;
}

public function
reftest()
{
echo
"REFTEST() called here!\n";
}
}

$reference = new reftest();
/*You must notice the above will also implicitly call reference->reftest()*/

$reference->reftest1();
$reference->reftest2();

echo
$reference->a."\n"; //Echoes 2, not 3 as previously noticed.
echo $reference->c."\n"; //Echoes 2.
?>

The above outputs:

REFTEST() called here!
2
2

Notice that reftest() appears to be called (though no explicit call to it was made)!
up
-5
admin at torntech dot com
11 years ago
Something that has not been discussed so far is reference of a reference.
I needed a quick and dirty method of aliasing incorrect naming until a proper rewrite could be done.
Hope this saves someone else the time of testing since it was not covered in the Does/Are/Are Not pages.
Far from best practice, but it worked.

<?php
$a
= 0;

$b =& $a;
$a =& $b;

$a = 5;
echo
$a . ', ' . $b;
//ouputs: 5,5

echo ' | ';

$b = 6;
echo
$a . ',' . $b;
//outputs: 6,6

echo ' | ';
unset(
$a );
echo
$a . ', ' . $b;

//outputs: , 6

class Product {

public
$id;
private
$productid;

public function
__construct( $id = null ) {
$this->id =& $this->productid;
$this->productid =& $this->id;
$this->id = $id;
}

public function
getProductId() {
return
$this->productid;
}

}

echo
' | ';

$Product = new Product( 1 );
echo
$Product->id . ', ' . $Product->getProductId();
//outputs 1, 1
$Product->id = 2;
echo
' | ';
echo
$Product->id . ', ' . $Product->getProductId();
//outputs 2, 2
$Product->id = null;
echo
' | ';
echo
$Product->id . ', ' . $Product->getProductId();
//outouts ,
To Top