PHP 8.1.28 Released!

下位互換性のない変更点

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 がスローされるようになりました。

    多くの 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 を返すことをチェックするコードに置き換えるべきです。

COM および .Net (Windows)

タイプライブラリ から 大文字小文字を区別せずに定数をインポートする機能は削除されました。 com_load_typelib() 関数の第二引数は もはや false ではありません。 com.autoregister_casesensitive はもはや無効ではなくなりました。 つまり、com.typelib_file の #case_insensitive マーカーは無視されます。

CURL

CURLOPT_POSTFIELDS は、 オブジェクトを配列として受け入れなくなりました。 オブジェクトを配列として解釈させるためには、 明示的に (array) キャストを行って下さい。 配列を受け入れる他のオプションにも、同じことが当てはまります。

日付と時刻

mktime()gmmktime() 関数は 少なくともひとつ引数が必要になりました。 現在のタイムスタンプを取得する用途には time() 関数が使えます。

DOM

DOM 拡張モジュールで実装されておらず、振る舞いも持たず、 テストデータもないクラスが削除されました。 これらのクラスは、最新のDOM標準からも削除されています:

  • DOMNameList
  • DomImplementationList
  • DOMConfiguration
  • DomError
  • DomErrorHandler
  • DOMImplementationSource
  • DOMLocator
  • DOMUserDataHandler
  • DOMTypeInfo
  • DOMStringExtend

DOM 拡張モジュールで実装されておらず、 振る舞いも持たない以下のメソッドが削除されました:

  • DOMNamedNodeMap::setNamedItem()
  • DOMNamedNodeMap::removeNamedItem()
  • DOMNamedNodeMap::setNamedItemNS()
  • DOMNamedNodeMap::removeNamedItemNS()
  • DOMText::replaceWholeText()
  • DOMNode::compareDocumentPosition()
  • DOMNode::isEqualNode()
  • DOMNode::getFeature()
  • DOMNode::setUserData()
  • DOMNode::getUserData()
  • DOMDocument::renameNode()

Enchant

Exif

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

フィルタ

GD

  • 推奨されなくなっていた image2wbmp() 関数は削除されました。

  • 推奨されなくなっていた関数 png2wbmp()jpeg2wbmp() が削除されました。

  • imagecropauto() 関数の mode 引数のデフォルトは -1 ではなくなりました。 IMG_CROP_DEFAULT を代わりに使うべきです。

  • Windows では、php_gd2.dll の名前が、php_gd.dll に変更されました。

GMP

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

Iconv

エラーが発生した時に errno を適切に設定していなかった iconv の実装はもはやサポートされなくなりました。

IMAP

国際化関数

  • 推奨されなくなっていた定数 INTL_IDNA_VARIANT_2003 が削除されました。

  • 推奨されなくなっていた定数 Normalizer::NONE が削除されました。

LDAP

マルチバイト文字列

OCI8

  • OCI-Lob クラスは OCILob と呼ばれるようになり、 OCI-Collection クラスは OCICollection と呼ばれるようになりました。 これは、PHP 8 の arginfo 型アノテーションツールの 名前に関する規約で強制されたものです。

  • いくつかの関数エイリアスが推奨されなくなりました。

  • oci_internal_debug() 関数と そのエイリアス ociinternaldebug() は削除されました。

ODBC

  • odbc_connect() 関数は 接続を使い回さなくなりました。

  • odbc_exec() 関数で使われていなかった flags 引数が削除されました。

OpenSSL

  • openssl_seal()openssl_open() 関数には method を渡すことが必須になりました。 なぜなら、以前のデフォルト値 "RC4" はセキュアでないと見なされているからです。

正規表現 (Perl互換)

不正なエスケープシーケンスを渡した場合、 もはやリテラルと解釈されなくなりました。 リテラルと解釈する振る舞いは X 修正子で必須でしたが、 無視されるようになっています。

PHP Data Objects(PDO)

  • デフォルトのエラーハンドリングのモードが "silent" から "exceptions" に変更されました。 詳細は Errors とエラーハンドリング を参照ください。

  • いくつかの PDOクラスのメソッドシグネチャが変更されました:

    • PDO::query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs)
    • PDOStatement::setFetchMode(int $mode, mixed ...$args)

PDO ODBC

php.ini ディレクティブ pdo_odbc.db2_instance_name は削除されました。

PDO MySQL

PDO::inTransaction() は、 PDO が管理しているおおよその情報ではなく、 実際のトランザクションの状態を報告するようになりました。 クエリが "暗黙のコミット" に依存していた場合、 PDO::inTransaction() は後に false を返します。 なぜなら、トランザクションが既にアクティブではないからです。

PostgreSQL

  • pg_connect() 関数で 推奨されなくなっていた、 接続文字列の代わりに複数の引数を受け入れる文法は もはやサポートされなくなりました。

  • 推奨されなくなっていた pg_lo_import()pg_lo_export() 関数の、 最後の引数で接続を渡すシグネチャはサポートされなくなりました。 代わりに、接続は最初の引数で渡すべきです。

  • pg_fetch_all() 関数 は 結果セットの行が0行の場合に、false ではなく空の配列を返すようになりました。

Phar

phar に関連付けられたメタデータは、自動的にアンシリアライズされなくなりました。 これは、オブジェクトのインスタンス化やオートローディングなどに起因する、 潜在的なセキュリティ上の脆弱性を修正するためです。

リフレクション

  • メソッドシグネチャに関する変更

    • ReflectionClass::newInstance($args)
    • ReflectionFunction::invoke($args)
    • ReflectionMethod::invoke($object, $args)

    は、以下のように変更されました:

    • ReflectionClass::newInstance(...$args)
    • ReflectionFunction::invoke(...$args)
    • ReflectionMethod::invoke($object, ...$args)

    PHP 7 と PHP 8 の間で互換性を保たなければならないコードは、 両方のバージョンで互換性を取るために、以下のようなシグネチャが使えます:

    • ReflectionClass::newInstance($arg = null, ...$args)
    • ReflectionFunction::invoke($arg = null, ...$args)
    • ReflectionMethod::invoke($object, $arg = null, ...$args)

  • ReflectionType::__toString() メソッドは、 完全な型のデバッグ用の文字列表現を返すようになり、非推奨ではなくなりました。 特に、nullable な型に対しては、nullを代入できることを示す情報が含まれるようになります。 戻り値のフォーマットは安定しておらず、PHP のバージョンによって変わる可能性があります。

  • リフレクションの export() メソッドは削除されました。 代わりに、リフレクションオブジェクトを文字列にキャストすることができます。

  • ReflectionMethod::isConstructor()ReflectionMethod::isDestructor() メソッドは インターフェイスの __construct()__destruct() についても true を返すようになりました。 これより前のバージョンでは、クラスとトレイトのメソッドに対してだけ true を返していました。

  • ReflectionType::isBuiltin() メソッドは、 ReflectionNamedType クラスに移動しました。 ReflectionUnionType クラスには存在しなくなっています。

Sockets

  • 推奨されなくなっていた socket_addrinfo_lookup() 関数の flags に指定する AI_IDN_ALLOW_UNASSIGNED および AI_IDN_USE_STD3_ASCII_RULES は削除されました。

Standard PHP Library (SPL)

標準ライブラリ

  • assert() 関数は 文字列の引数を評価しなくなりました。 代わりに、他の引数と同じように扱われます。 assert('$a == $b') ではなく assert($a == $b) を使うべきです。 INI ディレクティブ assert.quiet_eval と 定数 ASSERT_QUIET_EVAL も削除されました。 なぜなら、もはや何の効果も無いからです。

  • parse_str() は、 結果の配列を指定しなければ使えなくなりました。

  • string.strip_tags フィルタは削除されました。

  • strpos(), strrpos(), stripos(), strripos(), strstr(), strchr(), strrchr(), stristr() 関数の needle 引数は常に文字列として解釈されるようになりました。 これより前のバージョンでは、 文字列でない引数は ASCII コードポイントと解釈されていました。 chr() 関数を明示的に呼ぶことで、 以前の振る舞いを復元できます。

  • strpos(), strrpos(), stripos(), strripos(), strstr(), stristr(), strrchr() 関数の needle 引数は、空に出来るようになりました。

  • substr(), substr_count(), substr_compare(), iconv_substr() 関数の length 引数は null も指定できるようになりました。 null を指定した場合、 length 引数が指定されなかったことと同じように振る舞います。 よって、この場合、空文字列ではなく、残りの文字列を返します。

  • array_splice() 関数の length 引数は null も指定できるようになりました。 null を指定した場合、引数を省略した場合と同じ振る舞いをします。 よって、offset から配列の最後までの全ての要素を削除する動きになります。

  • vsprintf(), vfprintf(), vprintf() 関数の args 引数は、配列であることが必須になりました。 これより前のバージョンでは、あらゆる型の値を受け入れていました。

  • password_hash() 関数の 'salt' オプションはサポートされなくなりました。 'salt' オプションを使うと警告が生成され、 指定された salt は無視されます。 そして生成された salt が代わりに使われます。

  • quotemeta() 関数は、空文字列を渡すと空文字列を返すようになりました。 これより前のバージョンでは、false を返していました。

  • 以下の関数は削除されました:

  • 定数 FILTER_SANITIZE_MAGIC_QUOTES は削除されました。

  • ($pieces, $glue) のような形で、implode() 関数を逆の引数の順番で呼ぶことはサポートされなくなりました。

  • parse_url() 関数は、 query および fragment が存在しないことと、空であることを区別するようになりました:

    • http://example.com/foo → query = null, fragment = null
    • http://example.com/foo? → query = "", fragment = null
    • http://example.com/foo# → query = null, fragment = ""
    • http://example.com/foo?# → query = "", fragment = ""
    これより前のバージョンでは、上の全ての場合で、query と fragment の値が null になっていました。

  • var_dump()debug_zval_dump() 関数は INIディレクティブ precision ではなく serialize_precision を使って浮動小数点を印字するようになりました。 デフォルトの設定では、浮動小数点はこのデバッグ関数によって 全精度で印字されるようになるということです。

  • __sleep() が返す 配列に存在しないプロパティが含まれていた場合、 静かに無視されるようになりました。 これより前のバージョンでは、 そうしたプロパティは値 null を持つかのようにシリアライズされていました。

  • デフォルトの起動時のロケールは、常に "C" になりました。 デフォルトでは、どのロケールからも環境を継承しません。 これより前のバージョンでは、 LC_ALL"C" に設定されていましたが、 LC_CTYPE は環境から情報を継承していました。 しかしながら、関数によっては、明示的に setlocale() を呼び出さないと 継承した環境を尊重しないものもあります。 ロケールをデフォルトから変えるためには、 setlocale() 関数を明示的に呼ぶことが常に必須になりました。

  • 非推奨になっていた crypt() 関数が DES にフォールバックする振る舞いが削除されました。 不明な salt フォーマットを crypt() 関数に渡すと、 弱い DES ハッシュにフォールバックするのではなく、 *0 という値になり、失敗するようになります。

  • crypt() 関数に、 SHA256/SHA512 の範囲外の round を指定すると、 最も近い境界値に切り詰めるのではなく、 *0 という値になり、失敗するようになりました。 これは、glibc の振る舞いと一致します。

  • 配列に比較結果が等しい要素が含まれている場合、ソートを行う関数の結果が変わる可能性があります。

  • コールバックを受け入れるあらゆる関数のうち、 リファレンスの引数を受け入れるように明示的に指定して「いない」 ものに対し、 リファレンスを使ったコールバックを渡すと警告が発生するようになりました。 例としては、 array_filter()array_reduce() 関数があります。 これらでほとんどの関数の例を示していますが、全てではありません。

  • file_get_contents() 関数のように、 関数が使うHTTPストリームラッパーは、 デフォルトで HTTP/1.0 ではなく、 HTTP/1.1 を指定するようになりました。 これによって、クライアントの振る舞いが変わるわけではありませんが、 サーバーが異なるレスポンスを返す可能性があります。 古い振る舞いをさせるには、 以下のようにして 'protocol_version' ストリームコンテキストオプションを設定して下さい。

    <?php
    $ctx
    = stream_context_create(['http' => ['protocol_version' => '1.0']]);
    echo
    file_get_contents('http://example.org', false, $ctx);
    ?>

  • 明示的に salt を指定せず crypt() 関数を呼ぶことはサポートされなくなりました。 自動的に生成される salt で強いハッシュを生成したい場合、 代わりに password_hash() を使って下さい。

  • substr(), mb_substr(), iconv_substr(), grapheme_substr() 関数は、 文字列の境界を越えたオフセットを一貫した形で処理するようになりました。 これより前のバージョンでは、空文字列ではなく false を返す場合がありました。

  • Windows では、 シェルを使い、プログラムを実行する関数 (proc_open(), exec(), popen() など) は、 一貫して %comspec% /s /c "$commandline" を使うようになりました。 これは、(追加のクォート無しで) $commandline を実行することと同じです。

Sysvsem

  • sem_get() 関数の auto_release 引数は、 int ではなく bool の値を受け入れるように変更されました。

Tidy

Tokenizer

  • T_COMMENT トークンは最後の改行文字を含まなくなりました。 改行文字はその後に続く T_WHITESPACE トークンに含まれるようになります。 注意すべきなのは、 T_COMMENT の後にホワイトスペースが続くとは限らないことです。 T_CLOSE_TAG やファイルの終端が続く場合もありえます。

  • 名前空間の中にある名前は、 T_NAME_QUALIFIED (Foo\Bar), T_NAME_FULLY_QUALIFIED (\Foo\Bar), T_NAME_RELATIVE (namespace\Foo\Bar) トークンを使って表現されるようになりました。 T_NS_SEPARATOR トークンは 単独の名前空間の区切り文字としてだけ使われます。 そして、グループ化されたuse宣言と一緒に使った場合にだけ文法的に合法です。

XMLReader

XMLReader::open(), XMLReader::xml() が staticメソッドになりました。 これらは、まだインスタンスメソッドとして呼び出すことが出来ますが、 クラスを継承し、これらのメソッドをオーバーライドする場合は、 それらは static として宣言する必要があります。

XML-RPC

XML-RPC 拡張モジュールは PECL に移動し、PHPの一部として配布されなくなりました。

Zip

ZipArchive::OPSYS_Z_CPM は削除されました(この名前は typo でした)。 ZipArchive::OPSYS_CPM を代わりに使って下さい。

Zlib

  • gzgetss() 関数が削除されました。

  • zlib.output_compression は、Content-Type: image/* を自動的に無効にすることはなくなりました。

Windows の PHP Test Packs

テストランナーの名前が、run-test.php から run-tests.php に変更されました。 これは、php-src の名前に合わせるためです。

add a note

User Contributed Notes 3 notes

up
5
aphpguy at galaxy dot za dot net
9 months ago
If you have older projects that break with PHP7 to 8 migration due to the loose comparison issue:

i.e. if ($a == 0) different behaviour between PHP 7 and PHP 8
(for case like $a = "" or $a = "123foo" and other cases listed at top)

replace in old code:

if ($a == 0) { .. }

with

if (cmp_eq($a, $b)) { .. }

Tested with a wide range of scenarios, even against arrays, booleans, file handles, pipe handles, objects, scalars and numbers.

So old code still behave like before.
Then both PHP8.x and older PHP up to ver 7.x will give the exact same boolean true or false output for loose comparisons.

function cmp_eq($a, $b) {
// If both $a and $b are of type strings, compare them as strings
if (is_string($a) && is_string($b)) { return $a == $b; } // may not be === because php says '42' equals '042' true yet '42' === '042' is false.

// If both $a and $b are numeric strings, compare them as numbers
if (is_numeric($a) && is_numeric($b)) { return $a == $b; }

// If $a is an empty string and $b is 0, or vice versa, return true
if (($a === '' && $b === 0) || ($a === 0 && $b === '')) { return true; }

// If $a is a non-numeric string and $b is 0, or vice versa, return true
if ((is_string($a) && ($a !== '') && ($b === 0)) || (($a === 0) && is_string($b) && ($b !== ''))) {
return true;
}
// special case '123abc' == 123 .. php 7 casts 123abc to 123, then 123 == 123 results in true. lets mimic that.
if ((is_string($a) && ($a !== '') && (is_numeric($b)) && ((bool)$b))) {
$number = filter_var($a, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); //"-234.56xyz"
return $number == $b;
}
if (is_numeric($a) && ((bool)$a) && is_string($b) && ($b !== '')) {
$number = filter_var($b, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); //"-234.56xyz"
return $a == $number;
}

// If $a is a number and $b is a non-numeric string, cast $a to string and compare
if (is_numeric($a) && is_string($b)) { return strval($a) == $b; }

// If $b is a number and $a is a non-numeric string, cast $b to string and compare
if (is_string($a) && is_numeric($b)) { return $a == strval($b); }

// If $a and $b are both non-numeric strings, compare them directly, we should return true if they are the same
return $a == $b;
} // end func cmp_eq

Note: the better way would be to port code to PHP 8, use strict variable typing and rather make use of the === and !== operators.

But in some cases having lots of old code to quickly patch, this might help.
up
20
retry, abort, fail
2 years ago
The change in string to int comparison mentioned above (e.g. '' == 0 now equates to false) has some other nasty consequences:

$a = '';

// php 8

if ( $a < 0 ) echo 'true'; // echos true
if ( $a < -1) echo 'true'; // echos true
if ( $a < -100 ) echo 'true'; // echos true

// php 7

if ( $a < 0 ) echo 'true'; // no output
if ( $a < -1) echo 'true'; // no output
if ( $a < -100 ) echo 'true'; // no output

So in a situation where you may have a web form input and expected an empty value to equate to 0, watch out not only for == 0, != 0, and <= 0 comparisons, but ALL < or <= comparisons to negative integers.
up
-5
1035041238 at qq dot com
1 year ago
In PHP 8, an empty string is less than any number, an English letter is always bigger than any number.

More interesting are the punctuation marks and non-word characters, some bigger than numbers, some smaller than numbers

$string = '`~!@#$%^&*()-_=+[]{};:\'"\\|,.<>/?';

$number = 999999999999;

$str_len = strlen($string);

$bigger = $smaller = $equal = [];
for ( $i = 0; $i < $str_len; ++$i ) {
if ( $string[$i] > $number ) {
$bigger[] = $string[$i];
} elseif ( $string[$i] < $number ) {
$smaller[] = $string[$i];
} else {
$equal[] = $string[$i];
}
}

var_dump( $bigger ); //['`', '~', '@', '^', '_', '=', '[', ']', '{', '}', ';', ':', '\', '|', '<', '>', '?']
var_dump( $smaller); //['!', '#', '$', '%', '&', '*', '(', ')', '-', '+', ''', '"', ',', '.', '/']
var_dump( $equal); //[]
To Top