目次
はじめに
特に意識せず、コーディング標準ツールの PHPStan を利用していましたが、レベル毎にどの問題を補足してくれるのか気になったので、まとめてみました。
前提
バージョン
- PHP
- 8.1.6
- PHPStan
- 1.6.9
こちらのリポジトリで確認しました
レベル0
基本的なチェック:シンタックスエラー
-
PHPStan エラー発生コード
class Profile() {}
-
PHPStan エラー発生コード実行時のログ
Parse error: syntax error, unexpected token "(", expecting "{"
-
PHPStan エラー解決方法
// クラス定義に () は不要 class Profile {}
不明なクラス
-
PHPStan エラー発生コード
new ProfileRepository();
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught Error: Class "ProfileRepository" not found
-
PHPStan エラー解決方法
// ProfileRepository クラスを定義する class ProfileRepository {} new ProfileRepository();
不明な関数
-
PHPStan エラー発生コード
isArray([]);
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught Error: Call to undefined function isArray()
-
PHPStan エラー解決方法
// 存在する関数を呼び出す is_array([]);
呼び出された不明なメソッド
-
PHPStan エラー発生コード
class AccountRepository {} AccountRepository::find();
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught Error: Call to undefined method AccountRepository::find()
-
PHPStan エラー解決方法
// find メソッドを定義する class AccountRepository { public static function find() {} } AccountRepository::find();
関数に渡された引数の数が間違っている
-
PHPStan エラー発生コード
is_array();
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught ArgumentCountError: is_array() expects exactly 1 argument, 0 given
-
PHPStan エラー解決方法
// is_array 関数は第一引数に値が必須 is_array([]);
常に未定義の変数
-
PHPStan エラー発生コード
class Account { public function find() { return $account; } } echo (new Account)->find();
-
PHPStan エラー発生コード実行時のログ
Warning: Undefined variable $account
-
PHPStan エラー解決方法
// account 変数を初期化する class Account { public function find() { return $account = 1; } } echo (new Account)->find();
レベル1
未定義の変数
-
PHPStan エラー発生コード
echo $profile;
-
PHPStan エラー発生コード実行時のログ
Warning: Undefined variable $profile
-
PHPStan エラー解決方法
// profile 変数を初期化する echo $profile = 1;
レベル2
不明なメソッドをすべてチェックする
-
PHPStan エラー発生コード
class BookRepository {} (new BookRepository())->find();
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught Error: Call to undefined method BookRepository::find()
-
PHPStan エラー解決方法
// find メソッドを定義する class BookRepository { public function find() {} } (new BookRepository())->find();
PHPDocs を検証する
-
PHPStan エラー発生コード
class AuthorRepository { /** * @param int $id ID * @return void */ public function find() {} } (new AuthorRepository())->find();
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// find メソッドと一致する PHPDocs を記載する class AuthorRepository { /** * @return void */ public function find() {} } (new AuthorRepository())->find();
レベル3
返り値の型
-
PHPStan エラー発生コード
function findBook(): array { return null; } findBook();
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught TypeError: findBook(): Return value must be of type array, null returned
-
PHPStan エラー解決方法
// 返り値の型を実態に合うように定義する function findBook(): ?array { return null; } findBook();
プロパティに割り当てられた型
-
PHPStan エラー発生コード
class Book { public int $id; } $book = new Book(); $book->id = '1';
-
PHPStan エラー発生コード実行時のログ
declare(strict_types=1);
を定義していればエラーになるFatal error: Uncaught TypeError: Cannot assign string to property Book::$id of type int
-
PHPStan エラー解決方法
// 返り値の型を実態に合うように定義する class Book { public int $id; } $book = new Book(); $book->id = 1;
レベル4
基本的なデッドコードチェック:常に false
-
PHPStan エラー発生コード
$authorId = 1; if (!$authorId) { throw new Exception(); } echo $authorId;
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// デッドコードは削除する $authorId = 1; echo $authorId;
基本的なデッドコードチェック:リターン後の到達不能コード
-
PHPStan エラー発生コード
function setDefaultAuthor() { $authors = []; return $authors; $authors[] = 'Tanaka'; } print_r(setDefaultAuthor());
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// デッドコードは削除する function setDefaultAuthor() { $authors = []; return $authors; } print_r(setDefaultAuthor());
レベル5
メソッドと関数に渡される引数の型をチェックする
-
PHPStan エラー発生コード
class CategoryRepository { public function findById(int $id): int { return $id; } } $repository = new CategoryRepository(); echo $repository->findById('1');
-
PHPStan エラー発生コード実行時のログ
declare(strict_types=1);
を定義していればエラーになるFatal error: Uncaught TypeError: CategoryRepository::findById(): Argument #1 ($id) must be of type int, string given, called
-
PHPStan エラー解決方法
// 引数の型と一致する型でメソッド呼び出しする class CategoryRepository { public function findById(int $id): int { return $id; } } $repository = new CategoryRepository(); echo $repository->findById(1);
レベル6
欠落しているタイプヒントを報告する
-
PHPStan エラー発生コード
function deleteBook($id): int { return $id; } echo deleteBook(1);
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// 引数のタイプヒントを定義する function deleteBook(int $id): int { return $id; } echo deleteBook(1);
レベル7
部分的に間違った union 型を報告する
-
PHPStan エラー発生コード
function deleteAuthor(string|int $id): int { return $id; } deleteAuthor(1);
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// 返り値の型になりうる型を定義する function deleteAuthor(string|int $id): string|int { return $id; } deleteAuthor(1);
レベル8
null 許容型のメソッドの呼び出しとプロパティへのアクセス
-
PHPStan エラー発生コード
class Category { public string $name = ''; function setName(?string $name): void { $this->name = $name; } } $category = new Category(); $category->setName(null); echo $category->name;
-
PHPStan エラー発生コード実行時のログ
Fatal error: Uncaught TypeError: Cannot assign null to property Category::$name of type string
-
PHPStan エラー解決方法
// プロパティを nullable にするか、setName メソッドの引数を null 不許可にする class Category { public ?string $name = ''; function setName(?string $name): void { $this->name = $name; } } $category = new Category(); $category->setName(null); echo $category->name;
レベル9
型について厳密にする(mixed 不許可)
-
PHPStan エラー発生コード
function findDefaultCategory(mixed $id): string { findCategoryById($id); return 'Default'; } function findCategoryById(int $id): mixed { return []; } print_r(findDefaultCategory(1));
-
PHPStan エラー発生コード実行時のログ
エラーにはならない
-
PHPStan エラー解決方法
// mixed を使わない function findDefaultCategory(int $id): string { findCategoryById($id); return 'Default'; } function findCategoryById(int $id): mixed { return []; } print_r(findDefaultCategory(1));
おわりに
サンプルコードを製造するのに何気に時間がかかりました。
この記事が他のエンジニアの助けになれば幸いです。