PHPには、null合体演算子という機能があります。PHP7.0で導入された演算子で、??
を使い、isset()の代用として便利に使える機能です。
しかしながら、PHP3から触れてきた私にはなかなかisset()の癖が抜けず、この機能を避けてきてしまいました。同じような方もいらっしゃるのではないでしょうか?
本ページでは、null合体演算子の解説を通じ、「なんでもisset()」状態からステップアップすることを目標に書いています。
null合体演算子 ?? とは
概要
null合体演算子 (??
) は、PHP 7.0 で導入された新しい演算子で、左辺の変数や値が null
の場合に限り、右辺の値を返すという特性を持っています。
これにより、変数の値が null
であるかどうかを簡潔にチェックし、null
の場合にデフォルト値を提供することが非常に簡単になります。
$name = null;
$displayName = $name ?? "ゲスト";
echo $displayName; // 出力: "ゲスト"
上記では意図的に$name
にnull
を入れていますが、$name
が未定義であったとしても警告を出さないところがisset()と似ているところです。
isset()での例
先ほどの例を、isset()
で表現した例を挙げます。初心者の方であれば、こんなコードでしょうか。
$name = null;
if (isset($name)) {
$displayName = $name;
} else {
$displayName = "ゲスト";
}
echo $displayName; // 出力: "ゲスト"
多くの方は、以下のように書くと思います。
$name = null;
$displayName = isset($name) ? $name : "ゲスト";
echo $displayName; // 出力: "ゲスト"
三項演算子を使うことで、そう変わらない感じで書けます。慣れてしまえば、私はこれでも見やすいと感じています。
isset()が問題になるケース
isset()
を利用しての三項演算子は、ネストすると非常に見にくくなる問題があります。ネストした三項演算子のコード例を挙げます。
$name = null;
$role = null;
$location = null;
$displayName = isset($name) ? $name : (isset($role) ? $role : (isset($location) ? $location : "ゲスト"));
echo $displayName; // 出力: "ゲスト"
このように、途端に可読性が下がってしまいます。
意味不明……。でも、三項演算子はネストさせないようにするのでこういう状況は少ないかも。
??を使った例
先ほどのコードを、??
を使って書き直してみます。以下の様に、??で繋げて書くことが可能です。
$name = null;
$role = null;
$location = null;
$displayName = $name ?? $role ?? $location ?? "ゲスト";
echo $displayName; // 出力: "ゲスト"
とても分かりやすく書くことができました。5行目の$name
,$role
, $location
は左から順に評価され、nullでなかったものが$displayName
に代入されます。
基本的に、代入の用途においては三項演算子を使ったisset()
よりも??
を使った方が簡潔に書けますね。
null合体演算子の注意点
null合体演算子??
は便利でありながら、使用する際にいくつかの注意点や制限が存在します。これらを理解しておくことで、効果的かつ安全に演算子を利用することができます。
厳密にnullを評価する
??
は厳密に変数がnull
かどうかを評価します。
つまりは変数が0
, ''
(空文字), false
などの「falsy」な値(論理型コンテキストに現れたときに偽とみなされる値)を持っている場合でも、その値が返されることを意味します。
$value = false;
echo $value ?? 'default'; // 出力: false
これに関してはisset()と同じです。忘れると意図しないコードになりますのでご注意ください。
isset()との違いを認識しておく
多くのケースではisset()
の代わりに使えますが、違いをきちんと認識しておく必要があります。
isset()
は変数がnull
でない場合にtrue
を返しますが、nullの場合はfalse
を返します。つまり返される値は常にbool
値です。
??
は左辺の変数がnull
でない場合に左辺の変数を、null
なら右辺の値を返します。
非存在の変数の警告が出ない
これまでにも書いた通り、??
は変数が存在しない場合に警告が表示されません。これがありがたくもあるのですが、逆に変数のタイプミスがあっても警告が出ないことを意味します。
警告が出ない=そのまま発見されないと思わぬバグに繋がりかねません。そういった仕様であること理解し、慎重にコードを書く&確認する必要があります。
PHPのバージョン
これまでにも書いた通り、PHP7.0から導入された機能です。したがって、それ以前のバージョンのPHPでこの演算子を使用するとエラーが発生します。
最新がPHP8.3の現在において、PHP7.0以下で動作している環境は皆無に等しいでしょうが、念頭に置く必要があります。
??=代入演算子
PHP 7.4では、null合体演算子がさらに進化し、??=
という新しい代入演算子が追加されました。
この演算子を使用すると、変数がnull
の場合に限り、右辺の値をその変数に代入することができます。
参考例
まずはこれまでに解説した、通常の??
演算子を使った例です。
$variable = $variable ?? 'default value';
これを、PHP7.4からの??=
代入演算子を使った例に書き換えてみます。
$variable ??= 'default value';
この2つのコードで意味は同じです。要は+=
の??
版みたいなものですね。
こちらの方が簡潔に表現できるので、PHP7.4以降で適所があれば、この方法を使っても良いでしょう。
実際のコード例: ??の活用法
さて、一通りの??
の使い方を学んだところで、最後にコード例をいくつか挙げます。ご利用の際の参考にしていただければ幸いです。
チェックボックスの取得
Webフォームのチェックスボックスが未入力である場合に、デフォルト値を設定することができます。
<form method="post" action="process.php">
<label>
<input type="checkbox" name="accept_terms" value="yes"> 利用規約に同意する
</label>
<input type="submit" value="送信">
</form>
<?php
$acceptTerms = $_POST['accept_terms'] ?? 'no';
if ($acceptTerms == 'yes') {
echo "利用規約に同意しました!";
} else {
echo "利用規約に同意していません。";
}
チェックボックスは、チェックを入れない場合はnull
になるのでこのコードが成立します(ラジオボタンもOK)。
しかしながら、<input type="text" name="username" value="">
のようなテキストボックスの場合は、ユーザーの入力が無ければ''
(空文字)になってしまうのでこの方法は使えません。
そちらはempry()
などで判定した方が良いでしょう。
$username = !empty($_POST['username']) ? $_POST['username'] : 'guest';
設定や状態の更新
アプリケーションの設定値やオプションを配列やオブジェクトから取得する際、特定のキーが存在しない場合のデフォルト値を設定するのに有用です。
$config['timeout'] ??= 30;
関数やメソッドの引数
関数やメソッドの引数で、オプショナルな引数がnullの場合にデフォルト値を持たせたいときにも??
を利用できます。
function displayMessage($message = null) {
$text = $message ?? 'デフォルトのメッセージ';
echo $text;
}
1行目にパラメーターの初期値として埋め込むこともできますが、長いメッセージであればこの方が可読性が良さそうです。
オブジェクトのプロパティアクセス
オブジェクトのプロパティがオプションであり、存在するかが不確実な場合があります。
こういった場合に、存在しなかった場合の初期値を代入することができます。
$profileImage = $user->profile->image ?? 'default_image.png';
自分で作ったクラスであれば、それほど便利に感じないかもしれません。
しかし例えば、他サイトのAPIで取得したJSONをデコードしたクラスであれば、何が入っているかわかりません。以下のような場合、$videoは何が入っているか分からないので、??
で判定擦る手は使えそうです。
$apiUrl = 'https://example.com/api/hoge';
$header = array(
"Content-Type: application/x-www-form-urlencoded",
"User-Agent: Mozilla/5.0 (Windows NT 11.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0",
"Referer: https://example.jp"
);
$options = array(
'http' => array(
'method' => "GET",
'header' => implode("\r\n", $header),
)
);
// コンテンツ取得
$videoContent = file_get_contents($apiUrl, false, stream_context_create($options));
$video = json_decode($videoContent);
// ↑$videoは何が入っているか保証されない
// null合体演算子を使用して、$videoのitems[0]のsnippetが存在しない場合はnullを返す
$snippet = $video->items[0]->snippet ?? null;
if (!$snippet) {
return null;
}
// null合体演算子を使用して、サムネイルURLが存在しない場合はデフォルトを返す
$thumbnail = $snippet->thumbnails->medium->url ?? '/default_image.png';
まとめ
長年の癖というのはしみついており、私はisset()
から抜け出せませんでした。しかしながら、自分で記事にまとめたおかげで、今後は自信を持って??
や??=
を使っていくことができそうです。
エンジニアはブログを書くことが大事というのは、心底本当に思います。
本記事が私以外にも役立つことがあれば、とてもうれしいです。
今後も新しい記法は積極的に取り入れていきたいと思いました。
コメント