PHP中的"=&"和"&="运算符是什么意思?

78

"=&" 和 "&=" 运算符在 PHP 中是什么意思?我在哪里可以找到相关信息呢?

在谷歌上搜索没有帮助。


3
=& 不是一个“组合运算符”。这里有一篇文章解释了为什么你永远不应该写 =&:https://dev59.com/2nI-5IYBdhLWcg3wm5rN#63914758 - mickmackusa
相关问题及更多尝试解释引用:https://dev59.com/2nI-5IYBdhLWcg3wm5rN - IMSoP
2个回答

103

$a &= $b 是位运算符中的按位与(bitwise-and)操作的简写形式,等同于 $a = $a & $b

$a =& $b 将变量 $a 赋值为变量 $b 的引用(reference)。


12
我不认为有一个=&操作符。它是=(赋值)和一元&(引用)操作符的组合。 - Michael Krelin - hacker
3
@hacker:通常使用一个普通的名称来代替特殊字符是很有效的,例如“和符号 php”。 - GZipp
5
吹毛求疵:“$a =& $b 将 $a 分配为对 $b 的引用”是错误的,因为 $a 不指向 $b(或反之亦然),而是两者都指向同一个位置。这有微妙但重要的区别。 - Jürgen Thelen
2
@MichaelKrelin-hacker,PHP中没有所谓的解引用运算符。&是上下文敏感的。您说的=&不是单个运算符;相反,当在前面加上= 时,&就成为一个运算符,允许使用空格。或者您可以说&修改了=运算符。 - Zenexer
1
@theking2 在 PHP 中没有“引用类型”,可以作为操作数传递。在 PHP 中,$a =& $b; 只是 $a = &$b; 的另一种拼写方式,但这可能是一个更加清晰的拼写方式,因为它是“通过引用”发生的赋值,而不仅仅是其中一个变量。这与 C 中的等效方式不同,在 C 中,您可以将其分解为“创建引用”,然后进行普通赋值,因此 a = &b 是一种逻辑上合理的空间方式。 - IMSoP
显示剩余14条评论

50

=&

的翻译:

$a =& $b$a变成了$b的别名。如果改变$a的值或引用,$b的值或引用也会相应地改变。

这与对象的“指向同一位置”不同:我可以做$c = $d = new AnObject(),两个变量都指向同一位置;但是,改变其中一个指向的位置不会改变另一个指向的位置。也就是说,$c = null不会使$d = null。然而,在$a =& $b的情况下,$a = null会使$b = null

注意:官方上,别名实际上被称为引用。官方术语有点不准确并且肯定是含糊的,因此我选择使用“别名”一词。有关文档,请参见php.net

用途和影响

对于标量值,=& 有点像将该值包装在对象中,以便您可以在多个变量之间普遍地更改该值。对于通常通过引用传递的类型(对象),=& 提供了对引用的引用。
我倾向于在使用关联数组时使用 =&。与其多次重写 $foo['bar']['foobar'],我可以创建一个别名:$foobar =& $foo['bar']['foobar']。这甚至适用于索引尚不存在的情况。如果 $foo['bar']['foobar'] 不存在,则 isset($foobar) 将为 false。这比使用普通变量更好,因为我可以在测试密钥存在性之前创建别名而不触发错误。
只需确保在完成后取消设置别名 (unset($foobar))。否则,如果稍后重新使用变量名,则会覆盖别名所指向的任何内容。
您还可以以其他方式使用别名-它们不仅限于赋值。它们适用于:
  • foreach循环:foreach ($a as &$b)$b赋值将会覆盖$a中相应的值。完成后取消$b,否则会遇到奇怪的问题!
  • 函数/方法参数:function foobar(&$a)foobar内给$a赋值将更改调用者传递的任何变量作为$a
  • 函数/方法返回值:function &foobar() 返回的任何内容都可以由调用者修改;这对于传递别名非常有用。但也很容易被滥用。
  • 数组:$a = array(&$b)$a[0]的任何更改现在都会影响$b,包括赋值。
  • call_user_func_array:call_user_func('foobar', array(&$a)) 假设foobar需要一个单一的别名参数,foobar现在可以修改$a。这允许您使用call_user_func_array调用具有别名参数的函数/方法。

示例

标量

$original = 1;
$copy = $original;
$reference =& $original;
// All three variables == 1.

$reference = 2;
// $original == 2, $reference == 2, $copy == 1

$original = 3;
// $original == 3, $reference == 3, $copy == 1

$copy = 4;
// $original == 3, $reference == 3, $copy == 4

对象

#!/usr/bin/env php
<?php
class Object
{
        private $properties;

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

        public function __isset($key)
        {
                return isset($this->properties[$key]);
        }

        public function __unset($key)
        {
                unset($this->properties[$key]);
        }

        public function __get($key)
        {
                return isset($this->$key) ? $this->properties[$key] : null;
        }

        public function __set($key, $value)
        {
                $this->properties[$key] = $value;
        }

        public function __toString()
        {
                return print_r($this->properties, true);
        }
}

function print_vars()
{
        global $original, $ref, $refref;

        echo
                '$original: ', $original,
                '$ref: ', $ref,
                '$refref: ', $refref,
                PHP_EOL;
}

$original = new Object(array('a' => 1, 'b' => 2, 'c' => 3));
$ref = $original;
$refref =& $original;
print_vars();
/*
$original: Array
(
    [a] => 1
    [b] => 2
    [c] => 3
)
$ref: Array
(
    [a] => 1
    [b] => 2
    [c] => 3
)
$refref: Array
(
    [a] => 1
    [b] => 2
    [c] => 3
)
*/

$original->a = 'duck';
$ref->b = 'moose';
$refref->c = 'cow';
print_vars();
/*
$original: Array
(
    [a] => duck
    [b] => moose
    [c] => cow
)
$ref: Array
(
    [a] => duck
    [b] => moose
    [c] => cow
)
$refref: Array
(
    [a] => duck
    [b] => moose
    [c] => cow
)
*/

// This carries over to $refref, but not $ref.
$original = new Object(array('x' => 1, 'y' => 2, 'z' => 3));
print_vars();
/*
$original: Array
(
    [x] => 1
    [y] => 2
    [z] => 3
)
$ref: Array
(
    [a] => duck
    [b] => moose
    [c] => cow
)
$refref: Array
(
    [x] => 1
    [y] => 2
    [z] => 3
)
 */

// This does *not* carry over to $original or $ref.
$ref = new Object(array('o' => 42, 'm' => 123, 'n' => 1337));
print_vars();
/*
$original: Array
(
    [x] => 1
    [y] => 2
    [z] => 3
)
$ref: Array
(
    [o] => 42
    [m] => 123
    [n] => 1337
)
$refref: Array
(
    [x] => 1
    [y] => 2
    [z] => 3
)
*/

// This *does* carry over to $original, but not $ref.
$refref = new Object(array('alpha' => 10, 'beta' => 20, 'gamma' => 30));
print_vars();
/*
$original: Array
(
    [alpha] => 10
    [beta] => 20
    [gamma] => 30
)
$ref: Array
(
    [o] => 42
    [m] => 123
    [n] => 1337
)
$refref: Array
(
    [alpha] => 10
    [beta] => 20
    [gamma] => 30
)
*/
?>

&=

&==&无关。它来自一组赋值操作符,以下仅列举其中几个:

  • +=
  • -=
  • *=
  • /=

看到这里有趋势了吗?

二进制算术运算符通常都有赋值运算符对应。假设@是一个算术运算符(在撰写本文时不是),当$a$b是数字时,$a @ $b通常产生一个数字。(想想:加法、乘法、除法等)你需要多少次这样的操作呢?

$a = $a @ $b;

经常出现这种情况。重复使用$a似乎有点不必要。许多编程语言,包括PHP,使用赋值运算符数组来解决这个问题:
$a @= $b;

这种表示法更为简单,对于习惯这种表示法的程序员来说,或许更加简洁易懂。(我个人觉得更容易阅读,因为我已经习惯了这种表示法。)因此,要将一个变量加倍:

$a *= 2;

快速、简单、且相对描述性强。一些语言,包括PHP,在算术运算之外扩展了此功能,增加了一两个额外操作。尤其值得注意的是:

$a = $a . 'Appended text';
// Is the same as:
$a .= 'Appended text';

非常有用。

&= 是赋值运算符之一,因为 & 代表 按位与运算。PHP文档列出了其他几个赋值运算符(请参见上述链接),它们都是许多编程语言中常见的。

这意味着 $a &= $b$a = $a & $b 是相同的。


给你已经很棒的答案 @zenexer 再加一点。$a @= $b$a = $a @ $b 在一个重要的方面是不同的,那就是$a被评估的频率。在第一种情况下,只有一次,在第二种情况下有两次。如果$a不是一个“正常”的变量,而是一个具有副作用的属性或函数,这可能会产生影响。 - theking2
太棒了!我终于搞明白了!我记得之前看过相关文档,但是没能完全理解。 - Jacek Dziurdzikowski
“别名”并不是一个很好的术语,因为它仍然暗示着操作是有方向性的,但实际上并不是这样。一些描述中提到“添加到引用集合”,这更接近事实:两个名称都是同样有效的,而且都不能被认为是对另一个的引用或别名。 - IMSoP

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