Combined Comparison Operator

Some built-in PHP functions require passing of a callable (a function), for example sorting methods like usort() that require comparison methods or functions. A classic example would be trying to sort a collection or an array of objects. Since we cannot compare the objects directly, we have to call a comparison method. If, at some point in the future, PHP will support operator overloading, this might not be necessary anymore. But as of today, we have to write a comparison function, usually as an anonymous function, that compares two objects:

class Appointment
{
    private DateTimeImmutable $date;

    public function __construct(DateTimeImmutable $date)
    {
        $this->date = $date;
    }

    public function getDate(): DateTimeImmutable
    {
        return $this->date;
    }
}

$collection = [
    new Appointment(new DateTimeImmutable('2016-07-02')),
    new Appointment(new DateTimeImmutable('2016-06-06')),
    new Appointment(new DateTimeImmutable('2016-05-04'))
];

var_dump($collection);

usort(
    $collection,
    function($a, $b) {
        $dateA = $a->getDate();
        $dateB = $b->getDate();

        if ($dateA > $dateB) {
            return 1;
        }

        if ($dateA < $dateB) {
            return -1;
        }

        if ($dateA == $dateB) {
            return 0;
        }
    }
);

var_dump($collection);

In this example, we want to sort our Appointment object instances by date value. They are no scalar values, so we cannot just compare two instances using comparison operators == or <, but we have to retrieve the dates first by calling the getDate() method, and can then compare the values.

The second argument to usort() is a comparison function that either returns -1, 1 or 0 based on the result of the comparison. While this works fine, it is quite a bit of code to achieve such a simple task.

Luckily, PHP 7 features the new combined comparison operator <=> that makes is a lot easier to write comparison functions:

usort(
    $collection,
    function($a, $b)
    {
        return $a->getDate() <=> $b->getDate();
    }
);

The combined comparison operator, colloquially also called the spaceship operator, because it somewhat looks like a TIE fighter in Star Wars, will compare the two given values and either return -1, 1 or 0 based on the result.

Now we can run our sorting code with the shortened call to usort():

array(3) {
  [0] =>
  class Appointment#1 (1) {
    private $date =>
    class DateTimeImmutable#2 (3) {
      public $date =>
      string(26) "2016-07-02 00:00:00.000000"
      // ...
    }
  }
  [1] =>
  class Appointment#3 (1) {
    private $date =>
    class DateTimeImmutable#4 (3) {
      public $date =>
      string(26) "2016-06-06 00:00:00.000000"
      // ...
    }
  }
  [2] =>
  class Appointment#5 (1) {
    private $date =>
    class DateTimeImmutable#6 (3) {
      public $date =>
      string(26) "2016-05-04 00:00:00.000000"
      // ...
    }
  }
}

array(3) {
  [0] =>
  class Appointment#5 (1) {
    private $date =>
    class DateTimeImmutable#6 (3) {
      public $date =>
      string(26) "2016-05-04 00:00:00.000000"
      // ...
    }
  }
  [1] =>
  class Appointment#3 (1) {
    private $date =>
    class DateTimeImmutable#4 (3) {
      public $date =>
      string(26) "2016-06-06 00:00:00.000000"
      // ...
    }
  }
  [2] =>
  class Appointment#1 (1) {
    private $date =>
    class DateTimeImmutable#2 (3) {
      public $date =>
      string(26) "2016-07-02 00:00:00.000000"
      // ...
    }
  }
}

As we can see, the objects that initially were unsorted have been sorted by date in ascending order.