In PHP, what does the <=> spaceship operator return, and how do you use it to sort by several fields?
answer
- three-way comparison
- negative, zero or positive int
- same loose rules as ==
- arrays compare element by element
- bool comparators deprecated in 8.0
basics
~20 s$a <=> $b returns an int below, equal to or above zero when $a is less than, equal to or greater than $b. Comparing arrays of fields, [$a->x, $a->y] <=> [$b->x, $b->y], gives a multi-key comparator for usort().
solid answer
~40 s`<=>` is a **three-way comparison**: it returns an `int` that is negative, zero or positive, which in practice the engine normalises to `-1`, `0` or `1`. That is exactly the contract of a `usort()` callback, so `fn($a, $b) => $a <=> $b` sorts ascending and swapping the operands sorts descending. It uses the same loose rules as `==` and `<`, so numeric strings compare as numbers. For several keys, compare **arrays**: `[$a['discount'], $a['expires']] <=> [$b['discount'], $b['expires']]` compares element by element and stops at the first difference; put `$b` first on a key to reverse it. Since PHP 8.0 a comparator that returns `$a > $b` (a `bool`) triggers a deprecation, which is a common reason to reach for `<=>`.
code
php · 6 lines<?php
echo 1 <=> 2, ' '; // -1
echo 'b' <=> 'a', ' '; // 1
echo [1, 2, 3] <=> [1, 2, 4], ' '; // -1
echo '10' <=> '9', ' '; // 1: numeric strings compare as numbers
echo '10' <=> '9a', PHP_EOL; // -1: compared as textgo deeper
Recall that <=> returns negative, zero or positive and that it is the one-line body of a usort() callback.
Explain array-to-array comparison, reversing one key by swapping operands, and why bool-returning comparators are deprecated since PHP 8.0.
Spot comparators that truncate float differences or mix types, and choose between array tuples and the ?: chain when sorting large collections.
Decide where ordering belongs: in PHP comparators, in the database query, or in a dedicated sort key, weighing data volume and consistency.
## What <=> computes The **spaceship operator** `$a <=> $b` (added in PHP 7.0) performs a **three-way comparison** in one step: | Relation | Result | |---|---| | `$a < $b` | an `int` less than 0 | | `$a == $b` | `0` | | `$a > $b` | an `int` greater than 0 | The PHP 7.0 migration notes describe the result as `-1`, `0` or `1`, and that is what the engine returns for scalars, but the operator reference promises only the sign, so portable code tests `< 0`, `=== 0` or `> 0` rather than `=== -1`. It uses the **same comparison rules as `==` and `<`**, including type juggling: - `"10" <=> "9"` is `1`: both are numeric strings, so they compare as numbers. - `"10" <=> "9a"` is `-1`: `"9a"` is not numeric, so the strings compare byte by byte and `"1"` sorts before `"9"`. - `"a" <=> "B"` is `1`: string comparison is by byte value, and lowercase letters come after uppercase ones. If you want strictly textual or case-insensitive ordering, use `strcmp()` or `strcasecmp()` instead. ## Why it exists: comparators PHP's user-defined sorts (`usort()`, `uasort()`, `uksort()`) take a callback that receives two elements and must return an `int` with the same sign convention. Before `<=>`, people wrote nested ternaries or, worse, returned a `bool`: ```php usort($coupons, fn($a, $b) => $a->discount > $b->discount); // bool: deprecated since 8.0 usort($coupons, fn($a, $b) => $a->discount <=> $b->discount); // int: correct ``` Since PHP 8.0, a comparator that returns `true` or `false` emits `Deprecated: ... Returning bool from comparison function is deprecated, return an integer less than, equal to, or greater than zero`. PHP still sorts correctly by retrying with swapped operands, but the call costs extra and the notice points to code that relied on an accident. ## Sorting by several fields Arrays compare with `<=>` in a well-defined way: 1. The array with **fewer elements is smaller**. 2. With equal counts, values are compared **key by key, in the order of the left array**. 3. The first pair that differs decides; if all pairs are equal the result is `0`. That makes a tuple of fields a natural multi-key comparator. To sort coupons by **discount descending**, then **expiry ascending**: ```php usort($coupons, fn(Coupon $a, Coupon $b) => [$b->discount, $a->expiresAt] <=> [$a->discount, $b->expiresAt]); ``` Putting `$b` on the left for `discount` reverses that key only. Two cautions apply: both arrays must have the same keys (list arrays built inline do), and every element pair must itself compare sensibly. `DateTimeImmutable` objects do, because the date classes define their own comparison; arbitrary objects of the same class compare property by property, which is rarely what you want. ## The ?: chaining idiom An alternative that evaluates lazily is to chain comparisons with the short ternary: ```php fn($a, $b) => $b->discount <=> $a->discount ?: $a->expiresAt <=> $b->expiresAt ``` This works because `<=>` binds tighter than `?:`, and `0` (equal) is falsy, so the second comparison runs only on a tie. It avoids building two arrays per comparison, which matters in a hot sort of many elements. ## Ties and stability Since PHP 8.0 the built-in sorts are **stable**: elements whose comparator returns `0` keep their original relative order. Two consequences follow for comparators built on `<=>`: 1. A tie is safe to leave as `0`; you do not need an artificial last key just to make output deterministic. 2. A multi-key order can also be produced by sorting twice, least significant key first, although a single tuple comparator is clearer and does one pass. ## Common mistakes - Returning `$a - $b` for floats: the callback result is converted to `int`, so `0.4` becomes `0` and distinct values look equal. `<=>` does not have this problem. - Comparing mixed types and expecting a stable order: loose rules can make the ordering inconsistent across types. Normalise the type before sorting. - Assuming `-1` exactly: compare the sign. - Expecting `<=>` to be strict: it juggles types like `==`, so `0 <=> "0"` is `0`.
- Why can returning $a - $b break a comparator for floats?PHP converts the callback's return value to `int` before using its sign. A difference such as `0.4` becomes `0`, so two different prices are treated as equal and their order is arbitrary. `$a <=> $b` returns the sign directly, so it has no truncation problem.
- Does <=> use strict or loose comparison for mixed types?Loose. It follows the same rules as `==`, `<` and `>`, so `"10" <=> "9"` compares numerically while `"10" <=> "9a"` compares as text. A sort over a column that mixes numeric strings and other strings can therefore look inconsistent. Cast or normalise values to one type before sorting them with `<=>`.
saying these in an interview costs you the question
- Says <=> returns a bool that usort() accepts.
- Relies on the result being exactly -1 rather than negative.
- Thinks <=> compares types strictly like ===.
- Returns $a > $b from a comparator and calls it correct on PHP 8.
- Believes arrays compared with <=> are ordered by their first key only.