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

Posted

技术标签:

【中文标题】PHP 变量是按值传递还是按引用传递?【英文标题】:Are PHP Variables passed by value or by reference? 【发布时间】:2010-09-05 06:41:31 【问题描述】:

php 变量是通过值传递还是通过引用传递?

【问题讨论】:

另见are-arrays-in-php-passed-by-value-or-by-reference codeexpertz.com/blog/php/…中的很好解释 【参考方案1】:

PHP 引用是一个别名,允许两个不同的变量写入同一个值。

在 PHP 中,如果您有一个包含对象的变量,那么该变量不包含该对象本身。相反,它包含该对象的标识符。对象访问器将使用标识符来查找实际对象。因此,当我们将对象用作函数中的参数或将其分配给另一个变量时,我们将复制指向对象本身的标识符。

https://hsalem.com/posts/you-think-you-know-php.html

class Type 

$x = new Type();
$y = $x;
$y = "New value";

var_dump($x); // Will print the object.
var_dump($y); // Will print the "New value"

$z = &$x; // $z is a reference of $x

$z = "New value";
var_dump($x); // Will print "New value"
var_dump($z); // Will print "New value"

【讨论】:

【参考方案2】:

关于如何将对象传递给函数,您仍然需要了解,没有“&”,您向函数传递了一个对象句柄,对象句柄仍然通过值传递,并且它包含指针的值。但是你不能改变这个指针,直到你使用“&”通过引用传递它

<?php
        class Example 
        
            public $value;
         
        
        
        function test1($x) 
        
             //let's say $x is 0x34313131
             $x->value = 1;  //will reflect outsite of this function
                             //php use pointer 0x34313131 and search for the 
                             //address of 'value' and change it to 1

        
        
        function test2($x) 
        
             //$x is 0x34313131
             $x = new Example;
             //now $x is 0x88888888
             //this will NOT reflect outside of this function 
             //you need to rewrite it as "test2(&$x)"
             $x->value = 1000; //this is 1000 JUST inside this function
                 
        
        
         
     $example = new Example;
    
     $example->value = 0;
    
     test1($example); // $example->value changed to  1
    
     test2($example); // $example did NOT changed to a new object 
                      // $example->value is still 1
     
 ?>

【讨论】:

【参考方案3】:

看这个例子:

<?php

$string = 'First String. ';
$string .= 'Second String. ';
$string .= 'Third String. ';

echo $string;

// output: First String. Second String. Third String. 

?>

【讨论】:

您的回答没有回答问题【参考方案4】:

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

对于将来遇到此问题的任何人,我想从PHP docs, posted by an anonymous user分享这颗宝石:

这里似乎有些混乱。指针和引用之间的区别并不是特别有用。 已经发布的一些“综合”示例中的行为可以用更简单的统一术语来解释。例如,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.


?>

I wrote an extensive, detailed blog post on this subject for javascript,但我相信它同样适用于 PHP、C++ 和任何其他人们似乎对按值传递与按引用传递感到困惑的语言。

显然,PHP 与 C++ 一样,是一种支持按引用传递的语言。默认情况下,对象是按值传递的。在使用存储对象的变量时,将这些变量视为指针会有所帮助(因为在汇编级别,它们基本上就是这样)。如果按值传递指针,您仍然可以“跟踪”指针并修改所指向对象的属性。你不能做的是让它指向一个不同的对象。只有当你明确声明一个参数是通过引用传递时,你才能做到这一点。

【讨论】:

应该添加到顶部并阻止编辑。【参考方案5】:

无论哪种方式都可以。

在前面放一个“&”符号,您传递的变量将变为一个,并且与它的来源相同,即您可以通过引用传递,而不是复制它。

所以

    $fred = 5;
    $larry = & $fred;
    $larry = 8;
    echo $fred;//this will output 8, as larry and fred are now the same reference.

【讨论】:

已弃用并生成警告【参考方案6】:

PHP 变量按值分配,按值传递给函数,并且在包含/表示对象时通过引用传递。您可以使用“&”强制变量通过引用传递。

按值/引用分配的示例:

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

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

输出:

var1:测试,var2:最终测试,var3:最终测试

按值传递/引用示例:

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

changeThem($var1, $var2);

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

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

输出:

var1: foo, var2 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";

输出:

(最后一个例子可能会更好。)

【讨论】:

“对象”不是 PHP5 中的值,不能“通过”。 $foo 的值是一个指向对象的指针$foo 按值传递到函数 changeFoo(),因为 changeFoo() 没有用 &amp; 声明它的参数。 @newacct 我想你的意思是指针是按值传递的,但对象也被改变了!【参考方案7】:

在 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.

希望你能理解。

【讨论】:

第一个例子,obj 的引用值被传递给函数,使用新引用对 obj 的更改将反映到指向同一 obj 的所有其他引用。第二个例子,再次将 obj 引用的 VALUE 传递给函数,函数正在更改引用的值以指向新的 obj,旧的 obj 保持不变。第3个例子,obj的引用值的引用被传递到函数中,因此函数中的变化是在同一个obj上操作的。 这个例子过于复杂了。不需要$y - 在function changeValue 中没有任何内容要求它位于class Y 内部,因此将两个不相关的概念纠缠在一起。我会将function changeValue 移至$x = new X(); 上方的外部范围。删除对$y 的所有引用。现在更容易看出前两个示例单独保留了 $x,第三个示例将其内容更改为 new Y()。如果您丢弃了$xtype,这将更容易看出-XY 恰好包含$abc 字段的事实也与演示的内容无关【参考方案8】:

似乎很多人对对象传递给函数的方式以及通过引用传递的含义感到困惑。对象还是按值传递的,只是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

到pass by reference 意味着我们可以修改调用者看到的变量,显然上面的代码没有这样做。我们需要把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

为了通过引用传递。

【讨论】:

我认为这个证明是一种误解。看起来您正在尝试交换引用。您对如何分配对象有疑问。您正在重新分配不可变的引用。它指向的值可以从内部更改。对 swap 的重新定义现在传递了 **x,它允许您更改 *x。问题在于认为 $x = $y 会改变 $x 最初指向的内容。 这不是证据。如果将整个对象的当前值传递给swap 函数,则第一个示例的输出正是您所期望的;换句话说,如果对象“按值传递”。另一方面,如果将对象句柄传递给函数,这也正是您所期望的,这实际上就是发生的情况。您的代码无法区分这两种情况。【参考方案9】:

实际上这两种方法都有效,但这取决于您的要求。通过引用传递值通常会使您的脚本变慢。因此,考虑到执行时间,最好按值传递变量。当您通过值传递变量时,代码流也更加一致。

【讨论】:

【参考方案10】:

当您希望简单地更改原始变量并再次将其返回到相同的变量名称并为其分配新值时,请将其用于函数。

function add(&$var) // The &amp; is before the argument $var
   $var++;

$a = 1;
$b = 10;
add($a);
echo "a is $a,";
add($b);
echo " a is $a, and b is $b"; // Note: $a and $b are NOT referenced

【讨论】:

【参考方案11】:

根据PHP Documentation 的值。

默认情况下,函数参数是按值传递的(因此,如果函数内的参数值发生变化,它不会在函数外发生变化)。要允许函数修改其参数,它们必须通过引用传递。

要让函数的参数始终通过引用传递,请在函数定义中的参数名称前添加与号 (&)。

<?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.'
?>

【讨论】:

我也有这个疑问(新手)——所以为了清楚起见,如果我没有使用参考,这将与 $str = add_some_extra($str); 相同,对吧?那么它的真正附加价值是什么? @Obmerk 如果您使用与号来表示引用,则它不等同。请注意该函数如何没有返回语句,因此在您的情况下,$str 将被分配为 null。 如果你这样做@ObmerkKronen 那么你将不得不返回值并通过你的代码捕获它。否则初始变量将保持不变。 真正的好处是您可以操作/返回多个值。您还可以通过引用传递变量,并且仍然让函数返回一些内容。 @Choxx 不需要通过引用传递数组来提高性能。 PHP 引擎使用写时复制 - 它不会物理复制数组除非它在它可见的范围之一中被修改。然后它会制作一个副本,因此修改只适用于一个地方。【参考方案12】:

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

可以在函数定义中通过引用来定义段落:

<?php
function changeValue(&$var)

    $var++;


$result=5;
changeValue($result);

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

【讨论】:

【参考方案13】:
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>";

属性在不通过引用传递时仍然可以修改,所以要小心。

输出:

交换对象:b,a 交换值:a,b

【讨论】:

【参考方案14】:

对象在 PHP 5 中通过引用传递,在 PHP 4 中通过值传递。 变量默认传值!

在这里阅读:http://www.webeks.net/programming/php/ampersand-operator-used-for-assigning-reference.html

【讨论】:

“对象”不是 PHP5 中的值,不能“通过”。如果传递给的函数的参数没有&amp;,则所有变量都按值传递。 @newacct 不完全吗?对象是somewhat passed by reference。我想我已经观察到,即使没有在定义中的参数前面使用&amp; 定义,php 对象也可以被函数修改 - 如果它们是按值传递的,则范围中包含的对象使用它调用函数作为参数不会受到影响。 @FélixGagnon-Grenier:同样,“对象”不是值,不能“传递”。在 PHP5 中不能有一个值为“对象”的“变量”,只能有一个作为对象引用的值(即指向对象的指针)。当然,您可以通过值传递或分配指向对象的指针,并通过调用进行此类修改的方法来修改它指向的对象。这与通过引用传递无关。一个值是什么类型,一个参数是否是传值/传引用,是独立的、正交的东西。【参考方案15】:

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

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

【讨论】:

如果函数的参数没有&amp;,PHP中所有的变量都是传值的。【参考方案16】:

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

在 PHP 5 中有一个新的对象模型。 PHP 对对象的处理已经完全重写,从而获得了更好的性能和更多的特性。在以前的 PHP 版本中,对象的处理方式类似于原始类型(例如整数和字符串)。这种方法的缺点是语义上整个对象在分配变量时被复制,或者作为参数传递给方法。在新方法中,对象是通过句柄而不是值来引用的(可以将句柄视为对象的标识符)。

【讨论】:

【参考方案17】:

取决于版本,4 是按值,5 是按引用。

【讨论】:

我不认为这是真的。 @Karl Seguin,部分正确。在 PHP5 之前默认对象是按值传递的,但是从 5 开始它们是由处理程序传递的,实际上可以将其视为引用(有一些例外),因为我们可以通过它们修改对象属性php.net/manual/en/language.oop5.references.php。

以上是关于PHP 变量是按值传递还是按引用传递?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 数组的拷贝是按值传递 or 按引用传递

Swift 函数是按值还是按引用分配/传递?

JS基础类型和对象,分别是按值传递还是按引用传递?

java中的参数传递是按引用传递还是按值传递

JavaScript 是按引用传递还是按值传递? [复制]

Ruby 是按值传递还是按引用传递? [复制]