もう迷いたくない!isset,emptyなどの書き方

WordPressでカスタムフィールドの値を出力する際に、変数に代入させることがあります。
「値があれば代入」「値があれば出力」など色々書いていますが、理由もわからず出てくるエラーに対してあれこれ直しています。
デバックモードでは特にエラーを出してしまいます。

ちなみにタイトルは「もう迷わない!」と言えるほど強くないので希望を込めました(笑)

変数の状態・型を基準にまとめて、実際にどのように書けばよいか調べようと思います。

参考記事

PHPで未定義変数やnullかどうかを調べる関数まとめ | PisukeCode - Web開発まとめ

isset, empty, is_null の動作まとめ - Qiita

PHPで出来てしまうが控えたほうががいい書き方 - Qiita

変数の宣言、定義・未定義

宣言

$var;

一方で定義何かしら値が代入されている状態です。
値が代入されると型が決まります。

$var = null;

これでも定義されています。

なので未定義とは、

  • 変数が代入されていない
  • そもそも変数が未宣言

いずれかの状態を指します。

連想配列の存在しないキーというのも未宣言なので、アクセスすると未定義扱いになります。

定義されているかどうかを調べる書き方

それは isset です。

isset($var);

NULL以外、空でも代入されていて「定義」されていれば true になります。
未定義またはNULLのときに false を返します。
NULLは要注意です。

NULL

変数の中身がNULLnull でもよい)の状態です。

変数の型が「NULL」なので、$var = ''$var = []当てはまりません。 

NULLかどうかを調べる書き方

純粋に「NULLか否か」を調べるには is_null関数を使います。

is_null($var);

NULLで true、それ以外では false を返します。

中身が空

変数の型が決まっていて、中身が空の状態です。
ぱっと思いつくのは $var = ''(文字列)、$var = [](配列) です。

NULLはさておき、それ以外の型で空の状態はあるのでしょうか?

調べていて思いついた方法で型キャストがあります。

変数 - 型の変換 - 型キャストの一覧表 - PHP入門 - Webkaru

$var = '';
var_dump( $var );
var_dump( (int)$var );

空の文字列型を整数型に変えたら空の整数型になるのではという狙いです。
果たしてうまくいくのでしょうか?
※以下は改行させています(本当は1行)

string(0) ""
int(0)

0でした。
試しに $hoge = '100'(int)$hoge としてvar_dumpすると int(100) となるので、$var には0という整数が入っていると分かります。

空にすることはできるのでしょうか。
答えは「整数の0は空」でした。

空かどうかを調べる書き方

それは emptyを使います。

empty($var);

emptytrue が返ってくればそれは空であると捉えます。
※純粋な「空の値」のみを調べる場合は別の書き方になります(後述)。

...だんだん複雑になってきました。表が必要です。
型ごとにempty の「空」をまとめました。

型別の空の値

文字列 ""
文字列 "0"
整数 0
浮動小数点数 0.0
配列 array()、[]
boolean false

表からは外しましたが、NULLempty では空(true)の扱いです。

空ではない値が入っている

上記以外の値、0や空以外の値が数値、文字列、配列などに入っているものです。

空ではない値が入っているかどうかを調べる書き方

$var == true であるとき、空でない値が入っていると言えます。

というのも、
if() の引数に変数を入れると、その変数がbool(真偽の値、trueかfalse)に変換されます。
その変換パターンは上記の「空の値」の表に沿っています。中身によって true / false を返します。

下記は$varに空ではない値があれば true を返します。

if($var) { ... }  

これも同じです。

if(!empty($var)) { ... }

図にしてみる

これらのことを視覚化してみました。

f:id:yokoyoko_115:20211127025004j:plain

未宣言かつ未定義、みたいなところが若干モヤモヤしますが、未宣言だったら当然未定義、未定義だったら未宣言・宣言両方の場合がある、ということを表したかったです。

条件式の書き方

ここまでおさらいしたところで、実際に書くif文をまとめてみます。

変数に値が入っているかどうか

if (!empty($var)) { ... }

if ($var) でも同じかなと思いましたが、この場合は $var未定義の場合はエラーが出ます

定義されていることが確実であれば if ($var) でもいいと思います。

もちろん、文字列や数字の0も値が入っていない扱いになるので、その場合は別の方法を考えましょう。
(問題に直面して調べ次第追記します)

if (isset($var) && !empty($var)) { ... }

この書き方も見かけましたが、これは冗長であるようです。
たしかに作った上記の図を見ても if (!empty($var)) で十分ですね。

変数にNULL以外の空の値が入っているかどうか

empty だけでは未定義やnulltrue とみなされます。
下記は0や空文字のみを判別する書き方になります。

if (isset($var) && empty($var)) { ... }

変数に値が入っているかどうか(配列の場合)

if (is_array($array) && !empty($array)) { ... }

!empty($var) で配列が空ではないかどうかが分かりますが、これだけでは配列かどうかの判別がつかないので is_array() も条件に入れます。

これも配列であることが確実であれば if (!empty($array)) だけでいいと思います。

新しく知ったこと

issetとemptyは関数ではなく言語構造

調べると「isset関数」や「empty関数」とよく出てきます。ドキュメントにすら「この関数は」と書かれています。

言語構造については難しそうなので割愛しますが、issetempty は言語構造なので、
isset は変数のみを引数として受け取れるようになっています(複数の変数を引数に持てる)。

オーバーヘッドについて

「issetとemptyは関数ほどオーバーヘッドが大きくない」という文があったのでオーバーヘッドを調べました。

[PHP] function() がどれほど遅いか調べてみた | 株式会社オルタ

関数になってなければ、次はスグ下の行に移ればいいだけであることを考えると、全く違う場所に書かれている getHoge() を探さなきゃいけないってことはなんとなくイメージできるのではないでしょうか?

この、”どこにあるのか探す”という処理のことを、オーバーヘッドと呼びます。

issetとemptyは関数より処理が早いのですね。

他には比較演算子と速さを比べていましたが、ここでは突き詰めないようにします。

!isset() とも書ける

ドキュメントのemptyのページで書かれていました。

変数が存在しなくても警告は発生しません。 つまり、empty() は本質的に !isset($var) || $var == false と同じことを簡潔に記述しているだけです。

あまり見かけないのですが !isset() と書けるのですね。
使う場面がでてくるかどうか、出てきたら追記します。

変数の初期化

未宣言、未定義に加えて「初期化」という言葉も出てきました。

【PHP入門】これで完璧! 5分でわかる変数の基本徹底解説 | 侍エンジニアブログ

変数の初期化とはプログラムの初期処理において、あらかじめ変数に決められた値を設定したり、初期値であることを示すために0やNULLを指定することを言います。PHPでは変数の宣言と値の代入はセットで行いますので、一番始めに変数の宣言と値を代入する処理が初期化処理となります。

PHPでは初期化されていない変数はNULLが格納されているとみなされます。

他の言語を学ぶと違いがはっきり分かりそうですね。
ほぼ学んでいない身としては混乱です。

// 宣言のみ(初期化されていない)
$var;

// 初期化処理
$var2 = 'hoge';

// 「Notice: Undefined variable」というエラーとNULLが出る。
var_dump($var);

初期化されていない変数にはNULLが格納されているとみなされているはずなのに「Undefined variable」と「未定義の変数です」とエラーが出ておきながら、
未定義のはずなのにNULL が出るというのはどういうことなのでしょうか?
同じこと2回書いていますね。

これまで調べてきたことが覆る事態です。

【PHP入門講座】 NULLと未定義の違い - Qiita

PHPでは、未定義の値を取得しようとしたときに、Noticeを発生しながら NULL を代わりに取得します。

取得しようとした時に、NULLを取得して定義されるのですね!

カスタムフィールドを設定しただけでは未宣言?未定義?

ここはカスタムフィールドのプラグイン「Advanced Custom Fields」のお話です。

存在しないカスタムフィールドや設定済みのカスタムフィールドでも、その後に投稿ページを開いて「更新」しない状態でカスタムフィールドをvar_dumpすると、エラーなしのNULLが出ます。

これも、関数の中で呼び出された際にエラーが出ないような処理とNULLが入る処理が入っているものと思われます。
未宣言や未定義のカスタムフィールドでも安心設計になっているのではないでしょうか(想像)。

そして未入力のまま投稿を「更新」すると、設定した型のデフォルト値が入ります(「テキスト」の場合は文字列型の空文字)。

これまではその都度場当たり的に解決してきたので、どのような状況でエラーを出していたか具体例が挙げられず。残念です。

エラーについては別でまとめようと思います。

array_key_exists、property_exists

配列の中身のあるなしを調べる関数です。
正直ここまでは頭の容量オーバーで調べきれませんでした。

配列で詰まったら調べます。

さいごに

太字を多用して「迷いたくない!」気持ちが出てしまいました。

たくさんこの関連の記事は見るのですが、true/falseの表を見てもどうも覚えられず。
覚えられそうで覚えられない。

ただ覚えるだけでは応用が効かない気がして、視点を変えてまとめてみました。

図も作ってみました。自画自賛していますが、良くない解釈が入っていそうな気もします。

あくまで自分自身のメモなので、間違っている部分はその都度直していこうと思います。

おしまい。