47
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【PHP9】ついに未定義プロパティが使えなくなる

Last updated at Posted at 2022-05-16

先日PHP9で未定義変数が使えなくなることが決まりましたが、それのプロパティ版です。

class HOGE{
  public function __construct(){
    $this->a = 1;  // PHP8.2以降エラー
    echo $this->b; // PHP9.0以降エラー
  }
}

このソースコード、PHP8.1まではE_WARNINGが出るものの動作はします。
しかしこのうち、未定義プロパティへの代入$this->a=1PHP8.2で禁止されました。
そしてPHP9では、未定義プロパティの取得$this->bが禁止されることとなります。

以下は該当のRFC、Undefined Property Error Promotionの日本語訳です。

Undefined Property Error Promotion

Introduction

未定義プロパティとは、まだ定義されていないプロパティのことです。

未定義プロパティにアクセスするとUndefined property <className>::$varNameのE_WARNINGが表示され、その値はNULLであるかのように扱われますが、実行が中断されることはありません。
しかし、これは意図しない挙動である可能性が高いでしょう。

未定義プロパティへのアクセスは、かつてはE_NOTICEでしたが、Reclassifying engine warningsのRFCへの対応の結果、PHP8.0においてE_WARNINGになりました。
PHP9がリリースされる頃には、未定義プロパティへのアクセスがE_WARNINGになってから既に5年以上が経つことになります。

Proposal

このRFCは、次のメジャーバージョンアップにおいて未定義プロパティへのアクセスを禁止し、アクセスした場合はError例外をスローすることを提案しています。
未定義である可能性を考慮せずプロパティにアクセスした際に発生するもので、Undefined property <className>::$varNameのE_WARNINGが起こるか否かで区別されます。
isset() / empty() / null合体演算子等は、プロパティが未定義であるかを考慮するため、本RFCによる影響はありません。

マジックメソッド__getが存在する場合は現在と同じ動作となり、未定義プロパティへのアクセスはエラーにならず__getに転送されます。

Considerations

PHPは伝統的に、データ構造内の未定義値へのアクセスについては非常に緩い態度を取っていました。

値を格納する場所としては、主に以下の3か所があります。

変数 スタックフレーム
プロパティ HashTable
配列 バケツ

このうち変数については、Undefined Variable Error PromotionによってPHP9.0でエラーになる予定です。

次のターゲットがプロパティになるのは自明なことでしょう。

また未定義プロパティへの書き込みはDeprecate dynamic propertiesにおいて禁止されました。
PHP9.0以降、#[AllowDynamicProperties]アトリビュートを指定しないかぎりエラーになります。

未定義プロパティへの書き込みがエラーになるのであれば、未定義プロパティからの読み取りも同じようにエラーになるのが自然な成り行きというものです。

なお、書き込みのルールについては暗黙的な例外があり、それはstdClassです。
stdClassは未知のプロパティを保持することを目的としたクラスであるからです。
たとえば配列をオブジェクトにキャストしたり、json_decodeしたときに生成されます。

ユーザ定義クラスに対して未定義プロパティを読み込むのはほとんどの場合プログラマのミスですが、たとえばJSONを扱うときに以下のようなコードを書くのはよくあることでしょう。

if ($obj->name) { 
  echo "Hello " . $obj->name;
}

これは$obj->nameが未定義である場合はE_WARNINGが発生するのでお勧めはできませんが、値はnullと評価されるため処理自体は実行されます。

このRFCが採択された場合、今後はissetempty等を用いて、以下のように書き直す必要があります。

if (isset($obj->name)) { 
  echo "Hello " . $obj->name;
}

このRFCでは、stdClassについても例外を設けず、読み取りについては一貫してエラーを出します。

Backward Incompatible Changes

undefined propertyのE_WARNINGが発生する方法で未定義のプロパティにアクセスしている場合、PHP9以降ではエラーになります。

Proposed PHP Version(s)

PHP9.0。

ターゲットのバージョン指定としては類を見ないものですが、このRFCの意図として、この変更をあらかじめ告知しておくことで開発者へのコード修正の期間を最大限に増やす目的があります。

次のマイナーバージョンアップにおいて、PHP9においてエラーになる旨の警告メッセージを表示します。

Voting

投票期間は2022/04/22から2022/05/05まで。

このRFCは、賛成31反対5の賛成多数で受理されました。

感想

Q:どうしてUndefined Variable Error Promotionで一緒にエラーにしなかったの?
A:エラーメッセージがWarning: Undefined variable $varnameではなかったからじゃよ。

Undefined Variable Error PromotionのRFCでは、エラーになるのはメッセージがWarning: Undefined variable $varnameである場合だけだと定義していました。
プロパティの場合、メッセージがWarning: Undefined property: HOGE::$varnameとなるので対象外だったというわけですね。

そんなわけで、未定義変数および未定義プロパティへの読み取りアクセスはPHP9以降エラーになります。
定義されていない値へのアクセスは今後もびしばし厳しくなることが予想されます。
そのようなコードは書かないようにしましょう。

といっても、まだ残っている未定義値へのアクセスって配列インデックスくらいですかね。

それに現在でも未定義値へのアクセスはE_WARNINGになるので、普通に書いているかぎりでは対処しているはずです。
これが影響するのは、主にPHP5時代から残っているような古き悪きコードで、そしておそらくそういうコードはそもそも他の変更で既に動かなくなっていることでしょう。

47
16
2

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
47
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?