5
4

More than 1 year has passed since last update.

PHPStanのレベル別エラー発生のサンプルコードと解決方法

Posted at
1 / 34

目次


はじめに

特に意識せず、コーディング標準ツールの 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));
    

おわりに

サンプルコードを製造するのに何気に時間がかかりました。
この記事が他のエンジニアの助けになれば幸いです。

5
4
0

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
5
4