PHPのお勉強!

PHP TOP

下位互換性のない変更点

PHP コア

文字列と数値の比較

(厳密でないやり方で)数値と非数値文字列を比較する場合、 数値を文字列にキャストし、文字列と比較するようになりました。 数値と数値形式の文字列の比較は、以前と同じ振る舞いをします。 注意すべきなのは、これによって、 0 == "not-a-number"false と見なされるようになったことです。

比較 変更前 変更後
0 == "0" true true
0 == "0.0" true true
0 == "foo" true false
0 == "" true false
42 == " 42" true true
42 == "42foo" true false

その他の下位互換性のない変更

  • match が予約語になりました。

  • mixed が予約語になりました。 よって、クラスやインターフェイス、 トレイトの名前として使えなくなっています。 名前空間の中であっても同様です。

  • アサーションに失敗すると、デフォルトで例外をスローするようになりました。 古い振る舞いを望む場合、php.ini で assert.exception=0 と設定できます。

  • クラス名と同じ名前のメソッドは、コンストラクタと解釈されなくなりました。 __construct() メソッドを代わりに使って下さい。

  • static でないメソッドを、staticメソッドとしてコールできる機能が削除されました。 static でないメソッドをクラス名を使ってチェックした場合、 is_callable() は失敗します。 (オブジェクトのインスタンスを使ってチェックしなければいけません)

  • (real)(unset) キャストが削除されました。

  • track_errors ini ディレクティブは削除されました。 つまり、php_errormsg が利用できなくなったということです。 代わりに error_get_last() 関数が使えます。

  • 大文字小文字を区別しない定数を定義できる機能が削除されました。 define() 関数の第3引数はもはや true ではありません。

  • __autoload() 関数を使ってオートローダーを指定する機能は削除されました。 代わりに spl_autoload_register() を使うべきです。

  • set_error_handler() 関数で設定されるカスタムエラーハンドラには、 errcontext 引数は渡されなくなりました。

  • create_function() 関数は削除されました。 無名関数が代わりに使えます。

  • each() 関数は削除されました。 代わりに foreachArrayIterator を使うべきです。

  • Closure::fromCallable()ReflectionMethod::getClosure() を使って メソッドから生成されたクロージャーから this の束縛を解除できる機能は削除されました。

  • this を使っている適切なクロージャーから、 this の束縛を解除する機能も削除されています。

  • オブジェクトに対して array_key_exists() 関数を使える機能は削除されました。 isset() または property_exists() を代わりに使えます。

  • array_key_exists() 関数の引数 key の型に関する振る舞いが、 isset() 関数や配列アクセスの場合と一貫したものになりました。 全てのキーの型は通常の強制が行われ、配列やオブジェクトのキーは TypeError がスローされるようになりました。

  • はじめの数値のキーとして n を持つ配列は、 たとえ n が負の値であっても、 次の暗黙のキーは n+1 を使うようになります。

  • デフォルトの error_reporting のレベルは E_ALL になりました。 これより前のバージョンでは、 E_ALL から E_NOTICEE_DEPRECATED が除かれていました。

  • display_startup_errors は、 デフォルトで有効になりました。

  • 親クラスがないクラスの内部で parent を使うと、 致命的なコンパイルエラーが発生するようになりました。

  • @ 演算子は、致命的なエラー (E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_PARSE) を隠さなくなりました。 @ を使う時に、 error_reporting が 0 であることを期待しているエラーハンドラは、 代わりにマスクチェックを調整すべきです:

    <?php
    // こういうエラーハンドラは
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (
    error_reporting() == 0) {
    return
    false;
    }
    // ...
    }

    // 以下に置き換えましょう
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (!(
    error_reporting() & $err_no)) {
    return
    false;
    }
    // ...
    }
    ?>

    さらに、実運用環境で表示されていなかった、 情報のリークに繋がるエラーメッセージにも注意を払うべきです。 エラーのロギングと併せて、 display_errors=Off となっていることを確認するようにして下さい。

  • #[ は、コメントの開始として解釈されなくなりました。 この文法は、アトリビュート として使われるようになっているからです。

  • 非互換なメソッドのシグネチャによる継承エラー(リスコフの置換原則違反)については、 常に致命的なエラーが生成されるようになりました。 これより前のバージョンでは、警告が生成される場合がありました。

  • ビットシフトや加算、減算に対する連結演算子の優先順位が変更されました。

    <?php
    echo "Sum: " . $a + $b;
    // 上記は、以前のバージョンでは以下のように解釈されていました:
    echo ("Sum: " . $a) + $b;
    // PHP 8.0.0 からは、以下のように解釈されます:
    echo "Sum:" . ($a + $b);
    ?>

  • 実行時に null に解決されるデフォルト値を持つ引数は、 引数の型を暗黙のうちに nullable とマークすることはなくなりました。 明示的に nullable と宣言するか、 明示的にデフォルト値を null と宣言しなければなりません。

    <?php
    // 以下のようなコードは
    function test(int $arg = CONST_RESOLVING_TO_NULL) {}
    // このように置き換えるか
    function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
    // こう書きましょう
    function test(int $arg = null) {}
    ?>

  • たくさんの警告が Error 例外に変換されるようになりました:

    • オブジェクトでない値にプロパティを書き込もうとした場合。 これより前のバージョンでは、 null については stdClass オブジェクトが暗黙のうちに作られ、 空文字列については false となっていました。
    • PHP_INT_MAX キーが既に使われている配列に対して、 要素を追加しようとした場合。
    • 不正な型(配列やオブジェクト) を配列のキーや文字列のオフセットとして使おうとした場合。
    • スカラー値に配列のインデックスを書き込もうとした場合
    • 配列やTraversable でない値をアンパックしようとした場合
    • 未定義の、非修飾の定数にアクセスしようとした場合。 これより前のバージョンでは、 非修飾の定数にアクセスしようとすると、警告が発生し、 文字列として解釈されていました。
    • 可変長引数でない組み込み関数に、間違った数の引数を渡した場合、 ArgumentCountError がスローされるようになりました。
    • 無効な countable 型を count() に渡した場合、 TypeError がスローされるようになりました。

    多くの notice が警告に変換されるようになりました:

    • 未定義の変数を読み取ろうとした場合
    • 未定義のプロパティを読み取ろうとした場合
    • 未定義の配列のキーを読み取ろうとした場合
    • オブジェクトでない値のプロパティを読み取ろうとした場合
    • 配列でない値のインデックスにアクセスしようとした場合
    • 配列を文字列に変換しようとした場合
    • リソースを配列のキーとして使おうとした場合
    • null や bool 値や float の値を文字列オフセットとして使おうとした場合
    • 境界を超えて文字列のオフセットを読み取ろうとした場合
    • 文字列のオフセットに空文字列を割り当てようとした場合

  • 文字列のオフセットに複数バイトを割り当てようとすると、警告が発生するようになりました。

  • ソースファイル中に(文字列の範囲外のNUL バイトのような) 想定外の文字が含まれていた場合、 コンパイル時に警告を出す代わりに、 ParseError がスローされるようになりました。

  • 例外がキャッチされなかった場合、 "クリーンなシャットダウン" が行われます。 これは、例外がキャッチされなかった後、 デストラクタが呼ばれるということです。

  • コンパイル時の致命的なエラー "Only variables can be passed by reference" は、 実行時まで遅延され、 "Argument cannot be passed by reference" という Error 例外に変換されるようになりました。

  • "Only variables should be passed by reference" という警告は、 "Argument cannot be passed by reference" という例外に変換されるようになりました。

  • 無名クラスのために生成される名前が変更されました。 最初の親クラスやインターフェイスの名前が含まれるようになっています:

    <?php
    new class extends ParentClass {};
    // -> ParentClass@anonymous
    new class implements FirstInterface, SecondInterface {};
    // -> FirstInterface@anonymous
    new class {};
    // -> class@anonymous
    ?>

    上で示した名前の後に、NULバイトやユニークなサフィックスが続きます。

  • トレイトのエイリアス調整において、 クラス名を指定していないメソッド参照は、 曖昧でないことが必須になりました:

    <?php
    class X {
    use
    T1, T2 {
    func as otherFunc;
    }
    function
    func() {}
    }
    ?>

    T1::func()T2::func() が両方存在している場合、 PHP 8.0.0 より前のバージョンでは、 このコードは黙って動作し、func は T1::func を参照するものと想定されていました。 PHP 8.0.0 からは、このコードは致命的なエラーが発生します。 T1::func または T2::func を明示的に書く必要があります。

  • トレイトで定義される抽象メソッドのシグネチャは それを実装するクラスのメソッド側で、 一致しているかがチェックされるようになりました:

    <?php
    trait MyTrait {
    abstract private function
    neededByTrait(): string;
    }

    class
    MyClass {
    use
    MyTrait;

    // エラー。戻り値の型が一致しません。
    private function neededByTrait(): int { return 42; }
    }
    ?>

  • 無効にされている関数は、存在しない関数であるかのように扱われるようになりました。 無効にされている関数を呼び出しても unknown と報告されますし、 無効にされている関数を再定義することも可能になっています。

  • data:// ストリームラッパーは書き込み可能ではなくなりました。 これは、ドキュメント化されている振る舞いに一致します。

  • 算術演算子とビット演算子 +, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, -- は、オペランドのひとつが配列だったり、 リソース だったり、オーバーロードされていない object だったりした場合に、 一貫して TypeError をスローするようになりました。 これに対する唯一の例外は、配列と配列を + でマージする操作で、 これはまだサポートされています。

  • float 型から文字列型へのキャストは、 ロケールに依存しないように常に振る舞うようになります。

    <?php
    setlocale
    (LC_ALL, "de_DE");
    $f = 3.14;
    echo
    $f, "\n";
    // PHP 8.0.0 より前のバージョン: 3,14
    // PHP 8.0.0 以降: 3.14
    ?>

    数値のフォーマットをカスタマイズする方法については、 printf(), number_format() 関数 および NumberFormatter() メソッドを参照ください。

  • 推奨されなくなっていた、 オフセットを指定してアクセスするための波括弧のサポートが削除されました。

    <?php
    // 以下ではなく:
    $array{0};
    $array{"key"};
    // このように書いて下さい:
    $array[0];
    $array["key"];
    ?>

  • private メソッドに final 修正子を指定した場合、 そのメソッドがコンストラクタでない限り、警告が発生するようになります。

  • オブジェクトのコンストラクタで exit() が呼び出された場合、 オブジェクトのデストラクタはコールされなくなりました。 これは、コンストラクタが例外をスローしたときの振る舞いと一致します。

  • 名前空間に含まれる名前には、 ホワイトスペースを含めることができなくなりました。 つまり、Foo\Bar は名前空間の名前として認識されますが Foo \ Bar は認識されなくなったということです。 逆に、予約語のキーワードは名前空間の一部として許されるようになりました。 これによって、コードの解釈が変わるかもしれません。 new\xconstant('new\x') と同じですが、 new \x() とは異なります。

  • 三項演算子をネストする場合、明示的に括弧が必要になりました。

  • debug_backtrace()Exception::getTrace() メソッド は、引数にリファレンスを取らなくなりました。 これにより、バックトレースを通じて、関数の引数を変更できなくなります。

  • 数値形式の文字列の扱いが、より直感的で間違いにくいものに変更されました。 ホワイトスペースが前に付いている場合の扱いと一貫性を持たせるため、 数値形式の文字列の後にホワイトスペースを付けることが許されるようになりました。 これがもっとも影響するのは以下です:

    • is_numeric() 関数
    • 文字列同士の比較
    • 型宣言
    • インクリメントとデクリメント演算

    "数値が始めに来る文字列" の概念は殆どなくなっています; このケースは、移行を容易にするために存在しています。 "A non well-formed numeric value encountered" という E_NOTICE が発生する文字列 は、"A non-numeric value encountered" という E_WARNING が発生するようになっています。 そして、"A non-numeric value encountered" という E_WARNING が発生していた全ての文字列は、 TypeError が発生するようになりました。 これが最も影響するのは、以下の場合です:

    • 算術演算
    • ビット演算

    この E_WARNING から TypeError への変更は、 不正な文字列オフセットの場合に発生する "Illegal string offset 'string'" という E_WARNING にも影響します。 文字列から int/float に明示的にキャストする場合の振る舞いは変更されていません

  • マジックメソッドは引数と戻り値の型を持ち、 宣言された場合はチェックが行われるようになりました。 シグネチャは次の一覧と一致させるべきです:

    • __call(string $name, array $arguments): mixed
    • __callStatic(string $name, array $arguments): mixed
    • __clone(): void
    • __debugInfo(): ?array
    • __get(string $name): mixed
    • __invoke(mixed $arguments): mixed
    • __isset(string $name): bool
    • __serialize(): array
    • __set(string $name, mixed $value): void
    • __set_state(array $properties): object
    • __sleep(): array
    • __unserialize(array $data): void
    • __unset(string $name): void
    • __wakeup(): void

  • call_user_func_array() 関数に渡される 配列のキーは、引数名として解釈されるようになりました。 これより前のバージョンでは、静かに無視されていました。

  • 名前空間の内部で assert() と呼ばれる関数を宣言する ことは許されなくなり、 E_COMPILE_ERROR が発生するようになりました。 assert() 関数は PHP エンジンによって特別扱いを受けることになっているので、 名前空間の内部で同じ名前が定義されていると、 動作が一貫しなくなる原因になるからです。

リソースからオブジェクトへの移行

いくつかの リソース が、object に移行しました。 is_resource() 関数を使って戻り値をチェックしているコードは、 false を返すことをチェックするコードに置き換えるべきです。

  • curl_init() 関数は リソース ではなく、CurlHandle オブジェクトを返すようになりました。 curl_close() 関数はもはや意味をなしません。 CurlHandle インスタンスは、 参照されなくなった場合に、自動的に破棄されます。