【PHP8.2】PHP8.2がリリースされたので新機能全部やる

[PHP8.3](https://qiita.com/rana_kualu/items/f03efb9810c3ac5b2ab6) / [PHP8.2](https://qiita.com/rana_kualu/items/64bd73ad3d314a1cccdc) / [PHP8.1](https://qiita.com/rana_kualu/items/e350b8ff80f929626eb9) / [PHP8.0](https://qiita.com/rana_kualu/items/c110cb244c3ee38c6859)


2022/12/08に[PHP8.2.0がリリースされました](https://www.php.net/releases/8.2/ja.php)。
大きな新機能については、PHP8.0以降公開されるようになった[ランディングページ](https://www.php.net/releases/8.2/)で見ることができます。

ここでは、概要だけではなく[UPGRADING](https://github.com/php/php-src/blob/php-8.2.0/UPGRADING)に載っている変更点を全部見て回ることにします。


# Backward Incompatible Changes

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

## Date

### DateTime::createFromImmutable() now has a tentative return type of static

[DateTime::createFromImmutable()](https://www.php.net/manual/ja/datetime.createfromimmutable.php)の返り値の型がstatic型になりました。
以前はDateTime型でした。

これは[phpstanのバグ](https://github.com/phpstan/phpstan/issues/7225)への対応です。

```php
class CustomDateTimeImmutable extends DateTimeImmutable{
	public function test(): self{
		return CustomDateTimeImmutable::createFromInterface(new DateTime());
	}
}

var_dump(get_class((new CustomDateTimeImmutable())->test()));       // CustomDateTimeImmutable
```

CustomDateTimeImmutable上で[createFromInterface](https://www.php.net/manual/ja/datetimeimmutable.createfrominterface.php)を呼ぶと、実際はCustomDateTimeImmutableが返ってくるのにphpstanはDateTimeImmutableが返ってくると思い込んでエラーを出すよ、というバグです。
ところが調べてみたところ[PHPのシグネチャ的にはDateTimeImmutableが返ってくる](https://github.com/phpstan/php-8-stubs/blob/dbe2f6ac25edf0f8a41c2fb9b5e60156aea9d680/stubs/ext/date/DateTimeImmutable.php#L141-L143)のが正しいとなっていたので、[PHP本体のバグ](https://github.com/php/php-src/issues/8544)だったとのことでした。

ややこしい。


### DateTimeImmutable::createFromMutable() now has a tentative return type of static

[DateTimeImmutable::createFromMutable()](https://www.php.net/manual/ja/datetimeimmutable.createfrommutable.php)の返り値の型がstatic型になりました。
以前はDateTimeImmutable型でした。

上記の`DateTime::createFromImmutable()`と全く同じ内容です。


## ODBC

### The ODBC extension now escapes the username and password

[ODBC関数](https://www.php.net/manual/ja/function.odbc-connect.php)は、接続文字列とユーザー名/パスワードの両方を渡した場合に、ユーザー名/パスワードをエスケープするようになりました。
とあるけどこんなところにユーザ入力を渡すなって話だな。
phpMyAdminみたいなのが困るってことなのかな。


## PDO_ODBC

### The PDO_ODBC extension also escapes the username and password when a connection string is passed

[PDOのODBC](https://www.php.net/manual/ja/ref.pdo-odbc.php)も同様にユーザー名/パスワードをエスケープするようになりました。

## Standard

### glob() returns empty array if all paths are restricted by open_basedir.

[glob()](https://www.php.net/manual/ja/function.glob.php)は、パスがopen_basedirによって制限されている場合に空の配列を返すようになりました。
以前はエラーになっていましたが、これは存在しないパスを指定した場合などと異なる挙動でした。

### strtolower() and strtoupper() are no longer locale-sensitive

[strtolower](https://www.php.net/manual/ja/function.strtolower.php)と[strtoupper](https://www.php.net/manual/ja/function.strtoupper)は[ロケールの影響を受けなくなりました](https://qiita.com/rana_kualu/items/fc4b02e2daaf102aa92f#locale-independent-case-conversion)。
具体的にはロケールが"C"であるものとして扱われます。

また、stristr・stripos・strripos・lcfirst・ucfirst・ucwords・str_ireplace・array_change_key_caseの各関数、そしてソート関数のSORT_FLAG_CASEフラグも同じ動作になります。

### str_split() returns an empty array for an empty string now

[str_split](https://www.php.net/manual/ja/function.str-split.php)は空文字に空の配列を返すようになりました。

```php
var_dump(str_split(''));

[] // PHP8.2
[0 => ''] // PHP8.1
```

[mb_str_split](https://www.php.net/manual/ja/function.mb-str-split.php)は以前から`[]`であり、動作が異なっていました。
そこで[動作を揃えた](https://3v4l.org/S4vhW)ということです。


### ksort() and krsort() do numeric string comparison under SORT_REGULAR

[ksort](https://www.php.net/manual/ja/function.ksort.php)と[krsort](https://www.php.net/manual/ja/function.krsort.php)をSORT_REGULARでソートした場合、数値型文字列をPHP8のルールでソートするようになりました。

```php
$a = ['a'=>1, 0=>1];
ksort($a);
var_dump($a);

[0=>1, 'a'=>1] // PHP8.2
['a'=>1, 0=>1] // PHP8.1
```

PHP8.0で[比較演算子の挙動が変わった](https://qiita.com/rana_kualu/items/82cc8295d2102d14b88a)のですが、SORT_REGULARは何気にその変更に従っていませんでした。
今後は同じ挙動になります。

## SPL

### The following methods now enforce their signature

以下のメソッドがシグネチャを強制するようになりました。

```php
SplFileInfo::_bad_state_ex()
SplFileObject::getCsvControl()
SplFileObject::fflush()
SplFileObject::ftell()
SplFileObject::fgetc()
SplFileObject::fpassthru()
```

って書いてあるんだけどどういう意味かわかりません。
[SplFileObject::getCsvControl()](https://www.php.net/manual/ja/splfileobject.getcsvcontrol.php)とか引数ないし。
これまで返り値が守られないことがあったとか？

### SplFileObject::hasChildren() now has a tentative return type of false

[SplFileObject::hasChildren()](https://www.php.net/manual/ja/splfileobject.haschildren.php)の返り値の型がfalse型になりました。
以前はbool型でした。

### SplFileObject::getChildren() now has a tentative return type of null

[SplFileObject::getChildren()](https://www.php.net/manual/ja/splfileobject.getchildren.php)の返り値の型がnull型になりました。
以前は?RecursiveIterator型でした。

### GlogIterator returns empty array if all paths are restricted by open_basedir

[GlogIterator](https://www.php.net/manual/ja/class.globiterator.php)は、パスがopen_basedirによって制限されている場合に空の配列を返すようになりました。
以前はエラーになっていましたが、これは存在しないパスを指定した場合などと異なる挙動でした。


# New Features

新機能。

## Core

### Added the #[\SensitiveParameter] attribute to redact sensitive data in backtraces.

[SensitiveParameter](https://qiita.com/rana_kualu/items/fc4b02e2daaf102aa92f#redacting-parameters-in-back-traces)です。

```php
function test(
    $foo = null,
    #[\SensitiveParameter]
    $bar = null,
) {
    throw new \Exception('Error');
}

test('foo', 'bar');


PHP Fatal error:  Uncaught Exception: Error in test.php
Stack trace: #0 test.php(11): test('foo', Object(SensitiveParameterValue))
```

アトリビュート`#[\SensitiveParameter]`を設定した引数は、スタックトレースに出力されなくなります。
データベースパスワードなど、うっかり表示されてしまうと困るパラメータに指定しましょう。

### It is now possible to use null and false as standalone types.

null型とfalse型が導入されました。

### Added support for true type.

true型が導入されました。

```php
function test(true $true, null $null) :false{
    var_dump($null, $true);
    return false;
}

test(true, null);
```

true型にはtrueしか渡すことができず、false型にはfalseしか渡すことができず、null型にはnullしか渡すことができません。

元々[PHP8.0でUNION型](https://qiita.com/rana_kualu/items/f75e254553157d8eb0b7)を導入する過程において、主に歴史的理由からfalse疑似型とnull疑似型が導入されました。
これはUNION型の一部としてのみ使用可能で、単独では使えない型です。

その後[交差型](https://qiita.com/rana_kualu/items/f49582bc94e0bae23d9c)の対応などもあり、単独でも使えたほうが便利だろうということでPHP8.2で[false型とnull型が正式に導入](https://qiita.com/rana_kualu/items/2ef15cfa2dea8ba0f061)されました。
そうなると[true型がないのは片手落ちだろう](https://qiita.com/rana_kualu/items/2e64ecb8740d21f4fd34)ということになってtrue型も対応となりました。

これらは引数としても使用可能ですが、ほとんど意味がありません。
返り値としての使用がメインとなるでしょう。


### Added support for readonly classes

[readonlyクラス](https://qiita.com/rana_kualu/items/0d8cfb5d0f83ae24f943)です。

```php
readonly class HOGE{
    public int $a;

    public function setA(int $a)
    {
        $this->a = $a;
    }
}

$hoge = new HOGE();
$hoge->x = 1; // PHP Fatal error: Cannot create dynamic property HOGE::$x

$hoge->setA(1); // 1回目はOK
$hoge->setA(2); // PHP Fatal error: Cannot modify readonly property HOGE::$a

class FUGA extends HOGE{} // PHP Fatal error:  Non-readonly class FUGA cannot extend readonly class HOGE
readonly class FUGA extends HOGE{} // こちらはOK
```

`readonly`を指定したクラスには、動的プロパティ使用できず、プロパティには型が必須であり、さらに一度設定した値は変更できません。

できることが極めて限定されるかわりに、できることを限定した厳密な運用が可能となります。


### Added support for Disjoint Normal Form (DNF) types

[型合成](https://qiita.com/rana_kualu/items/ddadd32756afdefbe99f)です。


```php
interface A{}
interface B{}
class C{}

function hoge((Traversable & Countable)|string $param): (A&B)|C{
    return new C();
}

hoge('a'); // OK
hoge(new ArrayIterator()); // OK
hoge(new EmptyIterator()); // Fatal error: hoge(): Argument #1 ($param) must be of type (Traversable&Countable)|string, EmptyIterator given
```

A型かつB型、もしくはC型、みたいな複雑な型合成ができるようになります。

型に無頓着なPHPという評価は過去の遺物であり、今のPHPは下手な言語よりよっぽどかちかちに型を固めることが可能になっています。


### Added error_log_mode ini setting that allows setting of permissions for error log file

ini設定[error_log_mode](https://www.php.net/manual/ja/errorfunc.configuration.php#ini.error-log-mode)が追加されました。

`error_log_mode=0o644`とするとエラーログファイルのパーミッションが0644になります。


### Added support for fetching properties of enums in constant expressions

constにENUMの`->`が使えるようになります。

```php
enum E: string {
    case Foo = 'foo';
}

const C = E::Foo->name;
var_dump(C); // Foo
```

元々`const I = DateTimeInterface::ATOM`とか書けていたので、それと同じような扱いになります。


### Added support for defining constants in traits

トレイトに定数が書けるようになります。

```php
trait T {
    public const CONSTANT = 42;
}
```

どうしてこれまで使えなかったかというと、[実装したとき特に考えてなかった](https://externals.io/message/118039#118059)からだそうです。


## Curl

### Added CURLINFO_EFFECTIVE_METHOD option and returning the effective HTTP method in curl_getinfo() return value.

定数CURLINFO_EFFECTIVE_METHODが追加されました。
これは[Curlの同名の定数](https://curl.se/libcurl/c/CURLINFO_EFFECTIVE_METHOD.html)のラッパーです。
[curl_getinfo()](https://www.php.net/manual/ja/function.curl-getinfo.php)のオプションとして使用可能で、有効なHTTPリクエストメソッドを取得できます。

```php
$ch = curl_init();
var_dump(curl_getinfo($ch, CURLINFO_EFFECTIVE_METHOD)); // GET

curl_setopt($ch, CURLOPT_POST, true);
var_dump(curl_getinfo($ch, CURLINFO_EFFECTIVE_METHOD)); // GET ←？

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HOGE');
var_dump(curl_getinfo($ch, CURLINFO_EFFECTIVE_METHOD)); // HOGE
```


### Exposed multiple new constants from libcurl 7.62 to 7.80

同梱の[libcurl](https://curl.se/libcurl/)がバージョン7.62から7.80になったことに伴い、その間に実装された[curl定数](https://curl.se/libcurl/c/symbols-in-versions.html)が追加されました。
全部で80個くらいあるので詳細は省略します。


### Added new function curl_upkeep() to perform any connection upkeep checks

関数[curl_upkeep()](https://www.php.net/manual/ja/function.curl_upkeep.php)が追加されました。

長時間接続し続けたいときに、ちょっとだけデータを送って接続をキープする関数です。

## DBA

### The LMDB Driver now accepts the DBA_LMDB_USE_SUB_DIR or DBA_LMDB_NO_SUB_DIR flags

[LMDBドライバ](https://www.php.net/manual/ja/dba.installation.php)が、DBA_LMDB_USE_SUB_DIRおよびDBA_LMDB_NO_SUB_DIRフラグを受け付けるようになりました。
ファイルを作るときにサブディレクトリを有効にするしないの設定みたいです。
そもそも[Lightning Memory-Mapped Database](https://www.symas.com/lmdb)なるものが初耳だったよ。

## OCI8

### Added an oci8.prefetch_lob_size directive and oci_set_prefetch_lob() function

ini設定`oci8.prefetch_lob_size`と、関数[oci_set_prefetch_lob](https://www.php.net/manual/ja/function.oci-set-prefetch-lob.php)が追加されました。
Oracle 12.2以降で使用可能で、LOBへのクエリのパフォーマンスを調整することができます。

ということらしいのですがどういう意味だかさっぱりだ。


## OpenSSL

### Added AEAD support for chacha20-poly1305 algorithm

認証付き暗号アルゴリズム、[chacha20-poly1305](https://en.wikipedia.org/wiki/ChaCha20-Poly1305)がサポートされました。

なるほど、[さっぱりわからん](https://jovi0608.hatenablog.com/entry/20160404/1459748671)。


## ODBC

### Added odbc_connection_string_is_quoted, odbc_connection_string_should_quote, and odbc_connection_string_quote.

関数[odbc_connection_string_is_quoted](https://www.php.net/manual/ja/function.odbc-connection-string-is-quoted.php)、[odbc_connection_string_should_quote](https://www.php.net/manual/ja/function.odbc-connection-string-should-quote.php)、[odbc_connection_string_quote](https://www.php.net/manual/ja/function.odbc-connection-string-quote.php)が追加されました。

これは主にODBCとPDO_ODBCが裏で使用するものですが、うまく使えばユニットテストに便利なので公開しているそうです。
でも具体的に何なのかは不明。

## PCRE

### Added support for the "n" (NO_AUTO_CAPTURE) modifier

PCRE正規表現の[パターン修正子](https://www.php.net/manual/ja/reference.pcre.pattern.modifiers.php)`n`が追加されました。
これは2017年に導入された[NO_AUTO_CAPTURE](https://github.com/PCRE2Project/pcre2/blob/pcre2-10.40/ChangeLog#L1345)に対応したものです。

```php
// フラグnが追加された
preg_match('/(foo)(bar)(?<capture1>baz)/n', 'foobarbaz', $matches);
var_dump($matches);

/*
	array(3) {
	  [0]=>
	  string(9) "foobarbaz"
	  ["capture1"]=>
	  string(3) "baz"
	  [1]=>
	  string(3) "baz"
	}
*/

// 既存の挙動
preg_match('/(foo)(bar)(?<capture1>baz)/n', 'foobarbaz', $matches);
var_dump($matches);

/*
	array(5) {
	  [0]=>
	  string(9) "foobarbaz"
	  [1]=>
	  string(3) "foo"
	  [2]=>
	  string(3) "bar"
	  ["capture1"]=>
	  string(3) "baz"
	  [3]=>
	  string(3) "baz"
	}
*/
```

修正子`n`を指定すると、名前付きキャプチャグループ`(?<xxxx>)`だけがキャプチャ対象になり、ただの`()`はキャプチャグループとみなされなくなります。
修正子`n`を指定しない場合は、これまでどおり通常の`()`と名前付き`(?<xxxx>)`両方ともがキャプチャされます。

括弧を検索したい場合などに便利なフラグだと思います。

## Random

### New extension that organizes and consolidates existing implementations related to random number generators

[乱数の大幅改善](https://qiita.com/rana_kualu/items/ab82a9c6dbd2e7c6edd0)です。

これまで様々な問題のあったPHPの乱数を一新し、安全性と使い勝手を劇的に改善します。

```php
// 乱数エンジン
$engine = new \Random\Engine\Secure();
// 乱数生成器
$randomizer = new \Random\Randomizer($engine);

var_dump(
    $randomizer->getInt(0, 100),              // random_int
    $randomizer->shuffleArray([1, 2, 3]),     // shuffle
    $randomizer->getBytes(10),                // random_bytes
    $randomizer->shuffleBytes('abcde'),       // str_shuffle
    $randomizer->pickArrayKeys([1, 2, 3], 1), // array_rand
);
```

また、状態をクラスに閉じ込めることでグローバルな乱数状態の影響を受けることがなくなります。
さらに乱数生成エンジンを差し替えることもできるようになります。


# Changes in SAPI modules

SAPIの変更はありません。


# Deprecated Functionality

非推奨になった機能。

## Core

### Creation of dynamic properties is deprecated

[動的プロパティは非推奨](https://qiita.com/rana_kualu/items/53012c8f84e0f92220c1)になりました。

```php
class HOGE{
    public function __construct()
    {
        $this->foo = 1;
    }
}

new HOGE(); // PHP Deprecated:  Creation of dynamic property HOGE::$foo is deprecated
```

プロパティは定義しなければなりません。
PHP8.2でDeprecated、PHP9でエラーになります。

正しい対応はクラスのプロパティに`private $foo = null`と書くことですが、可変変数を使っているなどで対応が困難な場合はアトリビュート`#[AllowDynamicProperties]`を使うことで当面エラーを回避することができます。


## Mbstring

### Use of QPrint, Base64, Uuencode, and HTML-ENTITIES 'text encodings' is deprecated

mbstringにおいて、QPrint・Base64・Uuencode・HTML-ENTITIESのテキストエンコーディングが非推奨になります。

```php
mb_convert_encoding('あいうえお', 'Base64', 'UTF-8'); // PHP Deprecated:  mb_convert_encoding(): Handling Base64 via mbstring is deprecated; use base64_encode/base64_decode instead
```

これらはUNICODEエンコーディングではなくバイト列のエンコーディングだからとかなんとからしいのですがよくわかりません。

## SPL

### The SplFileInfo::_bad_state_ex() internal method has been deprecated

内部関数`SplFileInfo::_bad_state_ex()`が非推奨になりました。
[表には出ていない関数](https://www.php.net/manual/ja/class.splfileinfo.php)なので、ユーザには特に影響ないでしょう。


## Standard

### utf8_encode() and utf8_decode() have been deprecated

[utf8_encode](https://www.php.net/manual/ja/function.utf8-encode)および[utf8_decode](https://www.php.net/manual/ja/function.utf8-decode)が非推奨になりました。
この関数は予想に反して「ISO-8859-1をUTF-8に変換する」という用途が狭すぎる関数です。
かわりに[mb_convert_encoding](https://www.php.net/manual/ja/function.mb-convert-encoding.php)を使いましょう。

# Changed Functions

動作に変更のある関数。

## Core

### str\*cmp, str\*pos, substr_compare functions, using binary safe string comparison now return -1, 0 and 1.

[strcmp](https://www.php.net/manual/ja/function.strcmp.php)や[strncmp](https://www.php.net/manual/ja/function.strncmp.php)などの文字列比較関数について、返り値が1か-1か0になりました。
これまではプラス値とマイナス値を返す場合、その値は1になるとは限りませんでした。

```php
var_dump(strcmp('a', 'z'));
// PHP8.1まで-25
// PHP8.2から-1
```

## DBA

### dba_open() and dba_popen() now have the following enforced function signature

[dba_open](https://www.php.net/manual/ja/function.dba-open)に引数`$flags`が追加されました。

```php
dba_open(
    string $path,
    string $mode,
    ?string $handler = null,
    int $permission = 0644,
    int $map_size = 0,
    ?int $flags = null
): resource|false
```

### dba_fetch()'s optional skip argument is now at the end in line

[dba_fetch](https://www.php.net/manual/ja/function.dba-fetch.php)の引数が変更になりました。

```php
// PHP8.2以降
dba_fetch(string|array $key, resource $dba, int $skip = 0): string|false

// PHP8.1まで
dba_fetch(string $key, int $skip, resource $dba): string
```

[順番入れ替える](https://www.php.net/manual/ja/function.implode.php)のやめようよ。

## Random

### random_bytes() and random_int() now throw \Random\RandomException on CSPRNG failure.

[random_bytes](https://www.php.net/manual/ja/function.random-bytes.php)および[random_int](https://www.php.net/manual/ja/function.random-int.php)が、乱数生成器が手に入らないときに`\Random\RandomException`をスローするようになりました。
これまではただの`\Exception`でした。

これを発生させるには[CNG API](https://learn.microsoft.com/ja-jp/windows/win32/seccng/cng-portal)とか[getrandom(2)](https://man7.org/linux/man-pages/man2/getrandom.2.html)とかを削除しないといけないという、むしろ発生させることが難しいエラーです。


## SPL

### The $iterator parameter of iterator_to_array() and iterator_count() is widened to iterable from Iterator

[iterator_to_array](https://www.php.net/manual/ja/function.iterator-to-array)および[iterator_count](https://www.php.net/manual/ja/function.iterator-count.php)において、引数`$iterator`の型が`Iterator`から`iterable`になりました。

```php
iterator_to_array([1, 2, 3]);
// PHP8.2以降 OK
// PHP8.1まで Fatal error: iterator_to_array(): Argument #1 ($iterator) must be of type Traversable, array given
```

要するに配列を渡してもエラーが起こらなくなりました。
これはそもそも[今まで渡せなかったのがおかしい](https://qiita.com/suin/items/5f76a3eeaca70a5c3ba8)ので、妥当な修正でしょう。

## XML

### xml_parser_set_option() now actually returns false when attempting to set a negative tag start

[xml_parser_set_option](https://www.php.net/manual/ja/function.xml-parser-set-option)の第二引数にXML_OPTION_SKIP_TAGSTART、第三引数に負の値を渡したときの返り値がfalseになりました。


```php
$parser = xml_parser_create();
var_dump(xml_parser_set_option( $parser, \XML_OPTION_SKIP_TAGSTART, -1));

// PHP8.2以降 false
// PHP8.1まで true
```

これまではtrueを返す、でも不正な値なので設定は失敗、という状態になっていました。

# New Functions

新しい関数。

## Curl

### curl_upkeep

関数[curl_upkeep()](https://www.php.net/manual/ja/function.curl_upkeep.php)が追加されました。
少しだけデータを送って、接続を維持する関数です。

### mysqli_execute_query

関数[mysqli_execute_query()](https://www.php.net/manual/ja/mysqli.execute-query.php)が追加されました。

[mysqli_prepare](https://www.php.net/manual/ja/mysqli.prepare.php)と[mysqli_stmt_bind_param](https://www.php.net/manual/ja/mysqli-stmt.bind-param.php)と[mysqli_stmt_execute](https://www.php.net/manual/ja/mysqli-stmt.execute.php)と[mysqli_stmt_get_result](https://www.php.net/manual/ja/mysqli-stmt.get-result.php)をひとまとめにした関数です。

```php
$mysqli = new mysqli();
$query = 'SELECT Name, Population, Continent FROM Country WHERE Continent=? ORDER BY Name LIMIT 1';

// PHP8.2以降
$result = $mysqli->execute_query($query, ['Europe']);

// PHP8.1まで
$stmt = $mysqli->prepare($query);
$stmt->bind_param('s', 'Europe');
$stmt->execute();
$result = $stmt->get_result();
```

ちょっとしたSQLが非常に書きやすくなりますね。

## OpenSSL

### openssl_cipher_key_length

関数[openssl_cipher_key_length()](https://www.php.net/manual/ja/function.openssl-cipher-key-length)が追加されました。
暗号鍵の長さを取得します。

```php
$methods = openssl_get_cipher_methods();
foreach($methods as $method){
    $length = openssl_cipher_key_length($method);
    printf('Method:%1$s  KeyLength:%2$d'."\n", $method, $length);
}

/*
	こんなかんじになる
	Method:aes-128-cbc  KeyLength:16
	Method:aes-128-cbc-cts  KeyLength:0
	Method:aes-128-cbc-hmac-sha1  KeyLength:16
	Method:aes-128-cbc-hmac-sha256  KeyLength:16
	Method:aes-128-ccm  KeyLength:16
	Method:aes-192-ccm  KeyLength:24
	Method:aes-256-ccm  KeyLength:32
*/
```

どんなときに使うのかはよくわかりません。

## Reflection

### ReflectionFunction::isAnonymous()

[ReflectionFunction::isAnonymous](https://www.php.net/manual/ja/reflectionfunction.isanonymous.php)が追加されました。
無名関数かどうかを調べることができます。

```php
$f1 = function(){};
function f2(){};

var_dump(
    (new ReflectionFunction($f1))->isAnonymous(), // true
    (new ReflectionFunction('f2'))->isAnonymous(), // false
);
```

### ReflectionMethod::hasPrototype()

[ReflectionMethod::hasPrototype](https://www.php.net/manual/ja/reflectionmethod.hasprototype.php)が追加されました。
メソッドがプロトタイプを持つか調べることができます。


```php
class C1{
    function M2(){}
}
class C2 extends C1{
    function M2(){}
}
class C3 extends C1{
    function M3(){}
}

var_dump(
    (new ReflectionMethod('C2', 'M2'))->hasPrototype(), // true
    (new ReflectionMethod('C3', 'M3'))->hasPrototype(), // false
);
```

プロトタイプって何かというと要するに`parent::`のことです。
何故か[スコープ定義演算子](https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php)とか[継承](https://www.php.net/manual/ja/language.oop5.inheritance.php)のあたりではプロトタイプって言葉を使ってないのでわかりにくいですね。

## Sodium

### sodium_crypto_stream_xchacha20_xor_ic()

[sodium_crypto_stream_xchacha20_xor_ic](https://www.php.net/manual/ja/function.sodium-crypto-stream-xchacha20-xor-ic)が追加されました。

```php
var_dump(sodium_crypto_stream_xchacha20_xor_ic('a', random_bytes(24), 1, 'key'));

// PHP Fatal error:  Uncaught SodiumException: sodium_crypto_stream_xchacha20_xor_ic(): Argument #3 ($counter) must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long
```

第三引数`$counter`をSODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES桁のintで渡せと言われたんだけど、SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES=32なので32桁のintが必要となってこれ無理なんだけどどういうことだろう。


## Standard

### The peak memory usage can now be reset to the current usage thanks to memory_reset_peak_usage()

[memory_reset_peak_usage](https://www.php.net/manual/ja/function.memory-reset-peak-usage)が追加されました。

[memory_get_peak_usage](https://www.php.net/manual/ja/function.memory-get-peak-usage.php)の値をリセットします。

```php

/* なんかメモリ使う処理1 */
var_dump(memory_get_peak_usage()); // 最大メモリ使用量を確認

memory_reset_peak_usage(); // リセット

/* なんかメモリ使う処理2 */
var_dump(memory_get_peak_usage()); // 最大メモリ使用量を確認
```

メモリ使用量を調査するベンチマークなどは、これまで処理ごとに別ファイルにしなければならなかったのですが、この関数が入ったことにより1ファイルで書けるようになりました。


### ini_parse_quantity

[ini_parse_quantity](https://www.php.net/manual/ja/function.ini-parse-quantity.php)が追加されました。

```php
var_dump(ini_parse_quantity('1G')); // 1073741824
```

ini設定から、`post_max_size = 8M`みたいに書かれた値をパースします。

はい、それだけです。

対応している値はK、M、Gだけであり、TとかQとかは未対応です。
[ini_get](https://www.php.net/manual/ja/function.ini-get)の例にある関数return_bytesと実質同じだと思われます。

## XML

### libxml_get_external_entity_loader

[libxml_get_external_entity_loader](https://www.php.net/manual/ja/function.libxml-get-external-entity-loader.php)が追加されました。

[libxml_set_external_entity_loader](https://www.php.net/manual/ja/function.libxml-set-external-entity-loader.php)で登録したエンティティローダーを取得します。

```php
$loader = function(string $public_id, string $system_id, array $context):string{
    return $public_id.$system_id;
};
libxml_set_external_entity_loader($loader);

$loader2 = libxml_get_external_entity_loader();
var_dump($loader2('a', 'b', [])); // ab
```

どういうときに使うのかは、よくわかりません。


# New Classes and Interfaces

新しいクラスやインターフェイスはありません。


# Removed Extensions and SAPIs

削除されたエクステンションはありません。

# Other Changes to Extensions

## Date

### DatePeriod properties are now properly declared

[DatePeriod](https://www.php.net/manual/ja/dateperiod.construct)のプロパティが適切に宣言されました。

とだけしか書かれてないので何のことだかよくわかりません。


## Intl

### instances are no longer serializable

以下のIntl関連クラスがシリアライズできなくなりました。
これまではserialize自体は通せていましたが、元に戻せないものができていたりと不具合がありました。

・IntlBreakIterator
・IntlRuleBasedBreakIterator
・IntlCodePointBreakIterator
・IntlPartsIterator
・IntlCalendar
・Collator
・IntlIterator
・UConverter
・IntlDateFormatter
・IntlDatePatternGenerator
・MessageFormatter
・ResourceBundle
・Spoofchecker
・IntlTimeZone
・Transliterator


## mysqli

### The support for libmysql has been removed

MySQLドライバ[libmysqlが削除されました](https://qiita.com/rana_kualu/items/fc4b02e2daaf102aa92f#remove-support-for-libmysql-from-mysqli)。
あらゆる面で[mysqlndのほうが有利](https://www.php.net/manual/ja/mysqlinfo.library.choosing.php)なので、今後はそちらに統一されます。

### The reconnect property of mysqli_driver has been removed

[mysqli_driver::reconnect](https://www.php.net/manual/ja/class.mysqli-driver.php)が削除されました。
同時にini設定[mysqli.reconnect](https://www.php.net/manual/ja/mysqli.configuration.php#ini.mysqli.reconnect)も削除されました。

これらは元々libmysqlにしか存在しない機能で、mysqlndはこれを無視していました。

### The constant MYSQLI_IS_MARIADB has been deprecated

定数[MYSQLI_IS_MARIADB](https://www.php.net/manual/ja/mysqli.constants.php)が削除されました。

mysqliモジュールがMariaDBで構築されているかを示すフラグらしいのですが、これは[libmysqlにしか存在しない](https://github.com/php/php-src/pull/8919)らしいので一緒に削除されました。

## OCI8

### The minimum Oracle Client library version required is now 11.2

Oracleクライアントの最低バージョンが11.2になりました。
11.2は2009年9月リリースリリースらしいので、これより昔のバージョンとか流石に切っていいでしょう。

## PCRE

### NUL characters (\0) in pattern strings are now supported

[preg_match](https://www.php.net/manual/ja/function.preg-match)等に検索文字列としてヌルバイトが使えるようになりました。

```php
preg_match("/a".chr(0)."bc/", 'abc');

// PHP8.2以降 エラー出ない
// PHP8.1まで Warning: preg_match(): Null byte in regex
```

そんなもの検索してどうするんだって気がしないでもない。

## Session
### Trying to change the SameSite cookie now fail and emit a warning

セッションが既に有効になったあと、もしくは既にレスポンスが出力された後で、[samesite要素](https://www.php.net/manual/ja/function.session-set-cookie-params.php)を変更しようとすると失敗します。
これは他のセッション要素と同じ動作です。

## SQLite3

### sqlite3.defensive is now PHP_INI_USER

ini設定[sqlite3.defensive](https://www.php.net/manual/ja/sqlite3.configuration.php)がPHP_INI_USERになりました。
これまではPHP_INI_SYSTEMでした。

## Standard

### getimagesize() now reports the actual image dimensions, bits and channels of AVIF images.

[getimagesize](https://www.php.net/manual/ja/function.getimagesize)が、AVIFフォーマットの画像について正しいサイズやビット数を返すようになりました。
これまではサイズは0×0となり、ビット数などは返ってきませんでした。

```php
var_dump(getimagesize('test.avif')); // 適当なavif画像

// PHP8.2以降
array(7) {
  [0]=>
  int(960)
  [1]=>
  int(540)
  [2]=>
  int(19)
  [3]=>
  string(24) "width="960" height="540""
  ["bits"]=>
  int(8)
  ["channels"]=>
  int(3)
  ["mime"]=>
  string(10) "image/avif"
}

// PHP8.1まで
array(5) {
  [0]=>
  int(0)
  [1]=>
  int(0)
  [2]=>
  int(19)
  [3]=>
  string(20) "width="0" height="0""
  ["mime"]=>
  string(10) "image/avif"
}
```

## Tidy

### tidy properties are now properly declared

[tidy](https://www.php.net/manual/ja/class.tidy.php)のプロパティが適切に宣言されました。
[tidyNode](https://www.php.net/manual/ja/class.tidynode.php)のプロパティが適切にreadonlyで宣言されました。

とだけしか書かれてないので何のことだかよくわかりません。

## Zip

### extension updated to 1.20.0

[Zipエクステンション](https://pecl.php.net/package/zip)のバージョンが1.20.0になりました。

バージョンアップに伴い、[ZipArchive::clearError](https://www.php.net/manual/ja/ziparchive.clearerror.php)、[ZipArchive::getStreamName](https://www.php.net/manual/ja/ziparchive.getstreamname.php)、[ZipArchive::getStreamIndex](https://www.php.net/manual/ja/ziparchive.getstreamindex.php)が追加されました。


# New Global Constants

追加されたグローバル定数。
基本的には、PHPが使用しているドライバで追加された定数をそのままインポートしているだけのようです。

## COM_DOTNET

DISP_E_PARAMNOTFOUND
LOCALE_NEUTRAL

## Curl

CURLALTSVC_H1 (libcurl >= 7.64.1)
CURLALTSVC_H2 (libcurl >= 7.64.1)
CURLALTSVC_H3 (libcurl >= 7.64.1)
CURLALTSVC_READONLYFILE (libcurl >= 7.64.1)
CURLAUTH_AWS_SIGV4 (libcurl >= 7.75.0)
CURLE_PROXY (libcurl >= 7.73.0)
CURLFTPMETHOD_DEFAULT
CURLHSTS_ENABLE (libcurl >= 7.74.0)
CURLHSTS_READONLYFILE (libcurl >= 7.74.0)
CURLINFO_PROXY_ERROR (libcurl >= 7.73.0)
CURLINFO_REFERER (libcurl >= 7.76.0)
CURLINFO_RETRY_AFTER (libcurl >= 7.66.0)
CURLMOPT_MAX_CONCURRENT_STREAMS (libcurl >= 7.67.0)
CURLOPT_ALTSVC_CTRL (libcurl >= 7.64.1)
CURLOPT_ALTSVC (libcurl >= 7.64.1)
CURLOPT_AWS_SIGV4 (libcurl >= 7.75.0)
CURLOPT_CAINFO_BLOB (libcurl >= 7.77.0)
CURLOPT_DOH_SSL_VERIFYHOST (libcurl >= 7.76.0)
CURLOPT_DOH_SSL_VERIFYPEER (libcurl >= 7.76.0)
CURLOPT_DOH_SSL_VERIFYSTATUS (libcurl >= 7.76.0)
CURLOPT_HSTS_CTRL (libcurl >= 7.74.0)
CURLOPT_HSTS (libcurl >= 7.74.0)
CURLOPT_MAIL_RCPT_ALLLOWFAILS (libcurl >= 7.69.0)
CURLOPT_MAXAGE_CONN (libcurl >= 7.65.0)
CURLOPT_MAXFILESIZE_LARGE
CURLOPT_MAXLIFETIME_CONN (libcurl >= 7.80.0)
CURLOPT_PROXY_CAINFO_BLOB (libcurl >= 7.77.0)
CURLOPT_SASL_AUTHZID (libcurl >= 7.66.0)
CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 (libcurl >= 7.80.0)
CURLOPT_SSL_EC_CURVES (libcurl >= 7.73.0)
CURLOPT_UPKEEP_INTERVAL_MS (libcurl >= 7.62.0)
CURLOPT_UPLOAD_BUFFERSIZE (libcurl >= 7.62.0)
CURLOPT_XFERINFOFUNCTION (libcurl >= 7.32.0)
CURLPROTO_MQTT (libcurl >= 7.71.0)
CURLPX_BAD_ADDRESS_TYPE (libcurl >= 7.73.0)
CURLPX_BAD_VERSION (libcurl >= 7.73.0)
CURLPX_CLOSED (libcurl >= 7.73.0)
CURLPX_GSSAPI (libcurl >= 7.73.0)
CURLPX_GSSAPI_PERMSG (libcurl >= 7.73.0)
CURLPX_GSSAPI_PROTECTION (libcurl >= 7.73.0)
CURLPX_IDENTD_DIFFER (libcurl >= 7.73.0)
CURLPX_IDENTD (libcurl >= 7.73.0)
CURLPX_LONG_HOSTNAME (libcurl >= 7.73.0)
CURLPX_LONG_PASSWD (libcurl >= 7.73.0)
CURLPX_LONG_USER (libcurl >= 7.73.0)
CURLPX_NO_AUTH (libcurl >= 7.73.0)
CURLPX_OK (libcurl >= 7.73.0)
CURLPX_RECV_ADDRESS (libcurl >= 7.73.0)
CURLPX_RECV_AUTH (libcurl >= 7.73.0)
CURLPX_RECV_CONNECT (libcurl >= 7.73.0)
CURLPX_RECV_REQACK (libcurl >= 7.73.0)
CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED (libcurl >= 7.73.0)
CURLPX_REPLY_COMMAND_NOT_SUPPORTED (libcurl >= 7.73.0)
CURLPX_REPLY_CONNECTION_REFUSED (libcurl >= 7.73.0)
CURLPX_REPLY_GENERAL_SERVER_FAILURE (libcurl >= 7.73.0)
CURLPX_REPLY_HOST_UNREACHABLE (libcurl >= 7.73.0)
CURLPX_REPLY_NETWORK_UNREACHABLE (libcurl >= 7.73.0)
CURLPX_REPLY_NOT_ALLOWED (libcurl >= 7.73.0)
CURLPX_REPLY_TTL_EXPIRED (libcurl >= 7.73.0)
CURLPX_REPLY_UNASSIGNED (libcurl >= 7.73.0)
CURLPX_REQUEST_FAILED (libcurl >= 7.73.0)
CURLPX_RESOLVE_HOST (libcurl >= 7.73.0)
CURLPX_SEND_AUTH (libcurl >= 7.73.0)
CURLPX_SEND_CONNECT (libcurl >= 7.73.0)
CURLPX_SEND_REQUEST (libcurl >= 7.73.0)
CURLPX_UNKNOWN_FAIL (libcurl >= 7.73.0)
CURLPX_UNKNOWN_MODE (libcurl >= 7.73.0)
CURLPX_USER_REJECTED (libcurl >= 7.73.0)
CURLSSLOPT_AUTO_CLIENT_CERT (libcurl >= 7.77.0)
CURLSSLOPT_NATIVE_CA (libcurl >= 7.71.0)
CURLSSLOPT_NO_PARTIALCHAIN (libcurl >= 7.68.0)
CURLSSLOPT_REVOKE_BEST_EFFORT (libcurl >= 7.70.0)
CURL_VERSION_GSASL (libcurl >= 7.76.0)
CURL_VERSION_HSTS (libcurl >= 7.74.0)
CURL_VERSION_HTTP3 (libcurl >= 7.66.0)
CURL_VERSION_UNICODE (libcurl >= 7.72.0)
CURL_VERSION_ZSTD (libcurl >= 7.72.0)

## Filter

FILTER_FLAG_GLOBAL_RANGE

## Sockets

SO_INCOMING_CPU
SO_MEMINFO
SO_RTABLE (OpenBSD)
TCP_KEEPALIVE (MacOS)
TCP_KEEPCNT (Linux, others)
TCP_KEEPIDLE (Linux, others)
TCP_KEEPINTVL (Linux, others)
TCP_NOTSENT_LOWAT
LOCAL_CREDS_PERSISTENT (FreeBSD)
SCM_CREDS2 (FreeBSD)
LOCAL_CREDS (NetBSD)
SO_BPF_EXTENSIONS
SO_SETFIB
TCP_CONGESTION (Linux, FreeBSD)
SO_ZEROCOPY (Linux)
MSG_ZEROCOPY (Linux)

# Changes to INI File Handling

iniファイルの取り扱いに関する変更点。

## Support for binary and octal number

iniファイルで2進数`0b`と8進数`0o`の接頭辞がサポートされました。
これまでは16進数`0x`と、`0777`のように`0o`ではない形の8進数だけがサポートされていました。

## Parsing of some ill-formatted values will now trigger a warning

正しくないフォーマットの値が、警告を発生させるようになりました。
これまではエラーが出ませんでした。
値の解釈そのものは、互換性のためそのまま維持されます。

以下の項目で発生する可能性があります。

bcmath.scale
com.code_page
default_socket_timeout
fiber.stack_size
hard_timeout
intl.error_level
ldap.max_links
max_input_nesting_level
max_input_vars
mbstring.regex_retry_limit
mbstring.regex_stack_limit
mysqli.allow_local_infile
mysqli.allow_persistent
mysqli.default_port
mysqli.max_links
mysqli.max_persistent
mysqli.reconnect
mysqli.rollback_on_cached_plink
mysqlnd.log_mask
mysqlnd.mempool_default_size
mysqlnd.net_read_buffer_size
mysqlnd.net_read_timeout
oci8.default_prefetch
oci8.max_persistent
oci8.persistent_timeout
oci8.ping_interval
oci8.prefetch_lob_size
oci8.privileged_connect
oci8.statement_cache_size
odbc.allow_persistent
odbc.check_persistent
odbc.defaultbinmode
odbc.default_cursortype
odbc.defaultlrl
odbc.max_links
odbc.max_persistent
opcache.consistency_checks
opcache.file_update_protection
opcache.force_restart_timeout
opcache.interned_strings_buffer
opcache.jit_bisect_limit
opcache.jit_blacklist_root_trace
opcache.jit_blacklist_side_trace
opcache.jit_debug
opcache.jit_hot_func
opcache.jit_hot_loop
opcache.jit_hot_return
opcache.jit_hot_side_exit
opcache.jit_max_exit_counters
opcache.jit_max_loop_unrolls
opcache.jit_max_polymorphic_calls
opcache.jit_max_recursive_calls
opcache.jit_max_recursive_returns
opcache.jit_max_root_traces
opcache.jit_max_side_traces
opcache.log_verbosity_level
opcache.max_file_size
opcache.opt_debug_level
opcache.optimization_level
opcache.revalidate_freq
output_buffering
pcre.backtrack_limit
pcre.recursion_limit
pgsql.max_links
pgsql.max_persistent
post_max_size
realpath_cache_size
realpath_cache_ttl
session.cache_expire
session.cookie_lifetime
session.gc_divisor
session.gc_maxlifetime
session.gc_probability
soap.wsdl_cache_limit
soap.wsdl_cache_ttl
unserialize_max_depth
upload_max_filesize
user_ini.cache_ttl
xmlrpc_error_number
zend.assertions
zlib.output_compression_level

# Windows Support

Windowsのサポート。

## Core

### Windows specific error messages are no longer localized

Windows固有のエラーはローカライズされなくなり、常に英語で表示されるようになりました。

といっても元々ほぼ英語だったのであんまり関係なさそう。

### Preliminary and highly experimental support for building on ARM64 has been added

ARM64の予備実験的サポートが追加されました。

preliminaryかつexperimentalなので、まだ実用はできないでしょう。

## OCI8

### Since building against Oracle Client 10g is no longer supported

Oracle 10gはサポートされなくなり、設定オプション`--with-oci8`は削除されました。
設定オプション`--with-oci8-11g`・`--with-oci8-12c`・`--with-oci8-19`はまだサポートされています。

なんで19cじゃないんだろう？


## Zip

### The Zip extension upgraded to version 1.21.0

[Zipエクステンション](https://pecl.php.net/package/zip)のバージョンが1.20.0になりました。

### The Zip extension is now built as shared library (DLL) by default

ZipエクステンションのDLLがデフォルトで同梱されるようになりました。

# Other Changes

その他の変更点。

## CLI

### The STDOUT, STDERR and STDIN are no longer closed on resource destruction which is mostly when the CLI finishes

リソースを破棄してもSTDOUT、STDERR、STDINが閉じなくなりました。
とあるのですが、具体的にどのようなときに起こるのかよくわかりませんでした。

なお[fclose](https://www.php.net/manual/ja/function.fclose.php)等で明白に破棄すると、PHP8.2でも消えます。

```php
var_dump(STDIN); // resource(1) of type (stream)
fclose(STDIN);
var_dump(STDIN); // resource(1) of type (Unknown)
```

## Core

### iterable type is now a built-in compile time alias for array|Traversable

`iterable`型は`array|Traversable`型のエイリアスであるため、エラーメッセージなどに`iterable`ではなく`array|Traversable`を使うようになりました。

```
$f = function(iterable $p){};
$f(1);

// PHP8.2以降 Fatal error: {closure}(): Argument #1 ($p) must be of type Traversable|array, int given
// PHP8.1まで Fatal error: {closure}(): Argument #1 ($p) must be of type iterable, int given
```


# 感想

細かな使い勝手の向上などがメインであり、大きな新機能といえるのは[乱数改善](https://qiita.com/rana_kualu/items/ab82a9c6dbd2e7c6edd0)くらいでしょうか。
それ以外には動的プロパティの廃止など、PHP9に向けての前準備といったものが見当たります。
[PHP8.0](https://qiita.com/rana_kualu/items/fe7998fbe773544d5d25)のJITや、[PHP8.1](https://qiita.com/rana_kualu/items/a6601b49e0591eb42200)の型要素の大幅拡充などに比べると、目玉機能というようなものがなく、その点では少々物足りないところがありますね。

なんといっても最大の変更点は、動的プロパティの非推奨です。
これによって、どこから生えたかわからない謎プロパティみたいなのを撃滅することが可能になり、PHPのつらいところをひとつ減らすことができます。

ただ古くから継ぎ足して使われているようなソースでは、ほぼ間違いなく動的プロパティが使われていると思うので、この変更によって動かなくなってしまう可能性があります。
PHP8.2ではまだ警告が出るだけですが、PHP9で動かなくなることが決定しているので、この機会に見直してみるとよいでしょう。

またPHP8.0/8.1で大幅拡充された型システムについては、falseやtrue型などの追加でさらに隙が埋められました。
これで残る不足はいよいよ型エイリアスくらいでしょうか。


ところで[公式のマイグレーションガイド](https://www.php.net/manual/en/migration82.php)、いつも日本語版が出るのは遅めなのですが、[今回はリリース前から早々に日本語訳されて](https://www.php.net/manual/ja/migration82.php)いました。
誰の仕業かと思えばやはり[この人](https://github.com/mumumu)です。
いやーすごいね。