PHP で「変数が存在するか」「中身が空かどうか」を確かめる場面はとても多く、isset()・empty()・null 合体演算子(??)が登場します。よく似ているため混同しがちですが、判定するものはそれぞれ違います。この記事では、3つの違いと使い分けを、判定結果の対応表やフォーム値の安全な取り出しといった実践例を交えて、初心者向けに解説します。
目次
そもそも何をチェックしているのか
3つの構文は、ざっくり言うと「変数やキーが使える状態か」を確かめるものですが、見ているポイントが少しずつ違います。まずは役割をひとことで整理しておきましょう。
| 構文 | 判定すること |
|---|---|
isset($x) | 変数が定義済みで、かつ null でないか |
empty($x) | 変数が「空」とみなせる値か(未定義でも警告なし) |
$x ?? 'default' | 未定義または null のとき、右側の既定値を返す |
共通しているのは、いずれも未定義の変数を渡しても警告(Warning)を出さない点です。ふつう未定義の変数を参照すると PHP は警告を出しますが、これらの構文はその存在チェック自体が目的なので、安全に使えるよう特別に扱われています。
isset():定義済みかつ null でないか
isset() は、変数が定義されていて、なおかつ値が null でないときに true を返します。逆に言うと、変数が存在しなかったり、null が入っていたりすると false になります。
<?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 を返してくれます。
<?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(空である)になる値は決まっていて、空文字や 0、null、false、空配列などが該当します。isset() と同じく、未定義の変数を渡しても警告は出ません。
<?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 を「未入力」として弾いてしまいます。
<?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() と同じで、未定義の変数を直接渡しても警告は出ません。
<?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 のときだけ値を代入する」という、よくある処理を短く書けます。
<?php
$options = [];
// キーが無ければ 10 を代入。あれば何もしない
$options['perPage'] ??= 10;
echo $options['perPage']; // 10
// 上と同じ意味の書き方
// $options['perPage'] = $options['perPage'] ?? 10;
?: (短絡三項演算子)との違い
?? と混同しやすいのが、短絡三項演算子 ?:(エルビス演算子とも呼ばれます)です。$a ?: $b は「$a が真なら $a、偽なら $b」を返します。一見すると似ていますが、判定の基準がまったく違います。
?? は「未定義か null か」だけを見るのに対し、?: は値全体を真偽値として評価するため、empty() と同じ感覚で '' や 0 も「偽」とみなして右側を返します。さらに ?: は未定義の変数を渡すと警告を出すという違いもあります。
<?php
$value = '0';
// ?? は null でなければ左側を返す → '0' が返る
echo $value ?? 'default'; // 0
echo "\n";
// ?: は '0' を「偽」とみなす → 'default' が返る
echo $value ?: 'default'; // default
「キーが無い・null のときだけ既定値にしたい」なら ??、「空っぽ(0 や空文字を含む)なら既定値にしたい」なら ?:、と覚えておくと選びやすくなります。
値ごとの判定をまとめて比較する
ここまでの内容を、代表的な値ごとに表で整理しておきます。同じ値でも、isset()・empty()・?? で結果が変わることがひと目で分かります。?? の列は「左側の値がそのまま返るか(=値あり扱い)」を表しています。
| 変数の値 | isset() | empty() | ?? 既定値 の結果 |
|---|---|---|---|
"text"(中身あり) | true | false | 左側の値 |
""(空文字) | true | true | 左側の値(空文字) |
0 | true | true | 左側の値(0) |
"0"(文字列) | true | true | 左側の値(”0″) |
false | true | true | 左側の値(false) |
[](空配列) | true | true | 左側の値(空配列) |
null | false | true | 既定値 |
| 未定義 | false | true | 既定値 |
表を見ると、isset() が false になるのは null と未定義のときだけ、empty() は「偽になる値」すべてで true、?? は isset() と同じ基準で動く、という関係がはっきりします。
フォーム値を安全に受け取る
実務でこれらが活躍する典型例が、$_POST や $_GET からのフォーム値の取り出しです。送信されていないキーをそのまま参照すると警告が出てしまうため、?? で既定値を添えて受け取るのが定番のパターンです。
<?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でない」かを判定する。空文字や0はtrueempty()は「空とみなせる値」かを判定する。"0"まで空扱いになる点に注意??は「未定義・nullのときだけ既定値」を返す。?:は空も既定値にし、未定義だと警告が出る
「存在を確かめたいだけなら isset()」「空かどうか(0 も空扱いでよい)なら empty()」「未定義・null に既定値を添えたいなら ??」と、目的から逆算して選ぶと迷いません。まずはフォーム値の受け取りを ?? で書き換えるところから試してみてください。