LoginSignup
3

posted at

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

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));
    

おわりに

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

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
What you can do with signing up
3