PHPの開発において、変数が未定義であったり値がNULLであったりする場合の処理は非常に頻繁に発生します。

以前のPHPでは三項演算子とisset関数を組み合わせて記述していましたが、PHP 7.0以降では「Null合体演算子(??)」の登場により、これらを非常に簡潔に記述できるようになりました。

本記事では、PHPプログラミングにおける効率化の鍵となるNull合体演算子の基本的な使い方から、実務で役立つ応用テクニック、さらには間違いやすい注意点までを詳しく解説します。

Null合体演算子(??)の基本概念

Null合体演算子は、左側のオペランド(値)が「NULLではない」かつ「定義されている」場合にその値を返し、それ以外の場合に右側の値を返す演算子です。

具体的には、$a ?? $b という形式で記述します。

これは従来の isset($a) ? $a : $b と全く同じ挙動を示します。

以下のコードで、実際の動作を確認してみましょう。

PHP
// 変数がNULLの場合
$userName = null;
$displayName = $userName ?? 'ゲストユーザー';

echo $displayName;
実行結果
ゲストユーザー

上記の例では、$userName がNULLであるため、右側の値である「ゲストユーザー」が代入されています。

もし $userName に「山田太郎」という文字列が入っていれば、その値がそのまま採用されます。

この演算子の最大の特徴は、変数が定義されていない場合でも「Notice」エラーが発生しないという点にあります。

三項演算子(?:)やエルビス演算子との違い

PHPには似たような記述方法として「三項演算子」や、その省略形である「エルビス演算子(?:)」が存在します。

これらの違いを正しく理解しておくことは、バグを防ぐために非常に重要です。

主な違いを以下の表にまとめました。

演算子名称判定基準未定義変数の扱い
$a ?? $bNull合体演算子$a が NULL でなければ $aエラーが発生しない
$a ?: $bエルビス演算子$a が true と評価されれば $aNoticeエラーが発生する

エルビス演算子(?:)は、値が「空文字(””)」や「数値の0」の場合も「false」と判定し、右側の値を返します。

対して、Null合体演算子はあくまでNULLか未定義かのみを判定するため、0や空文字を有効な値として扱いたい場合に最適です。

具体的な挙動の比較

次のコードは、値が「0」の場合の挙動の違いを示しています。

PHP
$count = 0;

// Null合体演算子の場合
echo $count ?? 10; // 結果: 0

// エルビス演算子の場合
echo $count ?: 10; // 結果: 10

このように、数値の0を許容したい設定値の取得などでは、Null合体演算子を使用するのが一般的です。

Null合体演算子の連結(チェイン)

Null合体演算子は、複数を連結して記述することが可能です。

これにより、優先順位に従って「最初に見つかったNULL以外の値」を取得するロジックを1行で書くことができます。

PHP
// 優先順位:ユーザー設定 > デフォルト設定 > システム既定値
$userTheme = null;
$defaultTheme = 'Dark';
$systemTheme = 'Light';

$activeTheme = $userTheme ?? $defaultTheme ?? $systemTheme;

echo $activeTheme;
実行結果
Dark

この例では、$userTheme がNULLであるため次に $defaultTheme を参照し、それがNULLでないため「Dark」が採用されています。

条件分岐(if文)を重ねる必要がないため、コードの可読性が大幅に向上します。

Null合体代入演算子(??=)の活用

PHP 7.4からは、さらに便利な「Null合体代入演算子(??=)」が導入されました。

これは、「もし変数がNULLまたは未定義なら、値を代入する」という処理を短縮して書くためのものです。

PHP
$config = [];

// 従来の書き方
if (!isset($config['timeout'])) {
    $config['timeout'] = 30;
}

// Null合体代入演算子を使用した書き方
$config['timeout'] ??= 30;

echo $config['timeout'];
実行結果
30

配列の初期値をセットする場合などに非常に重宝します。

すでに値が存在する場合は代入が行われないため、既存の値を上書きしてしまう心配もありません。

実務での活用シーン:ユーザー入力の処理

最も実用的なケースの一つは、HTMLフォームから送信されたデータ($_GETや$_POST)の処理です。

特定のキーが存在しない可能性がある場合でも、Null合体演算子を使えばスマートに記述できます。

PHP
// クエリパラメータ 'page' があればその値を、なければ 1 を代入
$page = $_GET['page'] ?? 1;

// 複数の入力をまとめて処理する例
$search = [
    'keyword' => $_POST['keyword'] ?? '',
    'category' => $_POST['category'] ?? 'all',
    'sort' => $_POST['sort'] ?? 'newest'
];

print_r($search);

この方法を使えば、isset() によるチェックを繰り返す必要がなくなり、タイプミスの削減や保守性の向上に繋がります。

使用上の注意点と落とし穴

非常に便利なNull合体演算子ですが、使用にあたって注意すべき点がいくつかあります。

1. NULL以外は「存在」とみなされる

先述の通り、空文字列 "" や空の配列 []、数値の 0、論理値の false は「NULLではない」ため、これらは有効な値として返されます。

もし「値が空の場合もデフォルト値にしたい」という意図がある場合は、Null合体演算子ではなく三項演算子と empty() 関数を組み合わせるべきです。

2. ネストされた配列へのアクセス

Null合体演算子は、深い階層の配列アクセスに対しても安全に使用できます。

PHP
$data = [
    'user' => [
        'profile' => null
    ]
];

// 途中のキーが存在しなくてもエラーにならない
$city = $data['user']['address']['city'] ?? '未登録';

echo $city;

このように、'address' というキーが存在しない場合でも、自動的に右側の値が返されます。

ただし、意図しない構造のデータを許容してしまう可能性もあるため、データの整合性が重要な箇所では注意が必要です。

まとめ

PHPのNull合体演算子(??)は、冗長になりがちなNULLチェックを簡潔に記述するための非常に強力なツールです。

「変数が定義されているか」「NULLではないか」という2点を同時に、かつ安全に判定できる点が最大のメリットです。

最後に、本記事のポイントを振り返ります。

  • $a ?? $bisset($a) ? $a : $b の短縮形である。
  • 未定義の変数にアクセスしてもNoticeエラーが発生しない。
  • 0や空文字、falseはNULLではないため、左側の値として採用される。
  • 複数を繋げて記述することで、優先順位に基づいた値の取得が可能。
  • PHP 7.4以降は ??= による簡潔な代入も可能。

現代のPHP開発において、Null合体演算子の活用は標準的な作法となっています。

特性を正しく理解し、読みやすく、かつ堅牢なコードを記述するために積極的に活用していきましょう。