PHPって、「とにかく動けばいい」って感じのコードが多い気がします。そして、それらは実際動きます。何を書いてるのか分からないコードでも、ちゃんと動きます。でもいつか困る日が来ます。
下記のような、少し見通しの悪いコードがあるとします。
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{$parent='root';}if($parent!=='root'){$isAllowed=$modx->manager->isAllowed($parent);if(!$isAllowed){$parent=0;}}if($parent==='root'){$a='a=2';}elseif($parent==0){$a='a=120';}else{$a="a=120&id={$parent}";}return'index.php?'.$a;}
これは実際にMODXで使っているコードですが、こういうコードが多いと、このシステムはどういう処理の流れで動いているのかよく分からないということになってしまいます。不具合が増えたり、負荷が大きくなったりもします。少しでも整理を進めることで、今後の進化に備えることができます。
末尾のほうから見ていきます。このコードはどういう値を返そうとしているのかを明確にしていくためです。
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{$parent='root';}if($parent!=='root'){$isAllowed=$modx->manager->isAllowed($parent);if(!$isAllowed){$parent=0;}}if($parent==='root'){return'index.php?'.'a=2';}elseif($parent==0){return'index.php?'.'a=120';}else{return'index.php?'."a=120&id={$parent}";}}
上記のように書き換えました。$a
という変数が不要になりました。「とりあえず適当に変数を作って入れる」ということをやめることで、処理の流れが見やすくなります。
return
が3つ続いていますが、ここは整理できます。
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{$parent='root';}if($parent!=='root'){$isAllowed=$modx->manager->isAllowed($parent);if(!$isAllowed){$parent=0;}}if($parent==='root'){return'index.php?'.'a=2';}if($parent==0){return'index.php?'.'a=120';}return'index.php?'."a=120&id={$parent}";}
returnを整理しました。elseifやelseが不要になることが分かりますでしょうか?
次。'index.php?' . 'a=2'
や 'index.php?' . "a=120&id={$parent}"
をまとめます。
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{$parent='root';}if($parent!=='root'){$isAllowed=$modx->manager->isAllowed($parent);if(!$isAllowed){$parent=0;}}if($parent==='root'){return'index.php?a=2';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
上記のように書き換えました。ここまでは簡単です。
この関数は $parent
の値によって3通りのリターンを返すことが分かりますでしょうか?ここに注目して、さらに整理を進めます。
if($parent!=='root'){$isAllowed=$modx->manager->isAllowed($parent);if(!$isAllowed){$parent=0;}}
上記の部分を見ると、 $parent
の値が root
であっても、場合によっては変わることが分かると思います。条件を満たさない場合、 $parent
の値は root
ではなく 0
に変わります。
そもそも root
という文字列自体には何か意味があるのかな?
$parent
の値が 0
の場合は、
return'index.php?a=120';
上記のように返すことになります。ということは、ここでよく考えます。
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{$parent='root';}if($parent!=='root'&&!$modx->manager->isAllowed($parent)){return'index.php?a=120';}if($parent==='root'){return'index.php?a=2';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
少し難しいかもしれませんが、上記のように整理できます。$parent
に 0
を代入するのをやめて、そこで return で返すようにしました。
返す値が分かりやすくなったので、今度は分岐条件のほうに目を向けていきます。 $parent
の値ですが、これは関数に引数として与えられるものではありません。引数は $current
という配列です。
$parent
は $current['parent']
の値だけを見ます。$current['parent']
はMODX内では親ページのIDを示すもので、0を含む「数値」が入ります。
ということは・・・
$current['parent']
が存在する場合は、それをそのまま $parent
に代入し、存在しない場合は root
という「文字列」を代入する。ということが読み取れるでしょうか?
$parent
が root
の場合は、
return'index.php?a=2';
上記を返しますよね?ということは、
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{return'index.php?a=2';}if($parent!=='root'&&!$modx->manager->isAllowed($parent)){return'index.php?a=120';}if($parent==='root'){return'index.php?a=2';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
上記のようになります。root
という文字列は、ただの目印でした。 $parent
に root
を代入する処理がなくなり、すぐに return できるようになったので、つまり、
functiongetReturnAction($current){global$modx;if(isset($current['parent'])){$parent=$current['parent'];}else{return'index.php?a=2';}if(!$modx->manager->isAllowed($parent)){return'index.php?a=120';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
上記のように書き換えることができます。だいぶシンプルになりました。
冒頭の if-else に注目します。else節で return していますが、この判定を逆にするとさらに短くできることが分かりますでしょうか? 実際に書き換えてみます。
functiongetReturnAction($current){global$modx;if(!isset($current['parent'])){return'index.php?a=2';}else{$parent=$current['parent'];}if(!$modx->manager->isAllowed($parent)){return'index.php?a=120';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
上記のように書き換えます。ifとelseを逆にしました。すると、
functiongetReturnAction($current){global$modx;if(!isset($current['parent'])){return'index.php?a=2';}$parent=$current['parent'];if(!$modx->manager->isAllowed($parent)){return'index.php?a=120';}if($parent==0){return'index.php?a=120';}return'index.php?a=120&id='.$parent;}
上記のように、else 側の判定を省略できます。
まだ整理できます。さっき、root
という値を意識せずにすむようになりましたが、そうすると $parent
は単純に $current['parent']
の値を代入するだけです。意味のない代入になるので、そのまま使いましょう。
functiongetReturnAction($current){global$modx;if(!isset($current['parent'])){return'index.php?a=2';}if(!$modx->manager->isAllowed($current['parent'])){return'index.php?a=120';}if($current['parent']==0){return'index.php?a=120';}return'index.php?a=120&id='.$current['parent'];}
上記のように整理できました。
最初の3つのreturnは $current['parent']
の値が無効な処理になっていて、$current['parent']
の値をそのまま使う処理は最後に配置しています。falseな処理を前半で順番にひとつずつ落としていって、trueな処理をできるだけシンプルな形でひとつだけ残し、最後に返す。そういう書き方になっています。
返す値として return 'index.php?a=120'
が重複しているのが気になりますので、さらにまとめます。
functiongetReturnAction($current){global$modx;if(!isset($current['parent'])){return'index.php?a=2';}if($current['parent']==0||!$modx->manager->isAllowed($current['parent'])){return'index.php?a=120';}return'index.php?a=120&id='.$current['parent'];}
上記のようになります。
まとめる順番として $current['parent'] == 0
を先にしている点がポイントです。
もし仮に $modx->manager->isAllowed()
の処理が非常に重いものだとします。
そうすると、先に $current['parent'] == 0
を持ってくることで、この判定が正である場合は isAllowed()
の判定を回避することができ、負荷を軽減できる可能性が高まります。コードを整理することで、このような可能性に気づきやすくなります。
次。
少し脱線になりますが、global宣言はかっこ悪いので、最近のMODXでは下記のようにしています。
functionevo(){global$modx;return$modx;}functionmanager(){returnevo()->manager;}
結局global宣言は使うのですが、大きなシステムの中で、ここだけに限定することができます。これを使って、
functiongetReturnAction($current){if(!isset($current['parent'])){return'index.php?a=2';}if(!$current['parent']||!manager()->isAllowed($current['parent'])){return'index.php?a=120';}return'index.php?a=120&id='.$current['parent'];}
最終的にはここまで整理することができます。変数代入を一度も使わずにすみました。
こういう作業はPhpStormを使うとはかどります。あまり頭を使わずにどんどん手を動かせます。
追記 2020-06-12
https://qiita.com/yamamotomasa/items/0bba421f958475ff72b2#comment-ead4717086e37bd9e018
@vf8974さんからコメントいただきました。さらにまとめることができます。
functiongetReturnAction($current){returnsprintf('index.php?a=%d%s',!isset($current['parent'])?2:120,$current['parent']&&manager()->isAllowed($current['parent'])?'&id='.$current['parent']:'');}
複数行に渡っていますが、実質ワンライナーですね。個人的には三項演算子はネストさせると複雑になるので避けているのですが、このくらいだとちょうどいいかも?一行目からreturnというのも潔くていいと思います!