- PHP
PHP isset・empty・is_nullの違い|null判定の早見表
issetは「定義されていて、かつnullでない」ときにtrue、emptyは「未定義、またはfalseとみなされる値」のときにtrue、is_nullは「値がnull」のときにtrueを返します。 3つは似ていますが、"0" や 0、空の配列、未定義の変数を渡したときの結果がそれぞれ異なります。
この記事では、未定義・null・""・"0"・0・0.0・false・[]・" " などの値を PHP 8.4.21 で実際に実行した結果 を早見表にしました。あわせて、PHP 8 で Warning になった Undefined variable と Undefined array key の再現、array_key_exists との違い、??・??=・?-> への書き換え、フォーム入力の "0" が empty で未入力扱いになる例と直し方を、そのまま貼って動くコードで紹介します。
isset・empty・is_nullの早見表
まずは結論の表です。下の「実行コード」で出力した結果をそのまま整理しています(PHP 8.4.21)。
| 値 | isset($v) | empty($v) | is_null($v) | $v === null | $v ?? 'D' | if ($v) |
|---|---|---|---|---|---|---|
| 未定義の変数 | false | true | true(Warningあり) | true(Warningあり) | 'D' | false(Warningあり) |
null |
false | true | true | true | 'D' | false |
""(空文字) |
true | true | false | false | "" |
false |
"0"(文字列のゼロ) |
true | true | false | false | "0" |
false |
0 |
true | true | false | false | 0 |
false |
0.0 |
true | true | false | false | 0.0 |
false |
false |
true | true | false | false | false |
false |
[](空の配列) |
true | true | false | false | [] |
false |
" "(空白1文字) |
true | false | false | false | " " |
true |
"0.0" |
true | false | false | false | "0.0" |
true |
"a" |
true | false | false | false | "a" |
true |
[0] |
true | false | false | false | [0] |
true |
表から読み取れる関係は次の3つです。
| 覚え方 | 内容 |
|---|---|
isset($v) と $v ?? 'D' は同じ基準 |
どちらも「未定義またはnull」だけを特別扱いします。"" や 0 はそのまま通します |
empty($v) は !isset($v) || !$v と同じ |
未定義でもWarningを出さずに、if ($v) の逆を返します |
is_null($v) と $v === null は同じ結果 |
どちらも未定義の変数を渡すとWarningが出ます。Warningを出さないのは isset・empty・?? です |
やりたいことから選ぶときは、次の表が目安になります。
| やりたいこと | 書き方 |
|---|---|
| 変数や配列キーがあって、nullでないか調べたい | isset($a['key']) |
| なければ既定値を使いたい | $a['key'] ?? '既定値' |
| なければ代入しておきたい | $a['key'] ??= '既定値' |
| 値がnullかどうかだけ調べたい(変数は必ず定義済み) | $v === null |
| 配列にキーがあるか調べたい(値がnullでも「ある」としたい) | array_key_exists('key', $a) |
| 配列が空かどうか調べたい | $list === [] または count($list) === 0 |
| フォームの入力が空かどうか調べたい | trim($v ?? '') === '' |
| nullかもしれないオブジェクトのプロパティを読みたい | $user?->profile?->city |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP isset・empty・is_nullの違い|null判定の早見表」
早見表を出力したコード(コピペで再現できます)
早見表は次のコードで出力しました。table.php という名前で保存し、php table.php で実行できます。
<?php
// table.php : 値ごとの判定結果を一覧にする(PHP 8.4 で実行)
error_reporting(E_ALL);
set_error_handler(fn() => true); // 未定義変数の Warning を表に混ぜない
function b(bool $x): string { return $x ? 'true ' : 'false'; }
$cases = [
'null' => null,
'""' => '',
'"0"' => '0',
'0' => 0,
'0.0' => 0.0,
'false' => false,
'[]' => [],
'" "' => ' ',
'"0.0"' => '0.0',
'"a"' => 'a',
'[0]' => [0],
];
printf("%-8s| %-6s| %-6s| %-8s| %-9s| %-10s| %s\n",
'value', 'isset', 'empty', 'is_null', '=== null', "?? 'D'", 'if($v)');
echo str_repeat('-', 64), "\n";
// 未定義の変数
printf("%-8s| %s | %s | %s | %s | %-10s| %s\n", '(undef)',
b(isset($undef)), b(empty($undef)), b(is_null($undef)), b($undef === null),
var_export($undef ?? 'D', true), b((bool)$undef));
foreach ($cases as $label => $v) {
printf("%-8s| %s | %s | %s | %s | %-10s| %s\n", $label,
b(isset($v)), b(empty($v)), b(is_null($v)), b($v === null),
str_replace("\n", '', var_export($v ?? 'D', true)), b((bool)$v));
}
実行結果(PHP 8.4.21):
value | isset | empty | is_null | === null | ?? 'D' | if($v)
----------------------------------------------------------------
(undef) | false | true | true | true | 'D' | false
null | false | true | true | true | 'D' | false
"" | true | true | false | false | '' | false
"0" | true | true | false | false | '0' | false
0 | true | true | false | false | 0 | false
0.0 | true | true | false | false | 0.0 | false
false | true | true | false | false | false | false
[] | true | true | false | false | array () | false
" " | true | false | false | false | ' ' | true
"0.0" | true | false | false | false | '0.0' | true
"a" | true | false | false | false | 'a' | true
[0] | true | false | false | false | array ( 0 => 0,)| true
"0" は empty で true、"0.0" と " " は empty で false になる点が、いちばん間違えやすいところです。PHPが「falseとみなす文字列」は空文字 "" と "0" の2つだけ、と覚えておくと整理しやすくなります。
3つの関数の役割を整理する
isset:定義されていて、nullでないか
isset は関数のように見えますが、PHPの言語構造(言語に組み込まれた構文)です。そのため、未定義の変数や存在しない配列キーを渡してもWarningが出ません。
<?php
$user = ['id' => 1, 'nickname' => null];
var_dump(isset($name)); // 未定義の変数 → false
var_dump(isset($user['id'])); // true
var_dump(isset($user['nickname'])); // 値が null → false
var_dump(isset($user['email'])); // キーなし → false
var_dump(isset($user['id'], $user['email'])); // 全部そろっていれば true → false
複数の引数を渡すと、すべてが「定義済みでnullでない」ときだけtrue になります。必須項目がそろっているかを1行で確かめたいときに便利です。
empty:空とみなせる値か
empty も言語構造です。未定義でもWarningを出さず、「値がfalseとみなされるかどうか」を返します。trueになるのは、未定義・null・""・"0"・0・0.0・false・[] の8種類 です。
便利な反面、0 や "0" を「入力された値」として扱いたい場面では意図と違う結果になります。数量・点数・フラグのように ゼロが意味を持つ値には empty を使わず、後述の厳密な比較に置き換える と安心です。
is_null:値がnullか
is_null は通常の関数で、$v === null と同じ結果を返します。関数なので、未定義の変数を渡すとWarningが出ます(結果はtrue)。
<?php
error_reporting(E_ALL); ini_set('display_errors', '1');
var_dump(is_null($undef));
var_dump($undef === null);
実行結果(PHP 8.4.21):
Warning: Undefined variable $undef in misc.php on line 4
bool(true)
Warning: Undefined variable $undef in misc.php on line 6
bool(true)
変数が必ず定義されている場面(関数の引数、初期化済みのプロパティなど)では $v === null が読みやすく、定義されているか分からない場面では isset や ?? が向いています。
== null はゆるい比較になる
== null は型をそろえてから比べるため、null以外でもtrueになる値があります。
"" == null : true === null : false
"0" == null : false === null : false
0 == null : true === null : false
false == null : true === null : false
[] == null : true === null : false
null == null : true === null : true
"0" == null だけがfalseになるなど、覚えにくい結果になります。nullの判定は === で書く と、表を思い出す必要がなくなります。
PHP 8でWarningになったUndefinedエラーを再現する
未定義の変数・配列キー・プロパティを読むとどうなるか
PHP 8.0 から、未定義の変数・配列キー・プロパティの読み取りは Notice から Warning に引き上げられました。次のコードで再現できます。
<?php
// warn.php : 未定義の変数・配列キー・プロパティを読むとどうなるか
error_reporting(E_ALL);
ini_set('display_errors', '1');
echo "--- 1) 未定義の変数\n";
var_dump($name);
echo "--- 2) 未定義の配列キー\n";
$user = ['id' => 1];
var_dump($user['email']);
echo "--- 3) 未定義のプロパティ\n";
$obj = new stdClass();
var_dump($obj->email);
echo "--- 4) null を配列として読む\n";
$none = null;
var_dump($none['email']);
echo "--- 5) null のプロパティを読む\n";
var_dump($none->email);
echo "--- 6) null のメソッドを呼ぶ\n";
try {
$none->getEmail();
} catch (Error $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
実行結果(PHP 8.4.21。ファイルのパスは省略しています):
--- 1) 未定義の変数
Warning: Undefined variable $name in warn.php on line 7
NULL
--- 2) 未定義の配列キー
Warning: Undefined array key "email" in warn.php on line 11
NULL
--- 3) 未定義のプロパティ
Warning: Undefined property: stdClass::$email in warn.php on line 15
NULL
--- 4) null を配列として読む
Warning: Trying to access array offset on null in warn.php on line 19
NULL
--- 5) null のプロパティを読む
Warning: Attempt to read property "email" on null in warn.php on line 22
NULL
--- 6) null のメソッドを呼ぶ
Error: Call to a member function getEmail() on null
1〜5はWarningを出したあと NULL として処理が続きます。6の nullに対するメソッド呼び出しだけは Error 例外 になり、捕捉しなければそこで処理が止まります。
バージョン別のエラーレベル
| 操作 | PHP 7.4 まで | PHP 8.0 以降 | 8.4.21 でのメッセージ |
|---|---|---|---|
| 未定義の変数を読む | Notice | Warning | Undefined variable $name |
| 未定義の配列キーを読む | Notice | Warning | Undefined array key "email" |
| 未定義のプロパティを読む | Notice | Warning | Undefined property: stdClass::$email |
| nullを配列として読む | Notice(7.4で追加) | Warning | Trying to access array offset on null |
| nullのプロパティを読む | Notice | Warning | Attempt to read property "email" on null |
| nullのメソッドを呼ぶ | Error 例外 | Error 例外 | Call to a member function getEmail() on null |
上の3行(変数・配列キー・プロパティ)が Notice から Warning に変わったことは、PHPマニュアルの「PHP 7.4.x から PHP 8.0.x への移行 ― 下位互換性のない変更点」を今回開いて確認しました。PHP 7.4 での実行は今回の環境では未実行 です。PHP 7 系では Notice: Undefined variable: name、Notice: Undefined index: email という文言だったことが広く知られており、検索でこの文言が出てきた場合は PHP 7 以前の情報だと判断できます。
| よく見る文言 | 出るバージョン | 意味 |
|---|---|---|
Notice: Undefined index: xxx |
PHP 7.4 まで | 配列に文字列キー xxx がない |
Notice: Undefined offset: 3 |
PHP 7.4 まで | 配列に数値キー 3 がない |
Warning: Undefined array key "xxx" |
PHP 8.0 以降 | 上の2つが統合された文言 |
Notice: Undefined variable: xxx |
PHP 7.4 まで | 変数が未定義 |
Warning: Undefined variable $xxx |
PHP 8.0 以降 | 同上($ 付きの表記に) |
エラーレベルが変わっても、処理が続き、値が NULL になる動作は同じ です。変わるのは、error_reporting の設定で Notice だけを外していた環境でも表示・記録されるようになる点です。
Warningを出さずに読む書き方
次の書き方は、未定義でもWarningを出しません。
<?php
// nowarn.php : Warning を出さずに読む書き方
error_reporting(E_ALL);
ini_set('display_errors', '1');
$user = ['id' => 1, 'nickname' => null];
$none = null;
var_dump(isset($name)); // 未定義の変数
var_dump(isset($user['email'])); // 未定義のキー
var_dump(empty($user['email']));
var_dump($user['email'] ?? '(なし)');
var_dump($user['profile']['city'] ?? '(なし)'); // 何段でも
var_dump($none?->email); // nullsafe 演算子
var_dump($none?->getProfile()?->city);
var_dump(isset($user['id'], $user['email'])); // 複数まとめて
echo "Warning は 1 つも出ません\n";
実行結果(PHP 8.4.21):
bool(false)
bool(false)
bool(true)
string(8) "(なし)"
string(8) "(なし)"
NULL
NULL
bool(false)
Warning は 1 つも出ません
$user['profile']['city'] ?? '(なし)' のように、途中のキーがなくても ?? は最後まで安全に評価 してくれます。
Warningを例外に変えて気づきやすくする
開発中は、Warningを例外に変換しておくと、未定義のキーを読んだ場所で処理が止まり、原因の行がすぐ分かります。
<?php
// err.php : Warning を例外に変えて、見落としを防ぐ
set_error_handler(function (int $no, string $msg, string $file, int $line) {
throw new ErrorException($msg, 0, $no, $file, $line);
});
$user = ['id' => 1];
try {
echo $user['email'];
} catch (ErrorException $e) {
echo '捕捉: ', $e->getMessage(), ' (severity=', $e->getSeverity(), ', E_WARNING=', E_WARNING, ")\n";
}
実行結果(PHP 8.4.21):
捕捉: Undefined array key "email" (severity=2, E_WARNING=2)
LaravelやSymfonyなどのフレームワークは、標準でこの変換を行っています。素のPHPで書くときも、同じ仕組みを入れておくと原因の特定が早くなります。
issetとarray_key_existsの違い
isset は「キーがあり、値がnullでない」とき、array_key_exists は「キーがある」ときにtrue を返します。値がnullのキーで結果が分かれます。
<?php
// ake.php : isset と array_key_exists の違い
$row = ['id' => 7, 'deleted_at' => null];
echo "キー deleted_at(値は null)\n";
var_dump(isset($row['deleted_at']));
var_dump(array_key_exists('deleted_at', $row));
var_dump($row['deleted_at'] ?? 'default');
echo "キー memo(存在しない)\n";
var_dump(isset($row['memo']));
var_dump(array_key_exists('memo', $row));
echo "3 つの状態を区別する\n";
function state(array $a, string $k): string {
if (!array_key_exists($k, $a)) return 'キーなし';
if ($a[$k] === null) return 'キーあり・値は null';
return 'キーあり・値あり';
}
foreach (['id', 'deleted_at', 'memo'] as $k) {
echo str_pad($k, 11), ' => ', state($row, $k), "\n";
}
echo "オブジェクトのプロパティ\n";
class Member { public ?string $nickname = null; public string $name = 'Sato'; }
$m = new Member();
var_dump(isset($m->nickname));
var_dump(property_exists($m, 'nickname'));
var_dump(property_exists($m, 'age'));
実行結果(PHP 8.4.21):
キー deleted_at(値は null)
bool(false)
bool(true)
string(7) "default"
キー memo(存在しない)
bool(false)
bool(false)
3 つの状態を区別する
id => キーあり・値あり
deleted_at => キーあり・値は null
memo => キーなし
オブジェクトのプロパティ
bool(false)
bool(true)
bool(false)
| 状態 | isset | array_key_exists | ?? '既定値' |
|---|---|---|---|
| キーあり・値あり | true | true | 元の値 |
| キーあり・値はnull | false | true | 既定値 |
| キーなし | false | false | 既定値 |
使い分けの目安は次のとおりです。
| 場面 | 向いている書き方 | 理由 |
|---|---|---|
| 値があれば使い、なければ既定値 | ?? |
nullとキーなしを同じ扱いにできる |
| データベースの行でNULLの列を区別したい | array_key_exists |
「列はあるが値がNULL」を区別できる |
| JSONで「項目を送っていない」と「nullを送った」を区別したい | array_key_exists |
部分更新で、nullを「消す」の意味に使える |
| オブジェクトでプロパティの有無を調べたい | property_exists |
値がnullでもtrueになる |
issetの三項演算子を ?? ・??= ・?-> に書き換える
PHP 7.0 で ??(null合体演算子)、PHP 7.4 で ??=(null合体代入演算子)、PHP 8.0 で ?->(nullsafe演算子)が加わりました。isset と三項演算子の組み合わせは、これらで短く書けます。
| 演算子 | 使えるバージョン | 意味 | 書き換え前 |
|---|---|---|---|
$a ?? $b |
PHP 7.0 以降 | $a が定義済みでnullでなければ $a、そうでなければ $b |
isset($a) ? $a : $b |
$a ??= $b |
PHP 7.4 以降 | $a が未定義またはnullのときだけ $b を代入 |
if (!isset($a)) { $a = $b; } |
$o?->p |
PHP 8.0 以降 | $o がnullならnull、そうでなければ $o->p |
$o !== null ? $o->p : null |
$a ?: $b |
PHP 5.3 以降 | $a がtrueとみなせれば $a、そうでなければ $b(未定義だとWarning) |
$a ? $a : $b |
<?php
// coalesce.php : isset の三項演算子を ?? / ??= / ?-> に書き換える
error_reporting(E_ALL); ini_set('display_errors', '1');
$_GET = ['page' => '3', 'q' => ''];
$config = ['db' => ['host' => 'localhost']];
// 1) isset + 三項演算子 → ??
$page1 = isset($_GET['page']) ? $_GET['page'] : '1';
$page2 = $_GET['page'] ?? '1';
var_dump($page1 === $page2);
// 2) 連鎖:左から順に、最初の「null でない値」
$sort = $_GET['sort'] ?? $config['default_sort'] ?? 'id';
var_dump($sort);
// 3) ?? は空文字や 0 を通す。?: は falsy を置き換える
var_dump($_GET['q'] ?? 'すべて');
var_dump(($_GET['q'] ?? '') ?: 'すべて');
// 4) ??= : 未定義または null のときだけ代入
$config['db']['port'] ??= 3306;
$config['db']['host'] ??= '127.0.0.1'; // すでに値があるので変わらない
$count = null;
$count ??= 0;
echo json_encode($config), ' count=', $count, "\n";
// 5) ?-> : null ならそこで止めて null を返す
class Profile { public function __construct(public ?string $city = null) {} }
class User { public function __construct(public ?Profile $profile = null) {} }
$users = [new User(new Profile('Osaka')), new User(), null];
foreach ($users as $u) {
echo $u?->profile?->city ?? '(未登録)', "\n";
}
// 6) 演算子の優先順位:?? は + や . より低い
$bonus = null;
var_dump(100 + $bonus ?? 50); // (100 + null) ?? 50
var_dump(100 + ($bonus ?? 50));
実行結果(PHP 8.4.21):
bool(true)
string(2) "id"
string(0) ""
string(9) "すべて"
{"db":{"host":"localhost","port":3306}} count=0
Osaka
(未登録)
(未登録)
int(100)
int(150)
ポイントは3つあります。
??は空文字や0をそのまま通します。 3) のように、空文字のときも既定値にしたい場合は($v ?? '') ?: '既定値'と組み合わせます。??は+や.より優先順位が低い ため、6) のように計算や文字列連結と混ぜるときは( )で囲みます。囲まないと100 + nullが先に計算され、結果は100になります。?->は読み取り専用 です。$u?->name = 'x';のような代入には使えません。
フォーム入力の"0"がemptyで未入力扱いになる例と直し方
フォームから届く値はすべて文字列です。数量の欄に 0 と入力すると、PHPには "0" が届き、empty("0") はtrueになります。
<?php
// form.php : フォーム入力 "0" が empty で弾かれる例と直し方
$_POST = ['quantity' => '0', 'name' => ' ', 'note' => '']; // 送信内容の代わり
echo "--- 直す前:empty で必須チェック\n";
foreach (['quantity', 'name', 'note', 'zip'] as $key) {
$msg = empty($_POST[$key]) ? '未入力と判定' : '入力ありと判定';
printf("%-9s %-5s => %s\n", $key, var_export($_POST[$key] ?? null, true), $msg);
}
echo "--- 直した後:文字列として空かどうかを調べる\n";
function is_blank(mixed $v): bool
{
if ($v === null) return true; // 未送信
if (is_string($v)) return trim($v) === ''; // 空文字・空白だけ
if (is_array($v)) return $v === []; // 何も選ばれていない
return false; // 0, 0.0, false は「値あり」
}
foreach (['quantity', 'name', 'note', 'zip'] as $key) {
$msg = is_blank($_POST[$key] ?? null) ? '未入力と判定' : '入力ありと判定';
printf("%-9s %-5s => %s\n", $key, var_export($_POST[$key] ?? null, true), $msg);
}
実行結果(PHP 8.4.21):
--- 直す前:empty で必須チェック
quantity '0' => 未入力と判定
name ' ' => 入力ありと判定
note '' => 未入力と判定
zip NULL => 未入力と判定
--- 直した後:文字列として空かどうかを調べる
quantity '0' => 入力ありと判定
name ' ' => 未入力と判定
note '' => 未入力と判定
zip NULL => 未入力と判定
直す前は2か所で意図と違う結果になっています。
| 入力 | emptyの判定 | 期待する判定 | 理由 |
|---|---|---|---|
数量 "0" |
未入力 | 入力あり | "0" はfalseとみなされる文字列 |
名前 " "(空白だけ) |
入力あり | 未入力 | 空白1文字はtrueとみなされる |
フォームの必須チェックは trim($v ?? '') === '' で書く と、"0" を入力として受け取り、空白だけの入力を未入力として扱えます。全角スペースも取り除きたい場合は、PHP 8.4 で追加された mb_trim() が使えます。
型ごとの「空」の判定は、次のように書き分けると意図がはっきりします。
| 対象 | emptyを使った書き方 | 意図がはっきりする書き方 |
|---|---|---|
| 文字列の入力 | empty($s) |
trim($s ?? '') === '' |
| 数値(0も有効) | empty($n) |
$n === null |
| 配列 | empty($list) |
$list === [] または count($list) === 0 |
| 真偽値 | empty($flag) |
$flag === false または !$flag |
| null許容のオブジェクト | empty($obj) |
$obj === null |
つまずきやすい5つのポイント
1. 型付きプロパティは初期化前に読むとError
型を付けたプロパティは、値を入れる前は「未初期化」という状態です。isset はfalseを返しますが、直接読むとErrorになります。
<?php
class Item { public string $name; public ?string $memo = null; }
$i = new Item();
var_dump(isset($i->name), isset($i->memo));
try { var_dump($i->name); } catch (Error $e) { echo get_class($e), ': ', $e->getMessage(), "\n"; }
実行結果(PHP 8.4.21):
bool(false)
bool(false)
Error: Typed property Item::$name must not be accessed before initialization
null許容型(?string)でも、= null と書かなければ初期値は入りません。 コンストラクタで代入するか、宣言時に初期値を書いておくと、この状態を避けられます。
2. emptyには式を渡せるが、issetには変数しか渡せない
empty(trim($s)) のように、empty には関数の戻り値や式を渡せます(PHP 5.5 以降)。一方 isset(trim($s)) は構文エラーになります。式の結果がnullかどうかは === null で調べます。
<?php
function find() { return ''; }
var_dump(empty(find())); // bool(true)
var_dump(empty(trim(' '))); // bool(true)
3. 文字列に対するisset
文字列に [ ] を付けると、1文字を取り出す操作になります。isset もこの規則に従います。
<?php
$s = 'abc';
var_dump(isset($s[1]), isset($s[5]), isset($s['x']), isset($s[1][0]));
実行結果(PHP 8.4.21):
bool(true)
bool(false)
bool(false)
bool(true)
配列を期待している変数に文字列が入っていると、isset($data[0]) がtrueになることがあります。配列かどうかも確かめたいときは is_array($data) && isset($data[0]) と書くと確実です。
4. マジックメソッド __isset と __get
__get で動的なプロパティを返すクラスでは、isset と empty は先に __isset を呼びます。
<?php
class Bag {
private array $d = ['a' => 1, 'n' => null];
public function __isset($k) { echo "[__isset($k)] "; return isset($this->d[$k]); }
public function __get($k) { echo "[__get($k)] "; return $this->d[$k] ?? null; }
}
$bag = new Bag();
var_dump(isset($bag->a));
var_dump(empty($bag->a));
var_dump($bag->zzz ?? 'D');
実行結果(PHP 8.4.21):
[__isset(a)] bool(true)
[__isset(a)] [__get(a)] bool(false)
[__isset(zzz)] string(1) "D"
__get だけを実装して __isset を書いていないクラスでは、値があっても isset がfalse、empty がtrueになります。__get を書くときは __isset もセットで実装する と、?? や empty が期待どおりに動きます。
5. error_reportingでWarningを外しても原因は残る
error_reporting(E_ALL & ~E_WARNING) と設定すると、未定義キーのWarningは表示されなくなります。ただし値が NULL になる動作は変わらないため、後続の計算や表示に影響します。表示を止めるより、?? で既定値を決めるほうが、動作が読み手に伝わります。 @$a['x'] のようにエラー制御演算子で抑える方法も同じ理由で、$a['x'] ?? null に置き換えるのがおすすめです。
直す前と直した後の書き換え集
| 直す前 | 直した後 | 変わる点 |
|---|---|---|
isset($a['k']) ? $a['k'] : 'x' |
$a['k'] ?? 'x' |
同じ意味で短くなる |
if (!isset($a['k'])) { $a['k'] = []; } |
$a['k'] ??= []; |
同じ意味で1行になる |
if (empty($_POST['qty'])) |
if (trim($_POST['qty'] ?? '') === '') |
"0" を入力として扱える |
if (empty($list)) |
if ($list === []) |
配列以外が来たときに気づける |
if (is_null($v)) |
if ($v === null) |
同じ結果。関数呼び出しがなくなる |
if ($v == null) |
if ($v === null) |
0 や "" をnull扱いしなくなる |
@$a['k'] |
$a['k'] ?? null |
ほかのエラーを隠さない |
$u ? $u->getName() : null |
$u?->getName() |
nullのときだけ止まる |
if (isset($row['deleted_at'])) でキーの有無を判定 |
array_key_exists('deleted_at', $row) |
値がnullのキーも「ある」と判定できる |
関数の引数と戻り値では型で表す
変数が定義されているか分からない状況を減らすには、null許容型(?string)と既定値 を使います。
<?php
function show(?string $name = null): string
{
return $name ?? '(名前なし)';
}
echo show(), ' / ', show('Sato'), "\n";
実行結果(PHP 8.4.21):
(名前なし) / Sato
引数に型があれば、関数の中では $name が必ず定義されています。isset で確かめる必要がなく、=== null か ?? だけで書けます。
判定方法を選ぶフローチャート
flowchart TD
A[調べたい対象は?] --> B{変数やキーが<br>未定義の可能性がある?}
B -- ある --> C{なければ既定値を使う?}
C -- はい --> D["?? または ??="]
C -- いいえ --> E{値がnullのキーも<br>あると判定したい?}
E -- はい --> F[array_key_exists]
E -- いいえ --> G[isset]
B -- ない --> H{何を調べる?}
H -- nullかどうか --> I["=== null"]
H -- 文字列が空か --> J["trim して === ''"]
H -- 配列が空か --> K["=== []"]
H -- falseとみなせるか --> L["if (!$v) または empty"]
よくある質問
Q. issetとemptyはどちらを使えばよいですか?
A. 「あるかどうか」を調べたいなら isset、「falseとみなせる値かどうか」を調べたいなら empty です。0 や "0" を有効な値として扱う場面では、empty の代わりに === null や trim($v ?? '') === '' を使うと意図どおりになります。
Q. !empty($v) と isset($v) は同じですか?
A. 同じではありません。$v が 0・""・"0"・false・[] のとき、isset($v) はtrueですが !empty($v) はfalseです。!empty($v) は isset($v) && $v と同じ意味です。
Q. is_null($v) と $v === null はどちらがよいですか?
A. 結果は同じです。=== null は関数呼び出しがなく、否定も !== null と書けるので読みやすくなります。array_filter などにコールバックとして渡すときは、'is_null' や is_null(...) が便利です。
Q. Undefined array keyのWarningを消すにはどうすればよいですか?
A. キーがない場合の値を $a['key'] ?? '既定値' で決めるのが基本です。必須のキーなら isset で確かめてから読み、ない場合はエラーメッセージを返すようにします。error_reporting で表示を止める方法は、値が NULL になる動作が残るため、原因の対処にはなりません。
Q. PHP 7から8に上げたらWarningが増えました。動作は変わりましたか?
A. 未定義の変数・配列キー・プロパティを読む操作は、エラーレベルが Notice から Warning に変わっただけで、値が NULL になる点は同じです。ログに出たWarningの行を ?? や isset で書き換えていくと、順に整理できます。
まとめ
- issetは「定義済みでnullでない」、emptyは「未定義またはfalseとみなせる」、is_nullは「nullである」 を調べます。
"0"・0・0.0・""・false・[]・null・未定義は empty でtrue、" "と"0.0"はfalseです。- 未定義でもWarningを出さないのは isset・empty・
??・??=です。is_nullと=== nullは未定義の変数でWarningが出ます。 - PHP 8.0 から Undefined variable・Undefined array key は Warning になりました。値が
NULLになる動作は変わりません。 - 値がnullのキーを「ある」と判定したいときは array_key_exists、プロパティなら
property_existsを使います。 isset($a) ? $a : $bは$a ?? $b、nullかもしれないオブジェクトは?->で短く書けます。- フォームの必須チェックは
trim($v ?? '') === ''にすると、"0"を入力として受け取れます。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP isset・empty・is_nullの違い|null判定の早見表」
株式会社RJCでは、PHPのnull判定や型の扱いを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。