動的型付け1とか静的型付き2とか私には正直わかりません3が、PHPは実行前にコンパイルフェイズがある4ので、動作させる前に型をチェックすることも、実はあります。
<?php
echo "Foo";
if (false) {
1 + [];
}
echo "Bar";
3v4lでの実行結果: https://3v4l.org/k6vLn
このコードはPHP5以前ではFooBar
と出力されますが、PHP7ではFatal error: Unsupported operand types
とエラーが出ます。これは先の記事で紹介したコンパイル時のエラーといふことです。
注意されたいのは、if (false) array_merge(1);
のような文はこれと違って何の警告も出ません。飽くまで演算子式のみです。
つまり、このような関数はPHPのコンパイル時には警告されません。
function foo(array $a, int $b): bool
{
return $a + $b;
}
まとめると、「PHPは実行しないと型検査されない」は正確には誤りで、「PHP(7以降)はリテラルの二項演算に限って型検査し、コンパイルエラーにする」となります。ただしPHPは実行時にその都度ファイルを読み込むので、コンパイルエラーは実行時エラーと大差ないように見えるのが難しいところです。
PHPの静的検査について
php -l
PHPの一番単純な静的検査はphp -l
です。プロジェクトをGit管理してる場合は、このようなシェルコマンドで全ファイルについてチェックすることができます。
git ls-files '*.php' | xargs -I{} php -l {}
例ですが、全ファイルの検査に時間がかかる場合はこのようにすることでorigin/master
との差分があるファイルだけを検査できます。
git fetch && git diff origin/master HEAD --name-only --diff-filter=ACMR | (grep -E '.php$' || true) | xargs -I{} php -l {}
上記の通り繰り返しますが、PHPは実行時にその都度ファイルを読み込むので、コンパイルエラーは実行時エラーとして現れます。そのためデプロイ時に差分だけでも検査することは大変重要です。ユーザーのブラウザにまっしろな画面や壊れたHTMLを見せるのは超カッコワルイですからね。
Phan, PHPStan
表現は異なりますが、どちらでも検出できます。
src/functions.php:5 PhanTypeInvalidRightOperand Invalid operator: left operand is array and right is not
------ -----------------------------------------------------------------
Line functions.php
------ -----------------------------------------------------------------
5 Binary operation "+" between array and int results in an error.
------ -----------------------------------------------------------------
PhanやPHPStanについては過去にいろいろ書きました。
- PHP 5分でわかる静的解析入門 / 黒點 さん - ニコナレ
- はじめてのPHPDocと型 / 黒點 さん - ニコナレ
- Phan静的解析がもたらす大PHP型検査時代 - pixiv inside [archive]
- PhanでPHPコード静的解析2018 #phpstudy / 黒點 さん - ニコナレ
- PHPで開発が捗るリアルタイムエラーチェック - Qiita
PHPStanについてはあんまり触れてないですね。まあ上記のようにgit
で最終リリースからの差分だけとってプルリクエストのコミットごとに検査するなど比較的高速にCI回せて便利です。あとVimとかEmacsのバックエンドとしてインラインエラーチェックができるのですこぶる便利です。
まとめ
- PHPの処理系(7以降)は、リテラルの二項演算に限ってFatal Errorを発生させます
- この種類のエラーは
php -l
で検査することが可能です
- この種類のエラーは
- PHPの処理系は型に関して詳細な静的解析はしません
- 代りにPhanやPHPStanを適宜利用することで事前に型チェックができます
- 擬陰性はありますが、それぞれ発展途上なのでバージョンアップごとに改善があります