LoginSignup
18
4

More than 5 years have passed since last update.

PHPは事前に型を検査する(こともある)

Last updated at Posted at 2018-10-24

動的型付け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については過去にいろいろ書きました。

PHPStanについてはあんまり触れてないですね。まあ上記のようにgitで最終リリースからの差分だけとってプルリクエストのコミットごとに検査するなど比較的高速にCI回せて便利です。あとVimとかEmacsのバックエンドとしてインラインエラーチェックができるのですこぶる便利です。

まとめ

  • PHPの処理系(7以降)は、リテラルの二項演算に限ってFatal Errorを発生させます
    • この種類のエラーは php -l で検査することが可能です
  • PHPの処理系は型に関して詳細な静的解析はしません
    • 代りにPhanやPHPStanを適宜利用することで事前に型チェックができます
    • 擬陰性はありますが、それぞれ発展途上なのでバージョンアップごとに改善があります

PHPは弱い動的型付け? 知らんなあ5。静的解析しよう6


  1. dynamic typing, 実行時型検査, 動かしながら型を調べるイメージ 

  2. statically typed, 実行前型検査, 動かす前に変数や関数の型を決めてしまうイメージ 

  3. ある程度わかるし説明できるつもりですが、わからなくなりました 

  4. この表現はかなり語弊があるのですが、あるPHP入門書にこの記事を誤読したかのようなコラムが掲載されて、私はかなり落ち込みました 

  5. @tadsanは実行時型チェックが大好きです 

  6. 実際には静的型解析によってあらゆる問題を解決できるわけではなく、実際の用途と異なる擬陰性が報告されることも多くあるなど難しいところがあります 

18
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
4