こんにちは。福島です。今回はFuelPHPというPHPのwebフレームワークを使用したプロジェクトに、PHPStanという静的解析ツールを導入した時、解析対象のクラスの拡張元クラスの読み込みがうまくいかず困りましたので記事にしたいと思います。
静的解析ツールとは
コードを実行せずにコードをチェックするツールです。 今回ご紹介するPHPStan以外にも様々な静的解析ツールがあります。 ツールごとにどういった観点でコードをチェックするかが異なりますので、用途に合ったものを適宜選択していく必要があります。インデントを整えるもの、コードの中のバグを見つけるもの、依存関係の確認をするものなど様々な種類があります。
PHPStanについて
PHPStanは静的解析ツールの一つです。コードを実行することなく、コード内のエラーを指摘してくれます。下記のような観点でコードをチェックします。(公式ページのWhat it currently checks for?を翻訳し、引用しています。)
- instanceof、catch、typehints、およびその他の言語構造で使用されるクラスの存在。
- 呼び出されたメソッドと関数の存在とアクセシビリティ。渡された引数の数。
- メソッドが、返すように宣言したのと同じ型を返すかどうか。
- アクセスされたプロパティの存在と可視性。また、宣言されたタイプとは異なるタイプがプロパティに割り当てられているかどうか。
- フォーマット文字列に基づいて、sprintf / printf呼び出しに渡されるパラメーターの数。
- ブランチとループのスコープの中に変数が存在するかどうか。
- (string) 'foo'のような無意味なキャストと、異なる型を厳密に比較していて常にfalseになるオペランド。
少しわかりにくいので実際の指摘画面をご覧いただきイメージを膨らませていただけたらと思います。
sayHello
関数の中で未定義の$name
という変数を使おうとすると、上記のように未定義であることを指摘してくれます。こういったミスをコードを実行することなく修正できるようになり、コードの品質を上げることができます。
PHPStanのトップページに上記の画面のplaygroundがあるので、ブラウザ上でPHPのコードを書き解析を試すことができます。
FuelPHPにPHPStanを導入
前提条件
- 使用したOSはAmazon Linux 、Versionは2018.03
- FuelPHPのプロジェクトは作成済(今回はsampleとします)
- PHPの依存性管理ツールのComposerがインストール済
- コマンドは全て
sample
ディレクトリ直下で実行 - phpstan.neonファイルは
sample
ディレクトリ直下に配置
インストール方法
Composerを使用します。下記のコマンドでインストール可能です。
composer require --dev phpstan/phpstan
コマンド実行後にsample/fuel/vender/bin
の直下にphpstan
が追加されると思います。
使い方
<path_to_phpstan> analyse <target_file>
コマンドを実行することで解析が可能です。
今回の私の場合は下記のコマンドです。
fuel/vendor/bin/phpstan analyse <target_file>
設定
phpstan.neon
という設定ファイル、またはコマンドラインのオプションで設定を行います。
解析時の指摘ルールの厳しさを表すlevel
オプションのみ設定が必須です。
その他にも様々な設定項目があり、公式のConfig Referenceに詳細な記載がありますので、こちらの中からやりたいことを見つけていくのが早いのかなと思います。
FuelPHPに導入する際の拡張元のクラスの読み込みの設定
下記のようなsample/fuel/app/classes/controller/sample.php
の解析を行う場合を考えます。
sample.php
<?php class Controller_Sample extends Controller_Template { protected $data = array(); protected $header_link = true; public function before() { if (!empty($this->template) and is_string($this->template)) { // Load the template $this->template = \View::forge($this->template); } return parent::before(); } }
sample/fuel/app/classes/controller/sample.php
で定義されているController_Sample
クラスはsample/fuel/core/classes/controller/template.php
で定義されているController_Templates
クラスを拡張しており、さらにController_Templates
は、sample/fuel/core/classes/controller.php
で定義されているController
クラスを拡張しているという状況です。
phpstan.neon
parameters: level: 7
設定必須のlevel
オプションのみ上記のようにphpstan.neon
に設定を行い解析を行うと、下記のエラーが出ます。
$ phpstan analyse fuel/app/classes/controller/sample.php Note: Using configuration file /home/sample/phpstan.neon. 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------ -------------------------------------------------------------------- Line sample.php ------ -------------------------------------------------------------------- Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 6 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 6 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 6 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 8 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 8 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols 8 Reflection error: Controller_Template not found. Learn more at https://phpstan.org/user-guide/discovering-symbols ------ -------------------------------------------------------------------- [ERROR] Found 7 errors
Controller_Sample
クラスの拡張元であるController_Template
クラスが読み込めないという問題が発生しました。
PHPStanにはデフォルトで読み込む部分以外を読み込む場合には、Discovering Symbolsの機能を使う必要があります。
今回の場合は、FuelPHPの読み込みのルールを、PHPStanの機能のDiscovering SymbolsのbootstrapFiles
オプションで設定する必要があるのではないかと推測していました。しかし、どのファイルを指定すればよいか分からず困ってしまいました。
解決策
sample
ディレクトリ直下にあるoil
というファイルをbootstrap.php
に設定することで、拡張元のクラスの読み込みが可能となりました。実際には、oil
をコピーしbootstrap.php
として保存し、不要なLoad oil package
とFire up the command line interfact
の部分をコメントアウトし、そちらを読み込むように設定しています。現状は問題なく動いていますが、より良い方法があるかもしれません。
phpstan.neon
parameters: level: 7 bootstrapFiles: - bootstrap.php
bootstrap.php
<?php /** * Fuel is a fast, lightweight, community driven PHP 5.4+ framework. * * @package Fuel * @version 1.8.1 * @author Fuel Development Team * @license MIT License * @copyright 2010 - 2018 Fuel Development Team * @link http://fuelphp.com */ /** * Refuse to run oil when called from php-cgi ! */ if (substr(php_sapi_name(), 0, 3) == 'cgi') { die("The use of oil is not supported when running php-cgi. Oil needs php-cli to function!\n\n"); } /** * Set error reporting and display errors settings. You will want to change these when in production. */ error_reporting(-1); ini_set('display_errors', 1); /** * Website document root */ define('DOCROOT', __DIR__.DIRECTORY_SEPARATOR); /** * Path to the application directory. */ define('APPPATH', realpath(__DIR__.'/fuel/app/').DIRECTORY_SEPARATOR); /** * Path to the default packages directory. */ define('PKGPATH', realpath(__DIR__.'/fuel/packages/').DIRECTORY_SEPARATOR); /** * The path to the framework core. */ define('COREPATH', realpath(__DIR__.'/fuel/core/').DIRECTORY_SEPARATOR); // Get the start time and memory for use later defined('FUEL_START_TIME') or define('FUEL_START_TIME', microtime(true)); defined('FUEL_START_MEM') or define('FUEL_START_MEM', memory_get_usage()); // Load in the Fuel autoloader if ( ! file_exists(COREPATH.'classes'.DIRECTORY_SEPARATOR.'autoloader.php')) { die("No composer autoloader found. Please run composer to install the FuelPHP framework dependencies first!\n\n"); } // Load in the Fuel autoloader require COREPATH.'classes'.DIRECTORY_SEPARATOR.'autoloader.php'; class_alias('Fuel\\Core\\Autoloader', 'Autoloader'); // Boot the app require APPPATH.'bootstrap.php'; // Load oil package // \Package::load('oil'); // Fire up the command line interfact // \Oil\Command::init($_SERVER['argv']);
上記の設定を行った結果下記のような出力が得られ、問題なく解析が行えていることが分かります。
$ phpstan analyse fuel/app/classes/controller/sample.php Note: Using configuration file /home/sample/phpstan.neon. 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% [OK] No errors
まとめ
FuelPHPにPHPStanを導入する際に、拡張元のクラスの読み込みに苦戦した部分について紹介しました。 FuelPHPに静的解析を導入することをお考えの方にお役に立てれば嬉しいです。
参考資料
PHPStan のドキュメント
PHPStan機能の概要
[PHP] 静的解析ツールPHPStanの機能概要 - Qiita
株式会社メルカリ様での活用方法