70
votes

What do "=&" / "&=" operators in PHP mean? Where can I read information about them?

Searching Google doesn't help.

2
=& is NOT a "combined operator". Here is a post that explains why you should never write =&: stackoverflow.com/a/63914758/2943403mickmackusa

2 Answers

93
votes

$a &= $b is short for $a = $a & $b which is the bitwise-and operator.

$a =& $b assigns $a as a reference to $b.

39
votes

=&

$a =& $b turns $a into an alias for $b. If the value or reference of $a is changed, the value or reference of $b will change accordingly.

This differs from "both pointing to the same place" when it comes to objects: I could do $c = $d = new AnObject(), and both variables would point to the same place; however, changing where one points would not change where the other points. That is, $c = null would not make $d = null. In the case of $a =& $b, however, $a = null would make $b = null.

Note: Officially, aliases are actually called references. The official terminology is a bit of a misnomer and is certainly ambiguous, so I've opted to use the term "alias" instead. For documentation, see php.net.

Uses and effects

With scalar values, =& is sort of like wrapping the value in an object, so that you can change the value universally among several variables. With types that are normally passed by reference (objects), =& provides a reference to a reference.

I tend to use =& when I'm working with associative arrays. Rather than rewriting $foo['bar']['foobar'] several times over, I can create an alias: $foobar =& $foo['bar']['foobar']. These even works if the index doesn't exist yet. If $foo['bar']['foobar'] doesn't exist, then isset($foobar) will be false. It's better than using a plain old variable, because I can create the alias before testing for the existence of the key without triggering an error.

Just be sure to unset (unset($foobar)) the alias when you're done. Otherwise, if you reuse the variable name later, you'll end up overwriting whatever the alias was pointing to.

You can use aliases in other ways, too--they're not limited to assignments. They work with:

  • foreach loops: foreach ($a as &$b) Assigning to $b will overwrite the corresponding value in $a. Unset $b when you're done, or you'll run into weird problems!
  • function/method parameters: function foobar(&$a) Assigning to $a within foobar will change whatever variable the caller passed as $a.
  • function/method return values: function &foobar() Whatever is returned can be modified by the caller; this is useful for passing around aliases. It's also easy to abuse.
  • arrays: $a = array(&$b) Any changes to $a[0] will now affect $b, including assignments.
  • call_user_func_array: call_user_func('foobar', array(&$a)) Assuming foobar takes a single alias parameter, foobar can now modify $a. This allows you to call functions/methods with alias parameters using call_user_func_array.

Examples

Scalars

$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

Objects

#!/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
)
*/
?>

&=

&= is unrelated to =&. It comes from a set of assignment operations. Here's just a few:

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

See the trend here?

Binary arithmetic operators generally have assignment counterparts. Let's say @ were an arithmetic operator (it's not as of writing) such that $a @ $b generally yields a number when $a and $b are numbers. (Think: Addition, multiplication, division, etc.) How often do you need to do something like this?

$a = $a @ $b;

Pretty often. Doesn't it seem a bit unnecessary to repeat $a? Many languages, including PHP, solve this with an array of assignment operators:

$a @= $b;

Much simpler, and to a programmer accustomed to this notation, perhaps more concise and descriptive at a glance. (I certainly find it easier to read, since I'm so used to it.) So to double a variable:

$a *= 2;

Quick, easy, and relatively descriptive. Some languages, including PHP, extend this feature beyond arithmetic for an extra operation or two. Notably:

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

Very useful.

&= falls among these assignment operators, because & represents a bitwise arithmetic AND operation. There are a few others listed in the PHP documentation (see aforementioned link), all of which are common to many programming languages.

This means that $a &= $b is the same as $a = $a & $b.