Chapter 3: Combined Comparison (Spaceship) Operator

PHP 7 introduces a new three-way comparison operator <=> (T_SPACESHIP) which takes two expressions: (expr) <=> (expr). It compares both sides of the expression and, depending on the result, returns one of three values:

0 If both expressions are equal
1 If the left is greater
-1 If the right is greater

You may be familiar with this if you’ve worked with existing comparison functions like strcmp before.

It has the same precedence as the equality operator (==, ===, !=, !==) and behaves identically to the existing comparison operators (<, <=, ==, >=, >).

Comparing Values

You can compare everything from scalar values (like ints and floats) to arrays and even objects too. Here are some examples from the relevant RFC:

PHP RFC examples (licensed under CC BY 3.0)
// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1

echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1

// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

// Objects
$a = (object) ["a" => "b"];
$b = (object) ["a" => "b"];
echo $a <=> $b; // 0

$a = (object) ["a" => "b"];
$b = (object) ["a" => "c"];
echo $a <=> $b; // -1

$a = (object) ["a" => "c"];
$b = (object) ["a" => "b"];
echo $a <=> $b; // 1

// only values are compared
$a = (object) ["a" => "b"];
$b = (object) ["b" => "b"];
echo $a <=> $b; // 0

Sorting

Perhaps the best application of this operator is to simplify sorting, as functions like usort expect you to perform a comparison and return -1, 0, or 1 accordingly:

This simplification is especially apparent when comparing objects by some property value:

class Spaceship {
    public $name;
    public $maxSpeed;

    public function __construct($name, $maxSpeed) {
        $this->name = $name;
        $this->maxSpeed = $maxSpeed;
    }
}

$spaceships = [
    new Spaceship('Rebel Transport', 20),
    new Spaceship('Millenium Falcon', 80),
    new Spaceship('X-Wing Starfighter', 80),
    new Spaceship('TIE Bomber', 60),
    new Spaceship('TIE Fighter', 100),
    new Spaceship('Imperial Star Destroyer', 60),
];

// Sort the spaceships by name (in ascending order)
usort($spaceships, function ($ship1, $ship2) {
    return $ship1->name <=> $ship2->name;
});

echo $spaceships[0]->name; // "Imperial Star Destroyer"

// Sort the spaceships by speed (in descending order)
// Notice how we switch the position of $ship1 and $ship2
usort($spaceships, function ($ship1, $ship2) {
    return $ship2->maxSpeed <=> $ship1->maxSpeed;
});

echo $spaceships[0]->name; // "TIE Fighter"

Without the comparison operator, these functions would be much more complex:

usort($spaceships, function ($ship1, $ship2) {
    if ($ship1->maxSpeed == $ship2->maxSpeed) {
        return 0;
    } elseif ($ship1->maxSpeed < $ship2->maxSpeed) {
        return 1;
    } else {
        return -1;
    }
});

Sorting by multiple values

You can take advantage of the array comparison behavior to easily sort by multiple fields:

// Sort by speed (asc), then by name (asc)
usort($spaceships, function ($ship1, $ship2) {
    return [$ship1->maxSpeed, $ship1->name] <=> [$ship2->maxSpeed, $ship2->name];
});

foreach ($spaceships as $ship) {
    printf("%3d is the max speed of a(n) %s\n", $ship->maxSpeed, $ship->name);
}

// Outputs:
//  20 is the max speed of a(n) Rebel Transport
//  60 is the max speed of a(n) Imperial Star Destroyer
//  60 is the max speed of a(n) TIE Bomber
//  80 is the max speed of a(n) Millenium Falcon
//  80 is the max speed of a(n) X-Wing Starfighter
// 100 is the max speed of a(n) TIE Fighter

Further Reading