Home > ブログ > ReturnTypeWillChange属性の指定

ブログ

ReturnTypeWillChange属性の指定

PHPの古いバージョンで作成されたシステムの7系だったり8系への対応を頼まれることがよくあります。

現時点で最新版はPHP8.1なのでシステムの更新を行う場合は、7.4~8.1までの対応作業を行うことが多くなりますが、対応作業を行っていると以下のメッセージが出力されることがあります。

PHP Deprecated: Return type of <Method name> should either be compatible with <Method name>, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in xxxx

Deprecatedなので現状は問題なく動作するのですが、将来バージョンで動作しなくなる可能性もあるので対応しておくべきでしょう。 これはPHP8.1で加えられた非互換な変更の一つになります。 PHP8.1からは"内部クラス(*1)"のメソッドは、それをオーバライドする際、互換性がある戻り値の型を宣言することが必須になりました。

参考:
https://www.php.net/manual/ja/migration81.incompatible.php
内部クラスと戻り値の型の互換

エラーが出る具体的な例を見てみましょう。

PHP8.1でDeprecatedが発生する例

// PHP8.0まではOK
// PHP8.1ではPHP Deprecatedが発生
class SomethingClass implements \JsonSerializable
{
    public function jsonSerialize()
    {
        return 'This is a instance of '.get_class($this);
    }
}

このクラスはPHPの内部クラス/インターフェースであるJsonSerializableのjsonSerialize()メソッドを実装しています。 一方、PHPの処理系で定義されているJsonSerializeは以下のようになっており、SomethingClassのjsonSerialize()メソッドと戻り値の型宣言が異なります。

JsonSerializableの定義

interface JsonSerializable {
    public jsonSerialize(): mixed
}

このようなケースは、PHP8.0までは問題なかったのですが、PHP8.1では上記のDeprecatedが発生するようになりました。

「それじゃあSomethingClassの jsonSerialize() の定義を jsonSerialize(): mixed にしよう」と思ったらちょっと待ってください。 内部クラスとユーザ定義クラスのメソッドの戻り値の型宣言の不一致はDeprecateになるのですが、ユーザ定義クラス間での不一致はFatal Errorになります。

ユーザ定義クラス間での不一致はFatal Errorになる

// PHP7-8.1でFatal Errorになる。
class Foo {
    public function doSomething(): bool {}
}
class Bar extends Foo {
    public function doSomething() {}  /* 戻り値の型が未指定 */
}

このため、SomethingClassクラスを継承してjsonSerialize()をオーバーライドしているクラスがあった場合はそれらも修正する必要が出てきます。 自分のアプリ内でしか使っていないクラスなら全て修正すればいいのですが、 ライブラリとして外部に提供している場合は、このシグネチャーの変更は互換性がなくなることを意味するので注意が必要です。

このように戻り値の型宣言を修正できない場合は、エラーメッセージにもあるように#[\ReturnTypeWillChange]属性の宣言を追加して一時的にメッセージを抑止するようにします。

ReturnTypeWillChange属性を指定してDeprecatedを抑止

class SomethingClass implements \JsonSerializable
{
    #[\ReturnTypeWillChange]
    public function jsonSerialize()
    {
        return 'This is a instance of '.get_class($this);
    }
}

これでPHP8.1においてもDeprecatedメッセージが出力されなくなります。

PHP5.4系とかの旧PHPでも動作させたいのでreturn型を宣言できない場合もこの方法を取ることになります(*2)。

PHPはPHP7以降、型宣言まわりを中心に拡張が続けられており、互換性のない変更が加えられることも多くなっています。PHPという言語自体、過渡的な状態にあるようにみえます。

[型宣言関連の変更の履歴(参考)]

PHP7.0

スカラー型
戻り値の型宣言

PHP7.1

nullableな型
void関数

PHP7.2

object型

PHP7.4

型付きプロパティ

PHP8.0

union型
mixed型

PHP8.1

交差型
Never型

各バージョンで少しずつ拡張されているのがわかります。 型宣言があることでIDE等の静的解析機能によるサポートが便利になるのも事実ですが、開発をしていて「このPHPバージョンでこの型宣言ができたっけ?」となることも多いので、このあたりの機能開発も早く落ち着いて欲しいところです。

(*1) ここでいう内部クラスとはPDOなどのようにPHPの処理系によって提供されているクラスのことを指します。内部"クラス"となっていますが、内部インターフェースのメソッドについても同じ動作になります。

(*2) 5.4系はさすがに切り捨てたいところですが、CentOS7のデフォルトバージョンなのでいまだに対応が必要になるケースがあったりします。

投稿日:2022/06/14 00:03

タグ: PHP

Top

アーカイブ

タグ

Server (18) プログラミング (15) 作業実績 (15) PHP (11) ネットワーク (9) C (7) C++ (7) Webアプリ (6) EC-CUBE (6) Nginx (5) OpenSSL (5) laravel (4) Linux (4) 書籍 (4) JavaScript (3) AWS (3) Rust (3) Vue.js (3) Golang (2) Symfony (2) Apache (1) デモ (1) お知らせ (1) MySQL (1) CreateJS (1) OSS (1)