その他の変更

PHP コア

FFI

opcache.preload_user が現在のシステムユーザーに設定されている場合、事前ロードの間に FFI::load をコールできるようになりました。これより前のバージョンでは、opcache.preload_user が設定されている場合は、事前ロードの間に FFI::load を呼び出せませんでした。

FPM

ソケットのパスの長さが OS がサポートする長さ以上の場合、FPM CLI のテストが失敗するようになりました。

Opcache

CLI SAPI と phpdbg SAPI では、opcache.preload_user ディレクティブを設定しなくても root として事前ロードを実行できるようになりました。これら以外の SAPI では、root として事前ロードを実行する際に このディレクティブの設定が未だ必須です。なぜなら、SAPI が特権を持っていないユーザに移行する前に事前ロードが実行されるからです。

Streams

ソケット接続をブロックする fread 関数は、バッファリングされているデータが存在した場合、追加のデータを待つのではなく、すぐに値を返すようになりました。

seek のオフセットが終端を超えた場合でも、memory ストリームは失敗しなくなりました。代わりに、次回書き込まれたときにメモリサイズが増加し、以前の終端からオフセットまでの間は、ファイルを扱っているときのようにゼロバイトで埋められます。

stat のファイルアクセス操作が、file_exists 類似の関数と同様に、ストリームのパスではなく、実在のパスを使うようになりました。これはストリームをオープンするときの振る舞いと一致します。

SAPI モジュールへの変更

CLI

STDOUT, STDERR, STDIN ストリームは、リソースが破棄される際にクローズされなくなりました。リソースが破棄されるタイミングでは CLI がほぼ終了しているからです。但し、fclose やその類似の関数を使い、これらのストリームを明示的に閉じることは未だできます。

変更された関数

PHP コア

gc_status に、以下の8つのフィールドが追加されました:

  • "running" => bool
  • "protected" => bool
  • "full" => bool
  • "buffer_size" => int
  • "application_time" => float: gc の合計の実行時間を秒単位で示します(collector_time を含みます)
  • "collector_time" => float: 循環参照を回収するのに使った時間を秒単位で示します(dessructor_time と free_time を含みます)
  • "destructor_time" => float: 循環参照を回収している間に、デストラクタを実行した時間を秒単位で示します
  • "free_time" => float: 循環参照を回収している間に、メモリを開放するのにかかった時間を秒単位で示します

class_alias は、内部クラスのエイリアスを作成する操作もサポートしました。

ini_set('open_basedir', ...); を実行して実行時に open_basedir を設定する場合、親ディレクトリ (..) を含んだパスを受け入れなくなりました。これより前のバージョンでは、.. で始まるパスだけを拒否していました。.. で始まるパスのみを拒否する制限は、パスの先頭に ./ を付加することで簡単に回避できてしまっていました。

ユーザー定義の例外ハンドラは、シャットダウン時の例外もキャッチするようになりました。

highlight_stringhighlight_file の結果として出力されるHTMLが、変更されました。外側のHTMLタグの間にあるホワイトスペースは削除されます。改行とスペースはHTMLエンティティに変換されなくなりました。HTML全体を <pre> タグで囲むようになっています。外側の <span> タグは <code> タグに統合されました。

Calendar

easter_date は、64ビットシステム上で 1970 から 2,000,000,000 までの年の値をサポートしました。これより前のバージョンでは、1970 から 2037 までの年の値だけをサポートしていました。

Curl

curl_getinfo は、新しく2つの定数をサポートしました: CURLINFO_CAPATHCURLINFO_CAINFO です。$option が null の場合、以下の追加のキーが含まれるようになります: "capath", "cainfo"

DOM

DOMCharacterData::appendData の仮の戻り値の型が、true に変更されました。

DOMDocument::loadHTML, DOMDocument::loadHTMLFile, DOMDocument::loadXML の仮の戻り値の型が、bool になりました。これより前のバージョンでは、DOMDocument|bool を返すとドキュメントに記されていましたが、PHP 8.0 以降は static にコールできなかったため、 DOMDocument が返せていませんでした。

Gd

imagerotate 関数のシグネチャが変更され、$ignore_transparent パラメータが削除されました。このパラメータは PHP 5.5.0 以降、無視されていたためです。

国際化関数

datefmt_set_timezone (とそのエイリアス IntlDateformatter::setTimeZone) は、成功時に true を返すようになりました。これより前のバージョンでは、null を返していました。

IntlBreakiterator::setText は、失敗時に false を返すようになりました。これより前のバージョンでは、null を返していました。 また、成功時に true を返すようになりました。これより前のバージョンでは、null を返していました。

IntlChar::enumCharNames は、戻り値として boolean を返すようになりました。これより前のバージョンでは、成功時に null、失敗時に false を返していました。

IntlDateFormatter::__construct は、無効なロケールを設定した場合に U_ILLEGAL_ARGUMENT_ERROR 例外をスローするようになりました。

マルチバイト文字列

mb_strtolowermb_convert_case は、ギリシャ文字シグマの条件付きケース変換規則を実装しました。mb_convert_case では、条件付きケース変換は MB_CASE_LOWERMB_CASE_TITLE モードにのみ適用され、MB_CASE_LOWER_SIMPLEMB_CASE_TITLE_SIMPLE には適用されません。

mb_decode_mimeheader は、RFC 2047 で要求された通りに、Qprint でエンコードされた単語のアンダースコアを解釈するようになりました。つまり、それらはスペースに変換されます。このようにMIMEエンコードされた単語では、アンダースコアは "=5F" としてエンコードしなければいけません。

mb_encode_mimeheader の実装が変更され、レアケースではあるものの、PHP 8.2 で生の ASCII として渡されていた入力文字列が、transfer_encoding の値でエンコードされる場合があります。

mb_encode_mimeheader は、入力文字列が Qprint エンコーディングの場合に、NUL(ゼロ)バイトを削除しなくなりました。これより前のバージョンでは、あるテキストエンコーディングの場合(特に UTF-16 と UTF-32) に NUL バイトが削除され、mb_encode_mimeheader の結果が壊れる場合がありました。

mb_detect_encoding の "$strict に true を指定しない" モードが、ドキュメントの通りに振る舞うようになりました。これより前のバージョンでは、入力文字列と同一のバイト(たとえば、最初のバイト) が、候補となるエンコーディングのリスト全てに対して不正と判定された場合でも、false を返していました。もっと一般化すると、不正なバイトが見つかった時点で、候補となるエンコーディングから削除してしまっていました。そして、入力と同一のバイトを削除した場合に、残りのエンコーディングをすべて候補として考慮すべき場合でも、false を返していました。一方で、候補となるエンコーディングが削除されて残りがひとつだけになった場合、文字列中で後にどれくらいエンコーディングのエラーが見つかるのかを考慮することなく、その残ったエンコーディングを返していました。この振る舞いは、ドキュメントの以下の説明と一致しません: "strict が false の場合、 もっとも近いと判定された文字エンコーディングが返されます"

mysqli

mysqli_fetch_object は、$constructor_args が空でないのに、クラスにコンストラクタが存在しない場合、 Exception ではなく ValueError を発生させるようになりました。

mysqli_poll は、$read$error が両方渡されない場合、ValueError を発生させるようになりました。

mysqli_field_seekmysqli_result::field_seek の戻り値の型が、bool ではなく true に変更されました。

ODBC

odbc_autocommitは、$enable パラメータで null を受け入れるようになりました。null を渡すと、パラメータをひとつ渡した場合と同じ振る舞いをします。つまり、自動コミットが有効かどうかだけを示す動きをします。

PostgreSQL

pg_fetch_object は、$constructor_args が空でないのに、クラスにコンストラクタが存在しない場合、Exception ではなく ValueError を発生させるようになりました。

pg_insert は、指定されたテーブルが無効な場合に、E_WARNING ではなく ValueError を発生させるようになりました。

pg_insertpg_convert は、フィールドの値/タイプ が PostgreSQL の型と一致しない場合に、E_WARNING ではなく ValueError または TypeError を発生させるようになりました。

pg_fetch_result, pg_field_prtlen, pg_field_is_null のパラメータ $row は、nullable になりました。

Random

mt_srandsrand は、ランダムなシードを使うかを決めるために、引数の数をチェックしなくなりました。null を渡すとランダムなシードを生成します。0 を渡すとゼロをシードとして使います。これらの関数の振る舞いは、 Random\Engine\Mt19937::__construct と一貫したものになりました。

リフレクション

ReflectionClass::getStaticProperties の戻り値の型は、nullable ではなくなりました。

標準ライブラリ

unserialize で発生していた E_NOTICE は、E_WARNING に変更されました。

処理されていない入力が残っている場合に、unserialize は新たに E_WARNING を発生させるようになりました。

array_pad は、配列が持つことができる最大の要素数だけを制限するようになりました。これより前のバージョンでは、一度に追加できる最大の要素数が 1048576 になっていました。

strtok は、トークンの分割を開始した際にトークンが与えられていない場合、E_WARNING を発生させるようになりました。

password_hash は、ソルトの生成が失敗した場合に、ValueError$previous (直前にスローされた例外) として、既に存在する Random\RandomException をチェインさせるようになりました。

proc_open$command に配列を指定する場合、空でない要素を少なくともひとつ含んでいることが必須になりました。そうでない場合、ValueError がスローされます。

proc_open$command に配列を指定する場合、かつそれが不正なコマンドの場合に、後に警告が発生するリソースではなく false を返すようになりました。Windows では既にそのように動作していましたが、posix_spawn を使っている場合(ほとんどのプラットフォーム Linux, BSD, MacOS) でも同様の振る舞いをするようになりました。ただ、posix_spawn をサポートしていないために、以前の振る舞いが変更されていない古いプラットフォームもまだ残っています。

array_sumarray_product は、配列に含まれる値が int/float に変換できない場合、警告を発生させるようになりました。これより前のバージョンでは、配列とオブジェクトは無視され、それら以外の値は int にキャストされていました。さらに、数値へのキャストを定義しているオブジェクト (例: GMP) は、無視されるのではなく、数値に変換されるようになっています。

number_format$decimals パラメータは、負の整数値の四捨五入も扱うようになりました。つまり、$decimals が負の値の場合、$num は 小数点以下の有効桁数 $decimals 桁に丸められます。これより前のバージョンでは、負の $decimals を指定しても、黙って無視され、数値は小数点以下が0桁になるように丸められていました。

strrchr$before_needle が追加されました。これは strstrstristr の対応するパラメータと、似た振る舞いをします。

str_getcsvfgetcsv は、最後のフィールドに終端されていないクォートだけが含まれている場合、null バイトの文字列をひとつ返すのではなく、空文字列を返すようになりました。

拡張モジュールへのその他の変更

PHP コア

bool 型の値に対して、加算子/減算子 (++/--) を使うと、警告が発生するようになりました。現状、この操作にはなんの意味もありませんが、将来的に $bool += 1 のような振る舞いをするようになるからです。

null 型の値に対して、減算子 (--) を使うと、警告が発生するようになりました。現状、この操作にはなんの意味もありませんが、将来的に $null -= 1 のような振る舞いをするようになるからです。

内部オブジェクトで、_IS_NUMBER キャストを実装しているものの、加算と減算の振る舞いを上書きする do_operator ハンドラを実装していない場合があります。この場合でも、$o += 1$o -= 1 をあたかも実行したかのように、値をインクリメント / デクリメントできるようになりました。

DOM

DOM の寿命を管理する仕組みが書き直され、暗黙のうちに削除されていたノードが取得できるようになりました。これより前のバージョンでは、削除されていたノードを取得しようとすると例外が発生していました。

SQLite3

SQLite3 クラスは、Exception ではなく、(Exception を継承した) SQLite3Exception をスローするようになりました。

SQLite のエラーコードは、エラーメッセージに含められるのではなく、例外に渡されるようになりました。

INI ファイルの扱いの変更

  • assert.* INI 設定は、推奨されなくなりました。 以下の INI 設定が該当します:

    これらの設定値がデフォルト値の場合、推奨されない警告は発生しません。zend.assertions を代わりに使うべきです。

  • zend.max_allowed_stack_size は、許可される最大のスタックサイズの値を設定するための、新しい INI ディレクティブです。設定できる値は、0 (プロセスやスレッドの、スタックサイズの最大値を用います)、-1 (無制限)、そしてバイト単位の正の数値です。デフォルトは 0 です。プロセスやスレッドのスタックサイズの最大値がわからない場合、既知のシステムのデフォルト値を使います。この値に大き過ぎる値を設定すると、スタックサイズの制限を無効にすることと同じ効果があります。ファイバーは 許可される最大のスタックサイズとして fiber.stack_size の値を使います。プロセスのコールスタックが `zend.max_allowed_stack_size - zend.reserved_stack_size` バイトを超えると、スタックオーバーフローが引き起こすセグメンテーションフォルトを防ぐため、Error がスローされます。これは、デバッグを容易にすることが目的です。スタックサイズは、制御されていない再帰処理によって増加します。増加する範囲は、内部関数やマジックメソッド __toString(), __clone(), __sleep(), __destruct() 内も含みます。この機能はスタックバッファのオーバーフローとは無関係ですし、セキュリティのための機能でもありません。

  • zend.reserved_stack_size は、予約済みのスタックサイズをバイト単位で設定するための、新しい INI ディレクティブです。許可されたスタックサイズの最大値から、この値がバッファとして引かれ、スタックサイズがチェックされます。

パフォーマンスに関わる変更

DOM

DOMNodeList をループさせた場合、キャッシュを使うようになりました。よって、ノードをリクエストする処理に、デフォルトで O(n^2) 時間かかることはなくなりました。

ノードからテキストを取得する際に、メモリを確保する処理を避けるようになりました。この結果、パフォーマンスが向上しています。

DOMChildNode::remove は、計算量が O(1) で実行されるようになりました。

標準ライブラリ

file 関数の flags パラメータのエラーチェックは、約7% 高速になりました。

Standard PHP Library(SPL)

RecursiveDirectoryIterator を使ってディレクトリをループする場合の、I/O が削減されました。