Quantcast
Channel: 初心者タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 21113

コード整理の流れを実演してみます(PHP)

$
0
0

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;}

少し難しいかもしれませんが、上記のように整理できます。$parent0を代入するのをやめて、そこで return で返すようにしました。

返す値が分かりやすくなったので、今度は分岐条件のほうに目を向けていきます。 $parentの値ですが、これは関数に引数として与えられるものではありません。引数は $currentという配列です。

$parent$current['parent']の値だけを見ます。$current['parent']はMODX内では親ページのIDを示すもので、0を含む「数値」が入ります。

ということは・・・

$current['parent']が存在する場合は、それをそのまま $parentに代入し、存在しない場合は rootという「文字列」を代入する。ということが読み取れるでしょうか?

$parentrootの場合は、

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という文字列は、ただの目印でした。 $parentrootを代入する処理がなくなり、すぐに 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というのも潔くていいと思います!


Viewing all articles
Browse latest Browse all 21113

Trending Articles