PHPStanを用いた静的解析について

2024/01/17
高橋

こんにちは。株式会社リンクネット、ソリューション事業部の高橋です。

最近弊社ではPHPのバージョンアップに関する需要が増えています。
PHPのバージョンアップの際には、関数が削除されたり、
引数の数が変わるなどソースコードを修正する必要が生じます。
これらの変更を目視で確認し修正するのは大変なため、
何かツールがないか探してみたところ、PHPの静的解析ツールであるPHPStanが有効そうでした。

本記事では PHPStan がPHPのバージョンアップに適しているか検証します。

■ 環境

$ php -v
PHP 8.1.24 (cli) (built: Oct  6 2023 09:46:19) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.24, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.24, Copyright (c), by Zend Technologies

■ PHPStanの導入

今回は解析対象のソースコードのドキュメントルートに composerを使用して、 PHPStanをインストールします。

$ composer require --dev phpstan/phpstan

今回は素のPHPを対象としていますが、Laravelで静的解析をする場合は、
下記のようにcomposerを使用して、 larastanをインストールします。

$ composer require --dev -W phpstan/phpstan phpstan/extension-installer nunomaduro/larastan

■ PHPStanの設定

PHPStanの設定はコマンド時に指定可能ですが、
何度も実行することを想定して、設定ファイルを作成します。

$ vi phpstan.dist.neon 

phpstan.dist.neonの中身は下記です。
levelは解析時のレベル、pathsは解析対象のディレクトリを指定します。

parameters:
    level: 1
    paths:
        - src

各levelの詳細は下記を参照
https://phpstan.org/user-guide/rule-levels

設定ファイルを指定して実行する場合は下記のコマンドで実行します。

$ ./vendor/bin/phpstan analyze -c ./phpstan.dist.neon

一度、実行してしまえば、以降は下記のような短縮されたコマンドで実行できます。

$ ./vendor/bin/phpstan analyze

■ 現在のPHPのバージョンで利用できない関数がないかチェックする

現在は廃止されたcreate_functionを対象に静的解析を実行します。
ソースコードは下記です。

<?php
//  php8.0で廃止されて記述方法
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo $newfunc(2, M_E) . "\n";

//  php8.0でも使える記述方法
$newfunc = function($a,$b) { return "ln($a) + ln($b) = " . log($a * $b); };
echo $newfunc(2, M_E) . "\n";

実行結果は下記です。
想定通り、PHPの実行結果、PHPStanの実行結果はともにcreate_functionが存在しないのでエラーになっています。

$ php ./src/test70.php
PHP Fatal error:  Uncaught Error: Call to undefined function create_function() in /home/appuser/project/phpstan/src/test70.php:2
Stack trace:
#0 {main}
  thrown in /home/appuser/project/phpstan/src/test70.php on line 2
 [ERROR] Found 1 error
$ ./vendor/bin/phpstan analyze
Note: Using configuration file /home/appuser/project/phpstan/phpstan.dist.neon.
 ------ ---------------------------------------------------------------------
  Line   test70.php
 ------ ---------------------------------------------------------------------
  3      Function create_function not found.
         💡 Learn more at https://phpstan.org/user-guide/discovering-symbols
 ------ ---------------------------------------------------------------------


 [ERROR] Found 1 error

■ 予約語についてチェックする

php8.0以降mixedmatchといった単語は予約語になりました。 よって、クラスやインターフェース、 トレイトの名前として使えなくなっています。 そのため、関数と同様に下記のようなmixedをクラス名とした際は、 PHPの実行時と静的解析でともにエラーになると推測できます。

<?php

    class mixed{

        public string $str;

        function __construct()
        {
            $this->str = "";
        }
        public function index():bool
        {
            echo $this->str.PHP_EOL;
            return true;
        }
    }


    $old = new mixed();
    $old->str = 'hoge';
    $old->index();


    class mixedClass{

        public string $str;

        function __construct()
        {
            $this->str ='';
        }
        public function index():bool
        {
            echo $this->str.PHP_EOL;
            return true;
        }
    }


    $new = new mixedClass();
    $new->str = 'hoge';
    $new->index();

PHPの実行結果は下記です。 想定通りエラーが発生しています。

$ php ./src/test70.php
PHP Fatal error:  Cannot use 'mixed' as class name as it is reserved in /home/appuser/project/phpstan/src/test70.php on line 3

静的解析の実行結果は下記です。
想定と異なりエラーとなりませんでした。
予約語のチェックには使用できないようです。

$./vendor/bin/phpstan analyze
Note: Using configuration file /home/appuser/project/phpstan/phpstan.dist.neon.
 [OK] No errors

■ まとめ

PHPStanを用いて、PHPのバージョンアップ対応に利用できないか、
試してみましたが関数については問題なくチェックできるようですが、
予約語には対応できていませんでした。
ただし、予約語はIDE(統合開発環境)ではエラーとなっていたたため、
Xdebugなどと組み合わせれば十分役に立つと考えられます。

また解析レベルを上げることで、静的な型付けを正しくチェックできるため、 規模の大きい案件ではあったほうが潜在的なバグが見つけやすく有用なツールであると考えられます。

前の記事次の記事