コードを読んでいて気持ちのよい瞬間
システム開発をする際、要件があり、それを満たすようにコードが記述される。
私たちが対峙するコードには2種類あると考えており、1つ目はゼロから新たに作るコード、2つ目は誰かが作った既存のコード。後者のケースではコードと紐づく要件が残されていないケースがたまによくあり、コードから要件を読み取ることになる。
そして、ぱっとみ不可思議に思えたロジックの意図が読み取れて、認識していなかった要件の存在に気づけた時、すごく気持ちがよい。これは業務でもOSSでもそう。
先日Rectorのコードを読んでいた。Rectorというのは静的解析の一種でPHPのコードを自動的にリファクタリングしてくれる。
型を評価するNodeTypeResolverというクラスにresolveTernaryType()というメソッドがある。
/**
* @return \PHPStan\Type\MixedType|\PHPStan\Type\UnionType
*/
private function resolveTernaryType(Ternary $ternary)
{
if ($ternary->if !== null) {
$first = $this->getType($ternary->if);
$second = $this->getType($ternary->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
$condType = $this->getType($ternary->cond);
if ($this->isNullableType($ternary->cond) && $condType instanceof UnionType) {
$first = $condType->getTypes()[0];
$second = $this->getType($ternary->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
return new MixedType();
}
これは三項演算子を評価するメソッド。
三項演算子とは(expr1) ? (expr2) : (expr3)
という形式で、expr1がtrueの場合はexpr2、falseの場合はexpr3を値とする構文。
上記コードの$ternary->cond
はexpr1、$ternary->if
はexpr2、$ternary->else
はexpr3に相当する。
で、はじめ不可思議に感じたのは$ternary->if !== null
の部分。$ternary->if
がnullのケースを想定している。初見で、なぜ?となった。
マニュアルを見てみるとexpr2を省略する記法(expr1 ?: expr3
)があるとわかった。どうやらこれをエルビス演算子と呼ぶらしい。PHPマニュアルには書かれていないで公式の呼び方ではないっぽいが。
ちなみに似たような記法にnull合体演算子というものがあるけれど、こちらはPHP ParserにおいてTernaryではなくCoalesceで表される。
コードリーディングは筋トレに似てる。
地道で退屈な時もあるけど、たまに成果を感じる瞬間があり充実感がある。
Discussion