1. ホーム
  2. PHP

【PHP】isset・empty・null合体演算子(??)の違いと使い分け

Share

PHP で「変数が存在するか」「中身が空かどうか」を確かめる場面はとても多く、isset()empty()・null 合体演算子(??)が登場します。よく似ているため混同しがちですが、判定するものはそれぞれ違います。この記事では、3つの違いと使い分けを、判定結果の対応表やフォーム値の安全な取り出しといった実践例を交えて、初心者向けに解説します。

そもそも何をチェックしているのか

3つの構文は、ざっくり言うと「変数やキーが使える状態か」を確かめるものですが、見ているポイントが少しずつ違います。まずは役割をひとことで整理しておきましょう。

構文判定すること
isset($x)変数が定義済みで、かつ null でないか
empty($x)変数が「空」とみなせる値か(未定義でも警告なし)
$x ?? 'default'未定義または null のとき、右側の既定値を返す

共通しているのは、いずれも未定義の変数を渡しても警告(Warning)を出さない点です。ふつう未定義の変数を参照すると PHP は警告を出しますが、これらの構文はその存在チェック自体が目的なので、安全に使えるよう特別に扱われています。

isset():定義済みかつ null でないか

isset() は、変数が定義されていて、なおかつ値が null でないときに true を返します。逆に言うと、変数が存在しなかったり、null が入っていたりすると false になります。

isset.php
<?php
$name = 'Taro';
$empty = '';
$nothing = null;

var_dump(isset($name));    // bool(true)  値があるので true
var_dump(isset($empty));   // bool(true)  空文字でも「定義済み」なので true
var_dump(isset($nothing)); // bool(false) null なので false
var_dump(isset($undefined)); // bool(false) 未定義でも警告なし

ここで押さえておきたいのは、空文字 ''0 も「値が入っている」とみなされ isset()true を返すという点です。「空かどうか」ではなく「定義されていて null でないか」を見ている、という感覚を持っておくと混乱しません。

配列のキーが存在するか調べる

isset() は配列のキーの存在チェックでも頻繁に使われます。指定したキーが存在しない場合でも、警告を出さずに false を返してくれます。

isset_array.php
<?php
$user = ['name' => 'Taro', 'age' => null];

var_dump(isset($user['name'])); // bool(true)
var_dump(isset($user['age']));  // bool(false) 値が null なので false
var_dump(isset($user['email'])); // bool(false) キーが無くても警告なし

注意したいのは $user['age'] です。キー自体は存在しますが、値が null のため isset()false を返します。「キーがあるかどうか」だけを厳密に知りたい場合は、array_key_exists() を使うと null が入っていても true になります。

empty():空とみなせる値か

empty() は、変数が「空」とみなせる値かどうかを判定します。true(空である)になる値は決まっていて、空文字や 0nullfalse、空配列などが該当します。isset() と同じく、未定義の変数を渡しても警告は出ません。

empty.php
<?php
var_dump(empty(''));      // bool(true)  空文字は空
var_dump(empty(0));       // bool(true)  0 は空
var_dump(empty('0'));     // bool(true)  文字列の "0" も空!
var_dump(empty(null));    // bool(true)  null は空
var_dump(empty(false));   // bool(true)  false は空
var_dump(empty([]));      // bool(true)  空配列は空

var_dump(empty('Taro'));  // bool(false) 中身があるので空ではない
var_dump(empty(1));       // bool(false) 0 以外の数値は空ではない

「空とみなす値」の判定基準は、その値を真偽値に変換したときに false になるか、と同じです。つまり if (!$x) と書いたときに条件が成立する値が、そのまま empty() でも true になる、と考えると覚えやすいでしょう。

文字列の “0” が空扱いになる落とし穴

empty() でもっとも注意したいのが、文字列の "0" まで「空」と判定されることです。これは PHP が "0" を真偽値に変換すると false になる、という仕様に由来します。

たとえば、入力された値が「0個」「0円」のように 0 を正当な入力として受け付けたい場合、empty() で空判定をしてしまうと、ユーザーが入力した 0 を「未入力」として弾いてしまいます。

empty_pitfall.php
<?php
// フォームから送られてきた在庫数(文字列)
$stock = '0';

// これだと「0個」を未入力として弾いてしまう
if (empty($stock)) {
    echo '在庫数を入力してください'; // ここが実行されてしまう
}

// 「未入力かどうか」を見たいなら、空文字かどうかを直接判定する
if ($stock === '') {
    echo '在庫数を入力してください';
} else {
    echo "在庫は {$stock} 個です"; // 在庫は 0 個です
}

0"0" を有効な値として扱いたいときは、empty() ではなく、=== ''=== null のように意図した値だけを厳密に比較するのが安全です。empty() は「とにかく中身が空っぽなら何でも弾きたい」というときに向いています。

?? :未定義・null のときの既定値

null 合体演算子(??)は、PHP 7.0 で追加された構文です。$a ?? $b と書くと、$a が定義済みで null でなければ $a を、そうでなければ $b を返します。判定の基準は isset() と同じで、未定義の変数を直接渡しても警告は出ません。

null_coalescing.php
<?php
$config = ['theme' => 'dark'];

// キーがあればその値、なければ 'light'
$theme = $config['theme'] ?? 'light';
echo $theme; // dark

// キーが無い場合は既定値が使われる
$lang = $config['lang'] ?? 'ja';
echo $lang; // ja

// 未定義の変数を直接渡しても警告は出ない
$name = $undefinedVar ?? 'ゲスト';
echo $name; // ゲスト

?? が見ているのは「未定義か null か」だけです。そのため、空文字 ''0 が入っている場合は、それを「値あり」として返します。「null のときだけ既定値を入れたい」という場面にぴったりです。

代入する ??= も使える

PHP 7.4 以降では、null 合体代入演算子 ??= も使えます。「変数が未定義または null のときだけ値を代入する」という、よくある処理を短く書けます。

null_coalescing_assign.php
<?php
$options = [];

// キーが無ければ 10 を代入。あれば何もしない
$options['perPage'] ??= 10;
echo $options['perPage']; // 10

// 上と同じ意味の書き方
// $options['perPage'] = $options['perPage'] ?? 10;

?: (短絡三項演算子)との違い

?? と混同しやすいのが、短絡三項演算子 ?:(エルビス演算子とも呼ばれます)です。$a ?: $b は「$a が真なら $a、偽なら $b」を返します。一見すると似ていますが、判定の基準がまったく違います。

?? は「未定義か null か」だけを見るのに対し、?:値全体を真偽値として評価するため、empty() と同じ感覚で ''0 も「偽」とみなして右側を返します。さらに ?: は未定義の変数を渡すと警告を出すという違いもあります。

coalescing_vs_ternary.php
<?php
$value = '0';

// ?? は null でなければ左側を返す → '0' が返る
echo $value ?? 'default'; // 0

echo "\n";

// ?: は '0' を「偽」とみなす → 'default' が返る
echo $value ?: 'default'; // default

「キーが無い・null のときだけ既定値にしたい」なら ??、「空っぽ(0 や空文字を含む)なら既定値にしたい」なら ?:、と覚えておくと選びやすくなります。

値ごとの判定をまとめて比較する

ここまでの内容を、代表的な値ごとに表で整理しておきます。同じ値でも、isset()empty()?? で結果が変わることがひと目で分かります。?? の列は「左側の値がそのまま返るか(=値あり扱い)」を表しています。

変数の値isset()empty()?? 既定値 の結果
"text"(中身あり)truefalse左側の値
""(空文字)truetrue左側の値(空文字)
0truetrue左側の値(0)
"0"(文字列)truetrue左側の値(”0″)
falsetruetrue左側の値(false)
[](空配列)truetrue左側の値(空配列)
nullfalsetrue既定値
未定義falsetrue既定値

表を見ると、isset()false になるのは null と未定義のときだけ、empty() は「偽になる値」すべてで true??isset() と同じ基準で動く、という関係がはっきりします。

フォーム値を安全に受け取る

実務でこれらが活躍する典型例が、$_POST$_GET からのフォーム値の取り出しです。送信されていないキーをそのまま参照すると警告が出てしまうため、?? で既定値を添えて受け取るのが定番のパターンです。

form.php
<?php
// 送信されていなくても警告なしで受け取れる
$name = $_POST['name'] ?? '';
$keyword = $_GET['q'] ?? '';
$page = $_GET['page'] ?? 1;

// 必須項目のチェックは、空文字かどうかで判定する
// (empty だと "0" を弾いてしまうため)
if ($name === '') {
    echo '名前を入力してください';
} else {
    echo htmlspecialchars($name) . ' さん、こんにちは';
}

まず ?? でキーの有無を吸収して既定値(空文字や初期ページ番号)を用意し、そのうえで「入力が必須かどうか」を === '' で判定しています。empty() でまとめて判定したくなりますが、"0" のような正当な入力まで弾いてしまうので、必須チェックでは空文字との厳密比較が安全です。

まとめ

3つの構文は、どれも「変数が使える状態か」を確かめるものですが、見ているポイントが違います。最後に要点を振り返っておきましょう。

  • isset() は「定義済みかつ null でない」かを判定する。空文字や 0true
  • empty() は「空とみなせる値」かを判定する。"0" まで空扱いになる点に注意
  • ?? は「未定義・null のときだけ既定値」を返す。?: は空も既定値にし、未定義だと警告が出る

「存在を確かめたいだけなら isset()」「空かどうか(0 も空扱いでよい)なら empty()」「未定義・null に既定値を添えたいなら ??」と、目的から逆算して選ぶと迷いません。まずはフォーム値の受け取りを ?? で書き換えるところから試してみてください。

参考リンク