PHPでの null と false の使い分けの指針として、タイプヒンティングの仕様を参考に考えるという話。
/**
* @param \DateTime $d
* @return \DateTime
*/
function test(\DateTime $d)
{
return $d;
}
これで DateTime
じゃない型の変数を入れると fatal error になる関数のできあがり。
...なんですが、引数を省略できるようにしたいときどうするか。デフォルト引数に意味のあるインスタンスを指定できない(PHPはJavaと違ってstatic領域に静的に存在するインスタンスを定義できない)ので、なにか代わりの即値を入れるんですが...
function test(\DateTime $d = 0) { ... }
これはエラー。
function test(\DateTime $d = false) { ... }
これもエラー。
function test(\DateTime $d = null) { ... }
これはオッケー。タイプヒンティングのある引数のデフォルト引数には null しか書けないのです。
ってことは、これってつまり:
test();
これのことですね。
test(null);
DateTime
のインスタンスしかダメなはずの引数なのに、特別に null
と書いても通るということ。ここ、0
や false
だと fatal error です。
null
でもオッケーな関数宣言をあらためて。
/**
* @param \DateTime $d
* @return \DateTime
*/
function test(\DateTime $d=null)
{
return $d;
}
phpDocの型宣言として見た場合、型の部分は、「その型の値 (もしくは null
)」という意味を暗に持つということになります。null instansof Any == true
なのです。Javaっぽいです。Scalaで考えるとさらによくわかります。
ということで、戻り値も、その型もしくは null
を返すことを保証する、というふうに考えれば、
test(test(null));
は、型違反ではありません。あの「失敗した場合falseを返します」仕様は、このようなタイプヒンティング仕様とは矛盾するのです。というわけで、
/**
* @param DateTime|false $d
* @return DateTime|false
*/
function test($d=false) { ... }
こういうのは、せっかくタイプヒンティングのあるPHP5世代のインターフェースとしては、避けたほうがいいですね。false
を使う関数は、型を合わせる意識がなかった、あのPHP4時代の名残なのです。
暗黙的に 「null
かもしれない」は偏在しているので、false
とは違い、Docコメントに @param \DateTime|null $d
と書く必要はありません。PhpStormでも、オブジェクトまたは false
を入れる関数に関しては、|false
が必要ですが、そのいっぽうで |null
は書かなくてもそう解釈してくれます。
あ、けど、デフォルト引数指定しないタイプヒンティングは null
すら禁止になるので、そこはJavaと違ってていいですね。少なくともあのヌルポの心配はしなくていい。
新しく書くライブラリ、書き換えても問題ない箇所では、積極的に null
を使っていきましょうというのが、最近のPHPの仕様変更の理由ですね。これまで書いてた ===false
がバグるかもしれないのは辛いですが、まあ、未来に向かっていきますか。