PHP变量是按值传递还是引用传递?

311

PHP变量是按值传递还是引用传递?


2
在http://codeexpertz.com/blog/php/pass-value-and-pass-reference-php-example中有很好的解释。请参考。 - Premlatha
16个回答

375
根据PHP文档,函数参数默认是按值传递的(所以如果函数内部的参数值被更改,它不会在函数外部更改)。要允许函数修改其参数,必须通过引用传递它们。 为了让一个函数的参数始终被引用传递,请在函数定义中的参数名称前加上一个 & 符号。
<?php
function add_some_extra(&$string)
{
    $string .= 'and something extra.';
}

$str = 'This is a string, ';
add_some_extra($str);
echo $str;    // outputs 'This is a string, and something extra.'
?>

5
我也有这个疑问(新手)- 所以为了明确,如果我不使用引用,这将与 $str = add_some_extra($str); 相同,对吗?那么这样做的真正附加值是什么? - Obmerk Kronen
9
如果你不使用&符号来表示“传递引用”,它将不等同于原来的意思。注意这个函数没有返回语句,因此在你的情况下,$str将被赋值为null。 - The Unknown Dev
2
我在想传递引用是否有一些性能上的好处?我正在传递一个大数组,默认情况下它是按值传递的。因此,如果我使用按引用调用,那么在性能方面会有一些好处吗?(在整个过程中保持数组值不变) - Choxx
5
真正的好处在于你可以操纵/返回多个值。你还可以通过引用传递变量并仍然让函数返回某些东西。 - Martin Schneider
3
@Choxx 没有必要通过引用来传递数组以提高性能。PHP引擎使用写时复制 - 它不会在不可见的作用域内物理复制数组,除非在其中一个可见的作用域中被修改。然后它会复制一份,使得修改只适用于一个地方。 - bdsl
显示剩余2条评论

97
在PHP中,默认情况下,将对象作为引用传递给一个新的对象。
看这个例子:
class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue($obj)
  {
   $obj->abc = 30;
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 30

现在看看这个:

class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue($obj)
  {
    $obj = new Y();
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 10 not 20 same as java does.

现在看这个:

class X {
  var $abc = 10; 
}

class Y {

  var $abc = 20; 
  function changeValue(&$obj)
  {
    $obj = new Y();
  }
}

$x = new X();
$y = new Y();

echo $x->abc; //outputs 10
$y->changeValue($x);
echo $x->abc; //outputs 20 not possible in java.

我希望你能理解这个。


4
  1. 在函数中传递对象的引用值,对该对象所做的更改将反映在指向相同对象的所有其他引用上。
  2. 再次传递对象引用的值到函数中,函数将更改引用值指向一个新对象,旧对象保持不变。
  3. 将对象引用的值的引用传递到函数中,因此函数中的更改作用于同一对象。
- s-hunter
1
这个例子过于复杂。$y是不必要的 - function changeValue 中没有任何需要它在 class Y 内部的东西,因此将两个不相关的概念缠绕在一起。我会将 function changeValue 移动到外部作用域,就在 $x = new X(); 的上面。删除所有对 $y 的引用。现在更容易看出前两个示例保持 $x 不变,第三个示例将其内容更改为 new Y()。如果您转储了 $xtype,则更容易看到这一点 - XY 都包括 $abc 字段的事实也与所展示的内容无关。 - ToolmakerSteve
我讨厌第一个例子中的行为。 - André Walker

62

看起来很多人对于对象在函数中的传递及按引用传递的概念感到困惑。实际上,对象仍然是按值传递的,在PHP5中传递的值只是一个参考句柄。证明如下:

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

输出:

a, b

按引用传递意味着我们可以修改由调用方看到的变量,而上面的代码显然并没有这样做。我们需要将swap函数更改为:

<?php
function swap(&$x, &$y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

输出:

b, a

为了通过引用传递。

20
我认为这个证明是错误的解释。看起来你试图交换引用。你对对象分配方式有问题。你正在重新分配不可变的引用。它指向的值可以从内部改变。重定义swap现在通过 **x 传递,这允许你改变 * x。问题在于认为 $x = $y会改变 $x 最初指向的内容。 - grantwparks
11
这不是一个证明。第一个例子的输出结果,恰好表现出整个对象当前值被传递给“swap”函数时的预期行为;换句话说,如果对象是按“值传递”的方式传递的。另一方面,如果一个对象句柄被传递给函数,实际上就是发生了这种情况,它也完全符合你的预期。你的代码没有区分这两种情况。 - EML
对象仍然是按值传递的,在PHP5中传递的值只是一个引用句柄,不是“不”。这就像说将指针传递给C函数是按值传递一样。它不是。这是一个引用。是的,引用是一种值类型,但它仍然是一个引用。通过引用传递意味着我们可以修改调用者所看到的变量,确实,这也可以通过对象实现:$object->foo = 1; 将被调用者看到。在你的例子中,你正在为该名称分配一个新引用,这将擦除旧引用。这并不会使它更少成为引用。 - DimeCadmium
PHP文档中误导性地称为“reference”的&$foo更合适地被称为别名或绑定。 “按值传递”和“按引用传递”(以及实际上“引用”)在PHP范围之外已经具有现有的意义,而PHP奇怪的定义与其根本不兼容。 - DimeCadmium

37
在PHP 5中,有一个新的对象模型。 PHP对对象的处理已完全重写,这使得性能更佳、功能更多。在以前的PHP版本中,对象被处理为原始类型(例如整数和字符串)。这种方法的缺点是,当变量被分配或作为参数传递给方法时,语义上整个对象都被复制。在新的方法中,对象通过句柄引用,而不是按值引用(可以将句柄看作对象的标识符)。

http://www.php.net/manual/zh/migration5.oop.php


33

PHP变量通过值赋值,通过值传递给函数,当包含/表示对象时,则通过引用传递。您可以使用'&'强制变量通过引用传递。

按值/引用赋值示例:

$var1 = "test";
$var2 = $var1;
$var2 = "new test";
$var3 = &$var2;
$var3 = "final test";

print ("var1: $var1, var2: $var2, var3: $var3);

输出:

变量1:测试,变量2:最终测试,变量3:最终测试

按值/引用传递示例:

$var1 = "foo";
$var2 = "bar";

changeThem($var1, $var2);

print "var1: $var1, var2: $var2";

function changeThem($var1, &$var2){
    $var1 = "FOO";
    $var2 = "BAR";
}

输出:

变量1: foo, 变量2: BAR

对象变量传递引用示例:

class Foo{
    public $var1;

    function __construct(){
        $this->var1 = "foo";
    }

    public function printFoo(){
        print $this->var1;
    }
}


$foo = new Foo();

changeFoo($foo);

$foo->printFoo();

function changeFoo($foo){
    $foo->var1 = "FOO";
}

输出:

FOO

(最后一个示例可能还有改善空间。)


3
在PHP5中,“对象”不是值,因此无法“传递”。变量 $foo 的值是指向一个对象的指针。在函数 changeFoo() 中,由于未使用 & 声明其参数,因此 $foo 被按值传递。 - newacct
@newacct 我认为你的意思是指针被传值,但对象也被改变了! - Achraf JEDAY

10

你可以通过引用将变量传递给函数。这个函数将能够修改原始变量。

您可以在函数定义中通过引用定义传递的参数:

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

$result=5;
changeValue($result);

echo $result; // $result is 6 here
?>

9
TL;DR: PHP支持按值传递和按引用传递。使用&符号(&)声明引用,这与C ++非常相似。当函数的形式参数未使用&符号声明时(即它不是一个引用),所有内容都是按值传递的,包括对象。对象和基元素的传递方式没有区别。关键是要理解在将对象传递到函数中时会传递什么。这就是理解指针的价值所在。

对于将来遇到此问题的任何人,我想分享PHP文档中匿名用户发布的信息中的宝石:

这里似乎存在一些混淆。指针和引用之间的区别并没有特别有帮助。一些“全面”的示例中的行为可以用更简单的统一术语来解释。例如,Hayley的代码正在做您应该期望它应该做的事情。(使用 >= 5.3)

第一个原则: 指针存储内存地址以访问对象。每次分配对象时,都会生成一个指针。(我还没有深入研究Zend引擎,但据我所见,这适用于所有情况)

第二个原则,也是最容易混淆的来源: 默认情况下,将变量传递给函数作为值传递,即您正在使用副本。 "但是对象是按引用传递的!" 这是这里和Java世界中的一种常见误解。我从未说过复制了什么。默认传递是按值进行的。总是。然而,被复制并传递的是指针。当使用“->”时,您当然将访问调用函数中原始变量的相同内部。只使用“=”将仅使用副本。

第三原则: "&" 会自动且永久地将另一个变量名/指针设置为与其他东西相同的内存地址,直到你解除它们之间的关系。在这里使用术语“别名”是正确的。可以将其视为将两个指针连接在一起,直到通过 "unset()" 强制分离。这种功能存在于相同的作用域和传递给函数参数时。通常传递的参数称为“引用”,因为在 C 和 C++ 中“按值传递”和“按引用传递”之间的某些区别更清晰。
只需记住:将对象的指针而不是对象本身传递给函数。除非在参数列表中使用 "&" 来实际传递原始内容,否则这些指针都是原始内容的副本。只有当您深入对象的内部时,原始内容才会发生改变。
以下是他们提供的示例:
<?php

//The two are meant to be the same
$a = "Clark Kent"; //a==Clark Kent
$b = &$a; //The two will now share the same fate.

$b="Superman"; // $a=="Superman" too.
echo $a;
echo $a="Clark Kent"; // $b=="Clark Kent" too.
unset($b); // $b divorced from $a
$b="Bizarro";
echo $a; // $a=="Clark Kent" still, since $b is a free agent pointer now.

//The two are NOT meant to be the same.
$c="King";
$d="Pretender to the Throne";
echo $c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByValue($c, $d);
echo $c."\n"; // $c=="King"
echo $d."\n"; // $d=="Pretender to the Throne"
swapByRef($c, $d);
echo $c."\n"; // $c=="Pretender to the Throne"
echo $d."\n"; // $d=="King"

function swapByValue($x, $y){
$temp=$x;
$x=$y;
$y=$temp;
//All this beautiful work will disappear
//because it was done on COPIES of pointers.
//The originals pointers still point as they did.
}

function swapByRef(&$x, &$y){
$temp=$x;
$x=$y;
$y=$temp;
//Note the parameter list: now we switched 'em REAL good.
}

?>

我为JavaScript写了一篇详细的博客文章,但我认为它同样适用于PHP、C++和其他任何语言,在这些语言中人们似乎对按值传递和按引用传递感到困惑。

显然,PHP像C++一样支持按引用传递。默认情况下,对象是按值传递的。在处理存储对象的变量时,将这些变量视为指针会有所帮助(因为从汇编级别来看,这就是它们的本质)。如果您通过值传递一个指针,仍然可以“跟踪”该指针并修改所指向的对象的属性。您不能做的是让它指向另一个对象。只有明确声明参数按引用传递,您才能够这样做。


应该将其添加到顶部并阻止编辑。 - fearis
不知道为什么这里没有太多的赞..那些 PHP 文档中的引用清楚地解释了一切! - Jay Dadhania

6
你可以采用两种方式。
在变量前面加上'&'符号,传递的变量就会成为其原始值本身,即可以通过引用传递,而不是复制它。
因此,
    $fred = 5;
    $larry = & $fred;
    $larry = 8;
    echo $fred;//this will output 8, as larry and fred are now the same reference.

3
已停用,会生成警告。 - fijiaaron

5

在PHP5中,包含原始类型的变量按值传递。包含对象的变量按引用传递。 2006年,《Linux Journal》发表了一篇非常有趣的文章,提到了这一点以及4和5之间的其他面向对象差异。

http://www.linuxjournal.com/article/9170


1
如果函数的参数没有 &,则在 PHP 中所有变量都是按值传递的。 - newacct

1
class Holder
{
    private $value;

    public function __construct( $value )
    {
        $this->value = $value;
    }

    public function getValue()
    {
        return $this->value;
    }

    public function setValue( $value )
    {
        return $this->value = $value;
    }
}

class Swap
{       
    public function SwapObjects( Holder $x, Holder $y )
    {
        $tmp = $x;

        $x = $y;

        $y = $tmp;
    }

    public function SwapValues( Holder $x, Holder $y )
    {
        $tmp = $x->getValue();

        $x->setValue($y->getValue());

        $y->setValue($tmp);
    }
}


$a1 = new Holder('a');

$b1 = new Holder('b');



$a2 = new Holder('a');

$b2 = new Holder('b');


Swap::SwapValues($a1, $b1);

Swap::SwapObjects($a2, $b2);



echo 'SwapValues: ' . $a2->getValue() . ", " . $b2->getValue() . "<br>";

echo 'SwapObjects: ' . $a1->getValue() . ", " . $b1->getValue() . "<br>";

即使未通过引用传递,属性仍然是可修改的,所以请注意。

输出:

SwapObjects:b,a SwapValues:a,b


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接