Posted at

else-ifを三項演算子(?:)やnull合体演算子(??)に置き換えてみた


概要

PHP5.3以降で使用可能な三項演算子?:if-else構文の 完全互換だと思いこんで else-ifのネストに置き換えて使ってみたら 想定外の挙動 で少しハマったので反省も踏まえ共有してみた。


状況

画面上の押されたボタンによってデータベースに対する処理を分ける場面です。

formタグ自体は1つで「登録」「更新」「削除」「キャンセル」のsubmitボタン自体にname属性を持たせ、どのボタンが押されたかを判別しています(valueの中身は気にしない方向)

まず最初に書いたコード


db_ctrl.php

if (isset($_POST['regist'])) {

$action= "regist";
} else if (isset($_POST['update'])) {
$action= "update";
} else if (isset($_POST['delete'])) {
$action= "delete";
} else if (isset($_POST['cancel'])) {
$action= "cancel";
}

動作は問題なし。

でもなんだか冗長というか、我ながら同じ単語何回書くんだと 1人ツッコミ が入る

で、まずは上記を「if-elseの短縮構文」の つもり(思い込み) で三項演算子?:に置き換えてみました


(PHP5.3以降)db_ctrl.php

$action=

isset($_POST['regist'])? "regist" :
isset($_POST['update'])? "update" :
isset($_POST['delete'])? "delete" : "cancel";

最初のコードと比べればだいぶスッキリした。

尤も、この構文だと最後がelse-ifではなくelse(或いはswitch文のdefault:)のようになってしまうので そもそも置き換えられてない んですけども。

とりあえずテスト。

今回期待する結果は「登録」ボタン押下後で$action= "regist";

結果は・・・


南無った

$action = "delete";


ちーん :skull:

なんでやねん!

調べたところ、こういうことだそうで…

(PHP公式マニュアルから引用)

ダメだこいつ?:早くなんとかしないと…


代替案としてnull合体演算子(??)を使用

できれば 「中身が'regist'だったら"regist"を入れる」 なんて単語のリピートも1回の記述で済ませたいし

今回のケースは条件isset()if-elseネストだから、この三項演算子の下に説明のあるnull合体演算子ってのがいい感じで使えるんジャマイカ?

というわけで、早速さっきのPHPマニュアルで覚えたてのnull合体演算子を使ってみました


(PHP7以降)db_ctrl.php

$action =

$_POST['regist']??
$_POST['update']??
$_POST['delete']??
$_POST['cancel']?? "";

ただ、これだと$actionの中身が$_POSTの中身そのままになるので、<input type="submit" name="action" value="登録">ってHTML書いてると思いっきり送信ボタンの日本語ラベルがそのまま値($action = "登録")になるのがネック :cat:

やっぱり基本的にはsubmitボタンにname属性は持たせず<input type="hidden" name="action" value="regist">とセットにするのがいいのかな


最終的にforeachに落ち着いた

結局のところ以下のコードに落ち着きました…


db_ctrl.php

$actions = ['regist', 'update', 'delete', 'cancel'];

foreach ($actions as $action) {
if (isset($_POST[$action])) break;
}


消化不良な部分

今回の三項演算子?:のこの想定外の挙動、どうやら他の言語の三項演算子ではちゃんと期待通りの結果になる模様 :scream_cat:

なんでPHPだけ違う結果になるのか、 PHPマニュアル自体が「わかりにくい挙動」に要注意 と警鐘を鳴らす仕様を敢えて採用する理由はなんなんだと軽く調べてみたら 「間違って実装しちゃったけど今更修正できないから」 なんて言う人も…ホンマかいなそれ :fearful:

そんなだから海外の言語ランキングで「主軸のない補助輪」とか揶揄されるんじゃ…


結論、まとめ。

PHP(5.3以降)の三項演算子(?:)はネストしてはいけない。 修正しろy

PHP7以降のnull合体演算子(??)のネストは可能ではある。

未熟エンジニアなりの所感(おまけ)

こんなところまで読んで頂きありがとうございます m(_ _)m

PHPにおける三項演算子(?:)の使い所としては「HTML部分にPHPを埋め込む際、foreachのように一行にしたい」といった用途向きでしょうか

ネストは避けるとしても、あまり多用しても逆に可読性が悪くなるのかもしれません

null合体演算子(??)もPHP7以降なので、環境次第でエラー修正する羽目に陥る確率が高い気が…

そもそも他言語からPHP に流れてくる も学ぼうとされる方 は少数派かもですが、同じ仕様だと思って 「なんでやねん!:anger: と躓かれる方がいるかもしれないと思い投稿しました。

いつも他の方の情報を頂いてばかりの自分の投稿がどなたかのお役に立てば幸いです:pray:



あとがき

マークダウン記法の学習と技術情報共有や質問投稿に慣れていく目的も兼ねての初投稿です

MarkdownやQiitaでの投稿自体が初めてなので、主に以下のページを参考にさせていただきました

Markdown記法 チートシート