## 下位互換性のない変更点

### PHP コア

#### $GLOBALS へのアクセス制限

$GLOBALS 配列へのアクセスに対し、 多くの制限が適用されるようになりました。 `$GLOBALS['var']` のような、 個別の配列要素に対する読み取りや書き込みは、 これまで通り動作します。 $GLOBALS 配列全体への読み取り専用のアクセスも、 引き続きサポートされます。 しかし、$GLOBALS 配列全体への書き込みは、 もはやサポートされなくなりました。 たとえば、`array_pop($GLOBALS)` のようなコードは、エラーが発生します。

#### 継承したメソッド内で static 変数を使う

static 変数を使っているメソッドが継承された (但し、オーバーライドはされていない) 場合、継承されたメソッドは static 変数を親クラスのメソッドと共有するようになりました。

    <?php
    class A {
        public static function counter() {
            static $counter = 0;
            $counter++;
            return $counter;
        }
    }
    class B extends A {}
    var_dump(A::counter()); // int(1)
    var_dump(A::counter()); // int(2)
    var_dump(B::counter()); // int(3), 以前のバージョンでは int(1)
    var_dump(B::counter()); // int(4), 以前のバージョンでは int(2)
    ?>

これは、メソッド中の static 変数が、 static プロパティと同じ振る舞いをするようになったということです。

#### 必須のパラメータの前に、デフォルト値を持つパラメータを指定した場合

必須のパラメータの前に、 デフォルト値を持つパラメータ を指定した場合、 デフォルト値を持つパラメータは常に必須のパラメータとして扱われるようになりました。 これは、 名前付き引数 を使って関数を呼び出した場合でも同様です。 PHP 8.0.0 以降、かつ 8.1.0 より前のバージョンでは、 以下のコードは推奨されない警告を発生させていましたが、 呼び出しは成功していました。 PHP 8.1.0 以降では、 ArgumentCountError がスローされるようになっています。 位置を指定して呼び出した場合でも、同じエラーがスローされます。

    <?php
    function makeyogurt($container = "bowl", $flavour)
    {
        return "Making a $container of $flavour yogurt.\n";
    }
    try
    {
        echo makeyogurt(flavour: "raspberry");
    }
    catch (Error $e)
    {
        echo get_class($e), ' - ', $e->getMessage(), "\n";
    }
    ?>

上の例の PHP 8.0 での出力は、このようになります。:

    Deprecated: Required parameter container
     in example.php on line 3
    Making a bowl of raspberry yogurt.

上の例の PHP 8.1 での出力は、このようになります。:

    Deprecated: Optional parameter $container declared before required parameter
     $flavour is implicitly treated as a required parameter in example.php on line 3
    ArgumentCountError - makeyogurt(): Argument #1 ($container) not passed

但し、必須のパラメータの前であっても、 Null を許容する型 を指定するために、 パラメータにデフォルト値 `null` を指定できることに注意して下さい。 その場合でも、そのパラメータは必須であることには変わりありません。

#### 内部クラスと戻り値の型の互換性

ほとんどの final でない内部メソッドは、 それをオーバライドする際、 互換性がある戻り値の型を宣言することが必須になりました。 そうしない場合、継承が有効かを検証する際に、 推奨されない警告が発生するようになります。 PHP のバージョン間の互換性を保ちたいがために、 戻り値の型を宣言できない場合、 アトリビュート ReturnTypeWillChange を追加することで警告を抑止できます。

#### 新しいキーワード

`readonly` は予約されたキーワードになりました。しかし、まだ関数名としては使えます。

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

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

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

- FileInfo 関数は、 `fileinfo` resource ではなく、 finfo オブジェクトを受け取り、 返すようになりました。

- FTP 関数は、 `ftp` resource ではなく、 FTP\Connection オブジェクトを受け取り、 返すようになりました。

- IMAP 関数は、 `imap` resource ではなく、 IMAP\Connection オブジェクトを受け取り、 返すようになりました。

- LDAP 関数は、 `ldap link` resource ではなく、 LDAP\Connection オブジェクトを受け取り、 返すようになりました。

- LDAP 関数は、 `ldap result` resource ではなく、 LDAP\Result オブジェクトを受け取り、 返すようになりました。

- LDAP 関数は、 `ldap result entry` resource ではなく、 LDAP\ResultEntry オブジェクトを受け取り、 返すようになりました。

- PgSQL 関数は、 `pgsql link` resource ではなく、 PgSql\Connection オブジェクトを受け取り、 返すようになりました。

- PgSQL 関数は、 `pgsql result` resource ではなく、 PgSql\Result オブジェクトを受け取り、 返すようになりました。

- PgSQL 関数は、 `pgsql large object` resource ではなく、 PgSql\Lob オブジェクトを受け取り、 返すようになりました。

- PSpell 関数は、 `pspell` resource ではなく、 PSpell\Dictionary オブジェクトを受け取り、 返すようになりました。

- PSpell 関数は、 `pspell config` resource ではなく、 PSpell\Config オブジェクトを受け取り、 返すようになりました。

### MySQLi

mysqli_fetch_fields() と mysqli_fetch_field_direct() は、 max_length を指定すると 常に `0` を返すようになりました。 この情報は、結果セットを繰り返し処理する際に計算できますし、 長さの最大値をとります。 PHP でも以前のバージョンから、 内部的にこれと同じ処理が行われてきました。

`MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH` オプションは、もはや意味をなさなくなりました。

`MYSQLI_STORE_RESULT_COPY_DATA` オプションは、もはや意味をなさなくなりました。 mysqli::store_result() の `mode` 引数にどんな値を渡しても、 同様に意味がなくなっています。

mysqli::connect() は、 成功時に `null` ではなく、`true` を返すようになりました。

デフォルトのエラー処理モードが、 "silent" から "exceptions" に変更されました。 この変更の意味の詳細と、 どのようにこの属性を明示的に設定するかについては、 MySQLi のエラー報告モード のページを参照ください。 以前のバージョンの振る舞いは、 `mysqli_report(MYSQLI_REPORT_OFF);` を使うことで再現できます。

mysqli_stmt::execute() を継承したクラスは、 追加でオプションの引数を指定しなければいけません。

### MySQLnd

INI ディレクティブ mysqlnd.fetch_data_copy が削除されました。 このディレクティブによって、 ユーザーの目に見える形で、振る舞いが変更されるべきではないからです。

### OpenSSL

EC キーの秘密鍵は、他のキーと同じように PKCS#8 フォーマットでエクスポートされるようになりました。

openssl_pkcs7_encrypt() と openssl_cms_encrypt() は、 デフォルト値が RC2-40 から AES-128-CBC に変更されました。 RC2-40 暗号はセキュアではないと見なされており、 OpenSSL 3 からはデフォルトで有効ではなくなっています。

### PHP Data Objects(PDO)

`PDO::ATTR_STRINGIFY_FETCHES` は、bool 型の値を `"0"` や `"1"` に変換するようになりました。 以前のバージョンでは、bool は文字列に変換されていませんでした。

PDOStatement::bindColumn() に `PDO::PARAM_LOB` を指定してコールした場合、 かつ、`PDO::ATTR_STRINGIFY_FETCHES` が有効になっていない場合は、 常にストリームに結果をバインドするようになりました。 以前のバージョンでは、 使っているデータベースドライバやバインドが行われるタイミングによって、 文字列にバインドされることもあれば、 ストリームにバインドされることもありました。

#### MySQL ドライバ

結果セットの整数と浮動小数点の値は、 エミュレートされたプリペアドステートメントを使った場合、 文字列ではなく、 ネイティブの PHP の型を使って返されるようになりました。 これは、ネイティブのプリペアドステートメントの振る舞いと一致します。 以前のバージョンの振る舞いは、 `PDO::ATTR_STRINGIFY_FETCHES` オプションを有効にすることで再現できます。

#### SQLite ドライバ

結果セットの整数と浮動小数点の値は、 ネイティブの PHP の型を使って返されるようになりました。 以前のバージョンの振る舞いは、 `PDO::ATTR_STRINGIFY_FETCHES` オプションを有効にすることで再現できます。

### Phar

ArrayAccess インターフェイスに準拠するため、 Phar::offsetUnset() と、 PharData::offsetUnset() は bool を返さなくなりました。

### 標準ライブラリ

version_compare() は、 ドキュメントに書かれていない演算子の省略形を受け入れなくなりました。

htmlspecialchars(), htmlentities(), htmlspecialchars_decode(), html_entity_decode(), get_html_translation_table() のデフォルト値が、 `ENT_COMPAT` から `ENT_QUOTES | ENT_SUBSTITUTE` に変更されました。 これは、`'` が `&#039;` にエスケープされるようになるということです。 以前のバージョンでは、`'` に対してはエスケープは行われていませんでした。 さらに、不正な UTF-8 文字列は、空文字列を返すのではなく、 Unicode 置換文字に置きかえられるようになっています。

debug_zval_dump() は、 値の前に `&` を付加する代わりに、 自身のリファレンスカウントだけでなく、 リファレンスラッパーのリファレンスカウントも表示するようになりました。 これは、PHP 7.0 以降のリファレンスの表現をより正確にモデル化したものです。

debug_zval_dump() は、 インターン化された文字列や、 変更できない配列については、 ダミーのリファレンスカウントではなく、 `interned` と表示するようになりました。

### Standard PHP Library (SPL)

SplFixedArray は、 array のように JSON にエンコードできるようになりました。

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open