isset, empty, is_null の動作まとめ

  • 277
    Like
  • 4
    Comment
More than 1 year has passed since last update.

空値

以下に示されるものは全て if ($var) { ... } のように用いたとき false と評価されます。これらは俗に 空値 と呼ばれる値です。

名称 具体例
数値のゼロ 0
0.0
文字列のゼロ "0"
空文字列 ""
空配列 []
array()
false
ヌル null
空要素から成る
SimpleXMLElement
new SimpleXMLElement('<foo></foo>')

※ 未定義の変数は Notice: Undefined variable を発生しながら null として扱われます。

複雑な表を書き並べている記事が多いですが、実際は

  • null であれば
  • null でなければ
  • 空値であれば
  • 空値でなければ

の4種類の内容しか取り扱っていないものがほとんどであり、覚えるのが難しいなどと悩む必要はありません。至ってシンプルです。但し、(空配列はまだ分かるとして) 文字列のゼロ空要素から成るSimpleXMLElementの扱いには特に注意してください。これはPHPにおいて直感的ではない、最もクセのある実装のひとつです。JavaScriptなどと比較してもこれらの点だけ異なっていたりします。

変数の存在を確認するショートコード

これから紹介するissetemptyは、未定義とnullを区別することが出来ませんissetemptyについて紹介する前に、純粋に「変数が定義されているか」だけをチェックする方法を紹介します。compactという関数を用います。

compact関数の利用例
$a = 'hoge';
$b = 1;
$d = null;
var_dump(compact('a', 'b', 'c', 'd'));
/*
array(3) {
  ["a"]=>
  string(4) "hoge"
  ["b"]=>
  int(1)
  ["d"]=>
  NULL
}
*/

この関数は現在のスコープに変数が存在している場合のみ、変数名をキーとして値を配列に取り込む動作をします。これを利用すれば、定義済みかどうかを以下のように確認することが出来ます。上記の表で紹介した「空配列以外の配列は true として扱われる」という性質を利用しています。

変数varが現在のスコープ内で定義済みならば
if (compact('var')) { ... }
変数varが現在のスコープ内で未定義ならば
if (!compact('var')) { ... }

isset, !empty, !is_null の比較

上記のコードを用いると、これら3つは以下のように表現することが出来ます。

if (isset($var)) { ... }
if (compact('var') && $var !== null) { ... }
if (!empty($var)) { ... }
if (compact('var') && $var) { ... }
if (!is_null($var)) { ... }
if ($var !== null) { ... }

以下の原則が挙げられます。

  • issetは変数の存在をチェックするためのオーバーヘッドがあるので、定義済みであると確定している場合は $var !== null によるチェックで済ませるほうが良い。(但し複数同時にチェックする際に簡略化出来る場合を除く)
  • !emptyは変数の存在をチェックするためのオーバーヘッドがあるので、定義済みであると確定している場合は $var によるチェックで済ませるほうが良い。(但しemptyという語のわかりやすさから,配列が空かどうか調べるときには定義済みが確定していてもこれが用いられる場合がある)
  • !is_nullは関数呼び出しのオーバーヘッドがあるので、この用途では使う必要が全くない。

issetの特徴

  • これは関数では無く言語構造であるため、関数ほどオーバーヘッドが大きくありません。
  • 変数が未定義の場合でもエラーを出さずにnullの確認ができます。
  • 変数のみを引数として受け取ります。つまり、関数の返り値や値リテラルを直接渡すことは不可能となります。一時的に変数に代入する必要があります。
動作しません
if (isset(my_example_function($arg))) { ... }
  • 複数の変数を同時にチェックすることが出来ます。以下の記述はそれぞれ各行が等価になります。
\$a,\$b,\$cの値が全てnullでなければ
if (isset($a, $b, $c)) { ... } 
if (isset($a) && isset($b) && isset($c)) { ... }    
\$a,\$b,\$cの値のうちどれか一つでもnullであれば
if (!isset($a, $b, $c)) { ... } 
if (!isset($a) || !isset($b) || !isset($c)) { ... }    
  • 配列キーやオブジェクトプロパティに対しても使用することができ、その場合は再帰的に未定義チェックを行ってくれます。一階層ずつチェックする必要がなくなります。
配列キーやオブジェクトプロパティの存在を再帰的に確認する
if (isset($response->errors[0]->message)) { /* TwitterAPIでよくあるエラーチェック */ }
  • 文字列のオフセットをチェックすることにも使えます。以下の各行は等価です。但し、罠が多いので注意が必要です。
$bufが5バイト以上であるかどうかチェックする
if (strlen($buf) >= 5) { ... }
if (isset($buf[4])) { ... }

empty の特徴

  • これは関数では無く言語構造であるため、関数ほどオーバーヘッドが大きくありません。
  • 変数が未定義の場合でもエラーを出さずに空値の確認ができます。
  • issetとは異なり、PHP5.5以降では関数の返り値や値リテラルを直接渡すことが出来ます。但し、PHP5.4以前では変数のみを引数として受け取ります。
  • issetとは異なり、複数を同時にチェックすることは出来ません。
  • 配列キーやオブジェクトプロパティに対しても使用することができ、その場合は再帰的に未定義チェックを行ってくれます。一階層ずつチェックする必要がなくなります。

is_null の特徴

  • コールバック関数として利用することが出来ます。
\$arrayに要素として含まれるnullの個数を数える
$num_of_null = count(array_filter($array, 'is_null'));

array_key_exists, property_exists の利用

issetには少々汚いやり方をしないと未定義とnullを区別できない問題がありましたが、配列やオブジェクトにおいてはそれを可能にする専用関数が用意されています。但し、引数の順序に注意してください。

$arrayにキーkeyが定義されていれば
if (array_key_exists('key', $array)) { ... }
$objectにプロパティpropertyが定義されていれば
if (property_exists($object, 'property')) { ... }

個人的用途まとめ

  • $_GET, $_POSTなどを受け取る際の処理でも紹介しているfilter_inputを用いれば、issetemptyのように存在を判定する言語構造が必要になるシーンはかなり減少する。
  • 文字列が格納される可能性のある変数に関してemptyを使用するのは控える。"0"の扱いに問題があるためである。
  • 文字列のオフセットに対してissetを用いる際はPHPバージョンに気を付ける。古いバージョンでは避けたほうが無難。