PHP 变量是按值传递还是按引用传递?
它是根据 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.'
?>
在 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.
我希望你能理解这一点。
$y
- function changeValue
中没有任何内容要求它位于 class Y
内,因此将两个不相关的概念纠缠在一起。我会将 function changeValue
移至 $x = new X();
上方的外部范围。删除对 $y
的所有引用。现在更容易看出前两个示例单独保留了 $x,第三个示例将其内容更改为 new Y()
。如果您转储 $x
的 type
会更容易看出 - X
和 Y
恰好包含 $abc
字段这一事实也与演示的内容无关
似乎很多人对对象传递给函数的方式以及通过引用传递的含义感到困惑。对象还是按值传递的,只是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 意味着我们可以修改调用者看到的变量,显然上面的代码没有这样做。我们需要将交换函数更改为:
<?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
函数,则第一个示例的输出正是您所期望的;换句话说,如果对象“按值传递”。另一方面,如果将对象句柄传递给函数,这也正是您所期望的,这实际上就是发生的情况。您的代码不区分这两种情况。
Object are still passed by value, it's just the value that is passed in PHP5 is a reference handle
没有。这就像说将指针传递给 C 函数是按值传递。它不是。这是一个参考。是的,引用是一种值,但它仍然是引用。 To pass by reference means we can modify the variables that are seen by the caller
确实,您可以对对象执行此操作:调用者将看到 $object->foo = 1;
。在您的示例中,您将 new reference 分配给名称,这会删除旧引用。这并没有减少它的参考价值。
&$foo
的内容更恰当地称为别名或绑定。 “按值传递”和“按引用传递”(实际上是“引用”)具有 PHP 范围之外的现有含义,PHP 的奇异定义甚至与这些含义不兼容。
http://www.php.net/manual/en/migration5.oop.php
在 PHP 5 中有一个新的对象模型。 PHP 对对象的处理已被完全重写,以提供更好的性能和更多功能。在以前的 PHP 版本中,对象的处理方式类似于原始类型(例如整数和字符串)。这种方法的缺点是语义上整个对象在分配变量时被复制,或者作为参数传递给方法。在新方法中,对象是通过句柄而不是值来引用的(可以将句柄视为对象的标识符)。
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 酒吧
通过引用传递的对象变量示例:
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
的值是一个指向对象的指针。 $foo
按值传递 到函数 changeFoo()
,因为 changeFoo()
没有用 &
声明其参数。
您可以通过引用将变量传递给函数。该函数将能够修改原始变量。
您可以在函数定义中通过引用来定义段落:
<?php
function changeValue(&$var)
{
$var++;
}
$result=5;
changeValue($result);
echo $result; // $result is 6 here
?>
无论哪种方式,你都可以做到。
在前面放一个“&”符号,您传递的变量将变为一个,并且与它的来源相同,即您可以通过引用传递,而不是复制它。
所以
$fred = 5;
$larry = & $fred;
$larry = 8;
echo $fred;//this will output 8, as larry and fred are now the same reference.
在 PHP5 中,包含原始类型的变量是按值传递的。包含对象的变量通过引用传递。 2006 年的 Linux Journal 有一篇非常有趣的文章,其中提到了 4 和 5 之间的这个和其他 OO 差异。
http://www.linuxjournal.com/article/9170
&
,则所有变量在 PHP 中都是按值传递的。
TL;DR:PHP 支持值传递和引用传递。使用 & 符号 (&
) 声明引用;这与 C++ 的做法非常相似。当函数的形参没有用 & 号声明时(即,它不是引用),everything 都是按值传递的,包括对象。对象和原语的传递方式没有区别。关键是要了解将对象传递给函数时传递的什么。这就是理解指针非常宝贵的地方。
对于将来遇到此问题的任何人,我想分享 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++ 一样,是一种支持按引用传递的语言。默认情况下,对象是按值传递的。使用存储对象的变量时,将这些变量视为指针会有所帮助(因为在汇编级别,它们基本上就是这样)。如果按值传递指针,您仍然可以“跟踪”指针并修改所指向对象的属性。你不能做的是让它指向一个不同的对象。只有当你明确声明一个参数是通过引用传递时,你才能做到这一点。
对象在 PHP 5 中通过引用传递,在 PHP 4 中通过值传递。变量默认按值传递!
在这里阅读:http://www.webeks.net/programming/php/ampersand-operator-used-for-assigning-reference.html
&
,则所有变量都按值传递。
&
定义 php 对象也可以被函数修改 - 如果它们是按值传递的,则包含在范围内的对象使用它调用函数参数不会受到影响。
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
关于如何将对象传递给函数,您仍然需要了解,如果没有“&”,您将传递给函数一个对象句柄,对象句柄仍然通过值传递,并且它包含指针的值。但是你不能改变这个指针,直到你使用“&”通过引用传递它
<?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
?>
当您希望简单地更改原始变量并再次将其返回到相同的变量名称并为其分配新值时,可以将其用于函数。
function add(&$var){ // The & 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
实际上这两种方法都是有效的,但这取决于您的要求。通过引用传递值通常会使您的脚本变慢。因此,考虑到执行时间,最好按值传递变量。当您按值传递变量时,代码流也更加一致。
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"
取决于版本,4 是按值,5 是按引用。
$str = add_some_extra($str);
相同,对吧?那么它的真正附加价值是什么?$str
将被分配为 null。