2026/10/20

PHPの宇宙船演算子(<=>)とusortソート実践テクニック

PHPの宇宙船演算子(<=>)で迷わない覚え方とusortソート実践術

「echo 1 <=> 2; の出力って何だっけ?」と聞かれて、一瞬「1だっけ?-1だっけ?」と固まってしまったことはないでしょうか。宇宙船演算子(Spaceship Operator)はPHP 7.0で登場した比較演算子で、大小比較の結果を -1、0、1 で返します。今回は、現場で絶対に迷わなくなる「引き算ルール」と、実務の usort や複数キー並び替えでコードを劇的に短くするテクニック、実際に踏んだ落とし穴について自分の体験ベースでまとめてみました。

タイムラインで見かけたクイズに一瞬フリーズした日

先日、お昼休みにXを眺めていたら「PHPクイズ!宇宙船演算子の出力わかる?」という投稿が目に留まりました。コードはごく普通の echo 1 <=> 2; で、選択肢は 0、1、-1 の3択です。

自分はPHP歴もそれなりに長く、普段から普通にコードを書いています。もちろん宇宙船演算子の存在も知っているし、実務でも使ったことがあるはずでした。それなのに、いざ単体で「出力される数値はどれ?」と聞かれた瞬間、頭の中で「1が左で2が右だから……小さい方が先?あれ、どっちがマイナスだっけ?」と情けないことに数秒フリーズしてしまったんです。

正解は -1 なんですが、普段フレームワークの便利なコレクション操作に頼りすぎていて、素のPHPの比較ロジックを感覚だけで書いていたなと痛感しました。こういう基本構文こそ、理屈と直感的なイメージで頭に定着させておくのが大事だなと感じた出来事です。

基本の3パターンと絶対に忘れない「引き算」の覚え方

宇宙船演算子 $a <=> $b は、左辺と右辺を比較して3つの整数を返します。仕様としては以下の通りです。

条件 返り値 具体例
左辺 < 右辺(左が小さい) -1 1 <=> 2 → -1
左辺 == 右辺(等しい) 0 2 <=> 2 → 0
左辺 > 右辺(左が大きい) 1 2 <=> 1 → 1

この表を暗記しようとすると、「左が小さいときはマイナスだっけ?」と混乱してしまいがちです。そこでおすすめしたいのが、「左引く右($a - $b)の符号」と覚える方法です。

記号 <=> をマイナス記号 - に見立てて、小学校の算数の引き算をイメージしてみます。

// 1 <=> 2 を引き算に置き換える
1 - 2 = -1  // 答えはマイナスなので「-1」

// 2 <=> 1 を引き算に置き換える
2 - 1 = +1  // 答えはプラスなので「1」

// 2 <=> 2 を引き算に置き換える
2 - 2 = 0   // 答えはゼロなので「0」

引き算した結果の符号と、宇宙船演算子の戻り値がぴったり一致します。これに気づいてからは、どんなコードを見ても「左から右を引いたらマイナスだから -1」と一瞬で判断できるようになりました。暗記ではなく計算イメージなので、ど忘れする心配もありません。

ちなみに「宇宙船演算子」という名前は、記号の形がSF映画に出てくる宇宙船に似ていることから名付けられたそうです。PerlやRubyにもありますが、PHPには7.0から導入されました。

実務の usort が劇的にスッキリした

この演算子の一番の出番は、何と言っても配列のカスタムソート関数 usort() です。

昔のPHP 5時代、連想配列を特定のキーでソートしようとすると、毎回こんな冗長なコードを書いていました。

// 昔の書き方:比較処理だけで行数を食う
$users = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob',   'age' => 20],
    ['name' => 'Carol', 'age' => 30],
];

usort($users, function ($a, $b) {
    if ($a['age'] == $b['age']) {
        return 0;
    }
    return ($a['age'] < $b['age']) ? -1 : 1;
});

三項演算子でネストさせると読みにくいし、if文で書くと縦に長くなって面倒でした。昇順・降順の条件を追うのも疲れます。

これが宇宙船演算子とアロー関数(PHP 7.4+)を使うと、たった1行で書けます。

// 宇宙船演算子 + アロー関数で書く現代のPHP
$users = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob',   'age' => 20],
    ['name' => 'Carol', 'age' => 30],
];

// 年齢の昇順で並び替え
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);

比較ロジックが $a['age'] <=> $b['age'] だけで済みます。降順にしたいなら左右を入れ替えて $b['age'] <=> $a['age'] にするだけです。初めてこの書き方を見たときは、コードの見通しの良さにかなりテンションが上がりました。

複数条件ソートでの神技

実務でさらに重宝するのが、「第1条件が同じなら第2条件で並べる」という複数キーのソートです。SQLの ORDER BY priority DESC, id ASC みたいな並び順を配列に対して行いたいケースですね。

これも宇宙船演算子なら、スマートに書く方法が2つあります。

テクニック1: エルビス演算子(?:)でつなぐ

宇宙船演算子は「等しい」ときに 0 を返します。PHPでは数値の 0 は false 扱いなので、エルビス演算子 ?: で次々とフォールバックできます。

// 優先度(降順) → 同じならID(昇順)でソート
usort($items, function ($a, $b) {
    // 0(等しい)なら右辺の評価に進む
    return ($b['priority'] <=> $a['priority'])
        ?: ($a['id'] <=> $b['id']);
});

優先度が異なればその比較結果(-1か1)がそのまま返り、同じ値(0)だった場合だけ右辺のID比較が実行されます。条件が3つ以上あっても ?: でつなげるだけなので、ロジックが非常に読みやすいです。

テクニック2: 配列同士をそのまま比べる

実は宇宙船演算子、配列同士の比較にもそのまま使えます。要素数が同じ配列なら、先頭から順に比較して差がついたところで結果を返してくれます。

// 配列比較を使った複数条件ソート
usort($items, fn($a, $b) => 
    [$b['priority'], $a['id']] <=> [$a['priority'], $b['id']]
);

優先度とIDを配列に入れて <=> で比べるだけです。アロー関数1行で複数条件ソートが完結します。これを知ったときは「こんな書き方ができたのか」と驚きました。

実際に踏んだ落とし穴と注意点

便利な宇宙船演算子ですが、実務で実際に使っていく中で冷や汗をかいた注意点もあります。特に「小数の扱い」と「PHP 8での比較仕様の変更」です。

float(小数の計算)を引き算キャストする昔のバグを防げる

昔のコードで、小数のソートを return (int)($a - $b); のように引き算結果を整数キャストして済ませようとしているものを見かけたことがあります。しかし 1.2 - 1.8 は -0.6 なので、(int) にキャストすると 0 に切り捨てられてしまい、ソート順が狂ってしまいます。

宇宙船演算子は内部で浮動小数点数の大小を正しく判定して -1, 0, 1 を返すので、こうしたキャストによるバグを綺麗に防ぐことができます。小数の比較こそ宇宙船演算子の独壇場です。

PHP 8.0による「数値と文字列」の比較仕様変更

宇宙船演算子は緩やかな比較を行うため、異なる型同士を比べる場合は注意が必要です。PHP 7からPHP 8へのバージョンアップで、数値と文字列の比較ルールが大きく変わりました。

// 0 <=> "foo" の結果はどうなる?

// PHP 7系: 文字列 "foo" が数値 0 に変換され、0 == 0 となり結果は 0
echo 0 <=> "foo"; // 0

// PHP 8系: 数値 0 が文字列 "0" に変換され、文字列比較になる
// そのため "0" < "foo" と判定されて結果は -1
echo 0 <=> "foo"; // -1

PHP 7時代は文字列が 0 に強制変換されていたため、0 <=> "apple" が等しい(0)と判定される落とし穴がありました。PHP 8以降は是正されましたが、型が揃っていない配列をソートするときは、事前に型を揃えるか strcmp() を使うのが安全です。

nullが混ざったデータの挙動

DBから取得したデータに null が混ざっている場合、PHPの数値比較では null は 0 相当として扱われます。もし「nullのデータは末尾に送りたい」という要件があるなら、単純な宇宙船演算子だけでは対応できず、三項演算子でnullチェックを明示的に挟む必要があります。

記号の見た目に怯えず使ってみる

宇宙船演算子 <=> は、最初は記号のインパクトが強くて身構えてしまうかもしれません。自分も最初は「なんか癖の強そうな記号だな」と思っていました。ですが、「引き算の符号」という覚え方さえ知ってしまえば、ソート処理をこれほど短く美しく書ける道具はありません。

昔書いた長い usort() のコードが手元にあるなら、宇宙船演算子で書き直してみると行数が一気に減って見通しが良くなるのでかなり爽快だったりします。