0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PHPStan】nullチェック後もPHPStanの指摘が出る原因と解決策

Last updated at Posted at 2025-03-23

初めに

PHPStanを導入すると、nullチェックをしたにもかかわらず、null許容型と判断され、Cannot call method が出てしまうこともあるかもしれません。
ここでは、その際の解決方法について説明します。

問題:nullチェック後もnull許容型であることを指摘されてしまう

以下のようなコードを解析してみます。

  • $fugaFuga|null型のためnullチェックを実施
  • Exceptionの呼び出しのために共通的なメソッドを使用
解析対象
class Hoge
{
    public function hogeMethod():void
    {
        // 返却値がnull許容型のメソッドを呼び出す
        /** @var Fuga|null $fuga */
        $fuga = $this->getFuga();

        // null の場合は共通的なエラーを返すメソッドを呼び出す
        if (is_null($fuga)) {
            $this->throwCommonException();
        }

        // null じゃなくなったので、安全に呼び出せるはず
        $piyo = $fuga->getPiyo();
    }

    /**
     * 共通エラーを返すメソッド
     */
    private function throwCommonException(): void
    {
        throw new Exception(
            "common error",
            500
        );
    }
}

解析したところ、nullチェックしたにも関わらずCannot call method の指摘が出てしまいました。

実行結果
$ ./bin/phpstan analyse

:14	   Cannot call method getPiyo() on Fuga|null.

原因:nullチェック時に呼び出す共通エラーメソッドの戻り値がvoidになっている

この原因はthrowCommonException()の戻り値がvoidになっていることです。
このメソッドではthrowしかしていませんが」、phpstanは中身を気にしません。voidであればこの後も処理が継続されると認識してしまいます。

    /**
     * 共通エラーを返すメソッド
     */
    private function throwCommonException(): void
    {
        throw new Exception(
            "common error",
            500
        );
    }

解決策1:nullチェック時にreturnする

一つ目の解決策は「nullチェックの中でreturnしてあげること」です。
とはいえ、PHPStanのために意味のないreturnを書くのは少し気になりますね。

    public function hogeMethod():void
    {
        // 返却値がnull許容型のメソッドを呼び出す
        /** @var Fuga|null $fuga */
        $fuga = $this->getFuga();

        // null の場合は共通的なエラーを返すメソッドを呼び出す
        if (is_null($fuga)) {
            $this->throwCommonException();
            // 明示的にリターンする!
            return;
        }

        // null じゃなくなったので、安全に呼び出せるはず
        $piyo = $fuga->getPiyo();
    }

解決策2:neverを用いる

二つ目の解決策はPHPのnever型を用いることです。
returnを用いる場合と違い、余計な処理を書かなくて済むのは嬉しいですね。

    /**
     * 共通エラーを返すメソッド
     */
    private function throwCommonException(): never
    {
        throw new Exception(
            "common error",
            500
        );
    }

PHP公式ドキュメントには、never型について以下のように書いてあります。

never は、 関数が戻ってこないことを示す戻り値の型です。 これは、関数の中で exit() がコールされるか、 例外がスローされるか、 無限ループに入るかのいずれかであることを意味します。

これによって、処理が終わることをPHPStanに明示的に示すことができました。

終わりに

neverはPHP8.1から登場した比較的新しい型です。
PHPStanを既存のシステムに導入すると、このような問題に直面するかもしれません。
その際はnever型に置き換えることも検討してみてください。

ここまでご覧いただきありがとうございました!

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?