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

[PHP8.4](https://qiita.com/rana_kualu/items/9c4d476a4c044bac265f) / [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)


2023/11/23に[PHP8.3.0がリリースされました](https://www.php.net/)。
大きな新機能については、PHP8.0以降公開されるようになった[ランディングページ](https://www.php.net/releases/8.3/)で見ることができます。
[マイグレーションガイド](https://www.php.net/manual/ja/migration83.php)も既に日本語化されています。

また、採用されたRFCについては[PHP8.3の新機能](https://qiita.com/rana_kualu/items/91111443105a75a55e2f)でまとめています。

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


# Backward Incompatible Changes

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

## Core

### Programs that were very close to overflowing the call stack may now throw an Error

コールスタックの容量設定`zend.max_allowed_stack_size`・`zend.reserved_stack_size`が導入されました。
無限ループとかでスタック使用量が`zend.max_allowed_stack_size - zend.reserved_stack_size`バイトを超えるとエラーになります。

```php
PHP Fatal error:  Maximum call stack size of 500 bytes reached during compilation. Try splitting expression in test.php on line 1
```

どうして単純なサイズ指定ではなくreserved_stack_sizeみたいなのがあるのかはよくわからない。
そういえばGC発生確率も`session.gc_probability / session.gc_divisor`ですね。

ちなみにini_setでは効かなかったので、[PHP_INI_SYSTEM](https://www.php.net/manual/ja/configuration.changes.php)だと思います。


### Executing proc_get_status() multiple times will now always return the right value on posix systems

POSIXで、[proc_get_status](https://www.php.net/manual/ja/function.proc-get-status)を複数回呼んだときに正しい値を返すようになりました。
またproc_get_statusしたあとに[proc_close](https://www.php.net/manual/ja/function.proc-close.php)するとこれまで常に-1を返してきていたのが、正しい値を返すようになりました。

とあるのですがWindowsは以前から正しい値だったので変化がわかりませんでした。


### Zend Max Execution Timers is now enabled by default for ZTS builds on Linux

実行時間タイマーがLinux上のZTSビルドでも常に有効になりました。

むしろいままで有効じゃなかったってこと？


### Uses of traits with static properties will now redeclare static properties inherited from the parent class

トレイトを使うと、親クラスから継承されるstaticプロパティが再定義されるようになりました。

これ[マイグレーションガイド](https://www.php.net/manual/ja/migration83.incompatible.php)が[原語からして](https://www.php.net/manual/en/migration83.incompatible.php)何言ってるのかわからないのですが、[この問題への対応](https://github.com/php/php-src/issues/10935)です。

```php
class A {
    static $test;
}
class B extends A {
    static $test;
}

A::$test = 'A';
B::$test = 'B';
var_dump(A::$test, B::$test);
  // 常にA, B


trait Foo {
    static $test;
}
trait Bar {
    static $test;
}
class A {
    use Foo;
}
class B extends A {
    use Bar;
}

A::$test = 'A';
B::$test = 'B';
var_dump(A::$test, B::$test);
  // PHP8.3  A, B
  // PHP8.2  B, B
```

static変数`$test`を直接クラスに記載するのとtrait経由で使う場合で、これまでは挙動が異なっていました。
今後はクラス内に書いたのと同じような動作になります。


### Assigning a negative index n to an empty array will now make sure that the next index is n+1 instead of 0.

空の配列にマイナス値のキーを与えた場合、次のキーはこれまで0になっていたのが、普通にn+1になりました。

```php
$a = [];
$a[-10] = 'x';
$a[] = 'y';

var_dump($a);
  // PHP8.3 [-10=>x, -9=>y]
  // PHP8.2 [-10=>x, 0=>y]
```


### Class constant visibility variance is now correctly checked when inherited from interfaces

クラス定数の可視性が、インターフェイスを継承した際に正しくチェックされるようになりました。

とあるんだけど、そもそもインターフェイスには`public const`しか書けないので、いったいなにを正しくチェックするのかよくわかりませんでした。


### WeakMaps entries whose key maps to itself may now be removed

[WeakMap](https://www.php.net/manual/ja/class.weakmap.php)のエントリは、foreachなどのイテレーション以外で到達不能な場合に循環参照コレクタによって削除されるようになりました。

こういうのどうやって検証したらいいんだ。

## DOM

### DOMChildNode::after() on a node that has no parent is now a no-op instead of a hierarchy exception

[DOMChildNode::after](https://www.php.net/manual/ja/domchildnode.after.php)、[DOMChildNode::before](https://www.php.net/manual/ja/domchildnode.before.php)、[DOMChildNode::replaceWith](https://www.php.net/manual/ja/domchildnode.replacewith.php)は、親ノードが存在しない場合に例外を出していたのが何もしなくなりました。
これはDOMの仕様だそうです。

```php
$element = new DOMElement('div');
$ret = $element->before('hoge');
  // PHP8.3 エラー起きない
  // PHP8.2 PHP Fatal error:Uncaught DOMException: Hierarchy Request Error
```

### Using the DOMParentNode and DOMChildNode methods without a document now works

同様に、ノードがない状態で[DOMParentNode](https://www.php.net/manual/ja/class.domparentnode.php)・[DOMChildNode](https://www.php.net/manual/ja/class.domchildnode.php)を使ってもエラーが出なくなりました。


### createAttributeNS() without specifying a prefix would incorrectly create a default namespace

プレフィックスを指定せずに[DOMDocument::createAttributeNS](https://www.php.net/manual/ja/domdocument.createattributens.php)を呼ぶとデフォルトのネームスペースが生成されていました。
これはバグであるため修正されました。

### createAttributeNS() would previously incorrectly throw a NAMESPACE_ERR when the prefix was already used for a different uri

以前のバージョンでは、DOMDocument::createAttributeNS() は、prefix が既に異なる URI で使われている場合に DOM_NAMESPACE_ERRNAMESPACE_ERR DOMException をスローしていました。 このバージョンからは、prefix の名前が衝突していた場合でも、異なる prefix を正しく選べるようになりました。

と、[マイグレーションガイド](https://www.php.net/manual/ja/migration83.incompatible.php)には書いてあるのですが、何を言ってるのかさっぱりわからない。
再現する例を作ることすらできなかったよ。

正直、[DOM](https://www.php.net/manual/ja/book.dom.php)は[使い方が異常にややこしく](https://www.php.net/manual/ja/class.domdocument.php#82447)て何が正しくて何が間違っているのかさっぱりわかりません。
[SimpleXML](https://www.php.net/manual/ja/book.simplexml.php)のほうが遥かに使い勝手がいいですし、なんならテンプレートエンジンとかpreg_replaceとかのほうがもっと手っ取り早いですからね。


### New methods and properties were added to some DOM classes

いくつかのメソッドとプロパティが追加されました。
詳細は新機能のところで。

## FFI

### C functions that have a return type of void now return null

返り値がvoidの関数は、これまで`FFI\CData:void`を返していましたが、nullを返すようになりました。

## Opcache

### The opcache.consistency_checks INI directive was removed

ini設定[opcache.consistency_checks](https://www.php.net/manual/ja/opcache.configuration.php#ini.opcache.consistency-checks)が削除されました。

キャッシュのチェックを行うタイミングを調整する機能らしいですが、JITなどとの組み合わせによって正しく動かないことがあったそうです。
修正案は複雑すぎたため、単純に機能自体が削除されました。

## Phar

### The type of Phar class constants are now declared

[Pharクラス定数](https://www.php.net/manual/ja/phar.constants.php)に型宣言がつきました。

```php
$c = new ReflectionClassConstant('Phar', 'NONE');
var_dump($c->getType()->getName());  // int
```

クラス定数に型宣言できるのが[そもそもPHP8.3から](https://qiita.com/rana_kualu/items/f04f687b3f4e7d11e6b6)です。


## Standard

### The range() function has had various changes

[range](https://www.php.net/manual/ja/function.range.php)関数に[様々な変更](https://wiki.php.net/rfc/proper-range-semantics)がありました。
基本的に、おかしな値を渡したらこれまで関数側が適当にどうにかしていたのをやめてエラーを出すようになります。
数値をプラスの`$step`で使っている場合は動きは同じですが、変な使い方をしている場合は要注意です。


```php
var_dump(range(1, 3, -1));
  // PHP8.3 ValueError
  // PHP8.2 [1, 2, 3]

var_dump(range([1], [3]));
  // PHP8.3 TypeError
  // PHP8.2 [1]

var_dump(range(NAN, 3, 1));
  // PHP8.3 ValueError
  // PHP8.2 [NAN]

var_dump(range(1, 3, 1.0));
  // PHP8.3 [int1, int2, int3]
  // PHP8.2 [float1, float2, float3]

var_dump(range('', 2));
  // PHP8.3 [0, 1, 2] E_WARNING 
  // PHP8.2 [0, 1, 2]

var_dump(range('1x', 2, 1));
  // PHP8.3 [1, 2] E_WARNING
  // PHP8.2 [1, 2]

var_dump(range('9', 'A'));
  // PHP8.3 [9, :, ;, <, =, >, ?, @, A] 文字コード順
  // PHP8.2 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] Aは0にキャスト
```

### The file() flags error check now catches all invalid flags

関数[file](https://www.php.net/manual/ja/function.file)の第二引数`$flag`が対応している値を厳密にチェックするようになります。

```php
file('php://input', \FILE_APPEND);
  // PHP8.3  ValueError
  // PHP8.2  エラー出ない
```

読み込み専用関数にFILE_APPENDなど当然意味がありませんが、これまでは特にエラーも出ずに受け入れられていました。


## SNMP

### The type of SNMP class constants are now declared

[SNMPクラス定数](https://www.php.net/manual/ja/class.snmp.php)に型宣言が付きました。


# New Features

新機能

## Core

### Anonymous classes may now be marked as readonly

無名クラスにreadonlyをつけられるようになりました。

```php
$c = new readonly class {
  public function test($text){
      echo $text;
  }
};

```

そこまでして無名クラスを使う意味とは？


### Readonly properties can now be reinitialized during cloning.

マジックメソッド`__clone()`内において、[readonlyプロパティを一回だけ変更できる](https://qiita.com/rana_kualu/items/91111443105a75a55e2f#readonly-amendments)ようになります。

```php
class Foo {
  // コンストラクタ
  public function __construct(
      public readonly DateTime $bar
  ) {}

  // clone
  public function __clone()
  {
      $this->bar = new DateTime(); // OK
  }
}

$foo = new Foo(new DateTime());
$bar = clone $foo;
```

[コメント](https://qiita.com/rana_kualu/items/91111443105a75a55e2f#comment-6792099ba3f8dba3aace)によると、別に[Clone with](https://wiki.php.net/rfc/clone_with)のRFCがあって、これを実現するために必要な機能なので切り出されて先に入ったということのようです。


### Class, interface, trait, and enum constants now support type declarations

[クラス定数に型宣言できる](https://qiita.com/rana_kualu/items/f04f687b3f4e7d11e6b6)ようになりました。

```php
interface I{
  const string TEST = 'test';
}
trait T{
  const int TEST = 1;
}
class C{
  const I|null TEST = null;
}
```

べんりですね。

プリミティブでない型も宣言するだけなら可能なのですが、PHPでは`const object TEST = new stdClass()`とか書けないので実質意味がありません。


### Closures created from magic methods can now accept named arguments

マジックメソッドで作ったクロージャは、名前付き引数を受け入れるようになりました。

と[マイグレーションガイド](https://www.php.net/manual/ja/migration83.new-features.php)にはあるのですが、意味がよく分かりませんでした。

```php
$c = function($param1=null, $param2=null){
  var_dump($param1, $param2);
};
$c(param2:2, param1:1);

class C{
  public function __invoke($param1=null, $param2=null){
    var_dump($param1, $param2);
  }
}
$c = new C();
$c(param2:2, param1:1); // 1, 2
```

こういうことかと思ったんだけど両方ともPHP8.2以前でも動きます。
そもそも`created from magic methods`ってなんだよ具体的にメソッド名書けよ。


### The final modifier may now be used when using a method from a trait

トレイトからメソッドを使う場合に、そのメソッドに対して final を指定できるようになりました。

と[マイグレーションガイド](https://www.php.net/manual/ja/migration83.new-features.php)にはあるのですが、意味がよく分かりませんでした。

```php
trait T{
  function foo(){
    $this->bar();
  }
  final function bar(){
    var_dump(__CLASS__);
  }
}

class C{
  use T;
}

(new C)->foo(); // "C"
```

`foo()`をfinalにするしない、`bar()`をfinalにするしない、`bar()`をclassに定義するtraitに定義する、8パターン全て試しましたが全てPHP8.2でも動作しました。
いったいどんな書き方がこれまでできなかったんだ？


### Added the #[\Override] attribute to check that a method exists

[Overrideアトリビュート](https://qiita.com/rana_kualu/items/938e696c9ce86f5d5428)です。

```php
class C1{
  public function foo(){}
}

class C2 extends C1{
  #[\Override]
  public function foo(){}
}

class C3 extends C1{
  #[\Override]
  public function bar(){}
}
  // PHP8.3 Fatal error
  // PHP8.2 エラー出ない
```

Overrideを指定したメソッドが実はOverrideしていない場合、エラーになります。
これによって、親クラスをうっかり書き替えたといった場合の検出がより容易になります。

ちなみにこれは構文解析時にチェックされるため、C3をインスタンス化しなくてもエラーが発生します。


### Class constants can now be accessed dynamically using the C::{$name} syntax

[クラス定数を文字列から探せる](https://qiita.com/rana_kualu/items/d0dc17543f4b89db30cb)機能です。

```php
enum Suit:string{
  case Hearts = 'ハート';
  case Diamonds = 'ダイヤ';
  case Clubs = 'クラブ';
  case Spades = 'スペード';
}

echo Suit::{'Hearts'}->value;
  // PHP8.3 ハート
  // PHP8.2 syntax error
```

ENUMの使い勝手がぐっと上がりますね。


### Static variable initializers can now contain arbitrary expressions

[static変数に動的値を突っ込める](https://qiita.com/rana_kualu/items/20b80ded17c77e2b05d4)ようになります。

```php
function foo(int $start=0){
  static $i = $start;
  echo $i++;
}

foo(5);  // 5
foo();   // 6
foo(10); // 7
```

[static変数](https://www.php.net/manual/ja/language.variables.scope.php#language.variables.scope.static)は最初の一回だけ実行され、関数を抜けても値を保持するという便利なやつです。
しかしこの初期値として、どういうわけか今までは直接的な数値や定数しか使えませんでした。

PHP8.3以降は任意の式や変数値が使えるようになります。

## CLI

### It is now possible to lint multiple files

`php -l`で複数のファイルを文法チェックできるようになりました。

```php
php -l a.php b.php
```

これまでは、2番目以降に指定したファイルは単に無視されていました。


## DOM

### Added properties DOMElement::className and DOMElement::id
### Added properties DOMNode::isConnected and DOMNameSpaceNode::isConnected
### Added properties DOMNode::parentElement and DOMNameSpaceNode::parentElement

DOMに各プロパティが追加されました。

なお、libxml2の仕様上バイナリセーフではないそうで、ヌルバイトを突っ込むとそこで切れます。

## FFI

### It is now possible to assign CData to other CData

[FFI\CData](https://www.php.net/manual/ja/class.ffi-cdata.php)を別の`FFI\CData`に代入できるようになりました。


## Opcache

### opcache_get_status()['scripts'][n]['revalidate'] now contains a Unix timestamp

[opcache_get_status()](https://www.php.net/manual/ja/function.opcache-get-status.php)の返り値に、次にキャッシュの更新確認を行う時刻が追加されました。


## Posix

### posix_getrlimit() now takes an optional $res parameter to allow fetching a single resource limit

[posix_getrlimit()](https://www.php.net/manual/ja/function.posix-getrlimit.php)に第一引数`$res`が追加されました。
`posix_getrlimit('core')`で`core`だけ取得することができるようです。

### posix_isatty() now raises type warnings for integers following the usual ZPP semantics

[posix_isatty()](https://www.php.net/manual/ja/function.posix-isatty.php)の第一引数`$file_descriptor`の型は`resource|int`ですが、`int`型を渡すと警告を出すようになりました。

そもそも`int を指定した場合は、システムコールにそのまま渡せるファイル記述子だとみなします`という処理自体が謎すぎたので、今後はきちんと`resource`を渡す必要があります。


### posix_ttyname() now raises type warnings for integers following the usual ZPP semantics and value warnings for invalid file descriptor integers.

同じく[posix_ttyname()](https://www.php.net/manual/ja/function.posix-ttyname.php)も第一引数`$file_descriptor`にint型を渡すと警告を出すようになりました。

なおPOSIX関数は[Windowsでは使えない](https://www.php.net/manual/ja/intro.posix.php)のでよくわかりません。


## Streams

### Streams can now emit the STREAM_NOTIFY_COMPLETED notification

ストリームが[STREAM_NOTIFY_COMPLETED](https://www.php.net/manual/ja/stream.constants.php)を発するようになりました。

実は[これまで実装されていませんでした](https://github.com/php/php-src/issues/8641)。


# Changes in SAPI modules

SAPIの変更。

## $_SERVER['SERVER_SOFTWARE'] value from the built-in CLI server changed to make it compliant with RFC3875

[ビルトインWebサーバ](https://www.php.net/manual/ja/features.commandline.webserver.php)で実行した際の`$_SERVER['SERVER_SOFTWARE']`の値がRFC3875に従うようになりました。

```bash
php -S localhost:8000

var_dump($_SERVER['SERVER_SOFTWARE']);;
  // PHP8.3
    string(33) "PHP/8.3.0RC6 (Development Server)"
  // PHP8.2
    string(28) "PHP 8.2.6 Development Server"
```

設定による可能性もありますが、Apache経由では"Apache"とだけ出てきてPHPバージョンなどは一切出力されませんでした。
CLIでの実行ではそもそも未定義でした。
あまりに共通項がないので、これに依存するコードなんて普通は書かないでしょうから、実際何らかの問題になるかというと絶対ならないと思います。


# Deprecated Functionality

非推奨になる機能。
PHP8.3でE_Deprecatedとなり、PHP9.0で削除されます。
この機能・書き方は今後使ってはいけません。

## Core

### Using the ++ operator on empty, non-numeric, or non-alphanumeric strings is now deprecated
### Using the -- operator on empty or non-numeric strings is now deprecated

[インクリメント・デクリメント演算子の改善](https://qiita.com/rana_kualu/items/91111443105a75a55e2f#path-to-saner-incrementdecrement-operators)です。

数値以外に対する`++`・`--`の挙動が変更になります。

```php
$s = 'foo';
$s++; // 'fop'

$s = '<foo';
$s++; // '<fop'
  // PHP Deprecated:  Increment on non-alphanumeric string is deprecated 

$s = true;
$s++; // true
  // PHP Warning:  Increment on type bool has no effect

$s = null;
$s++; // 1

$s = '';
$s++; // '1'
  // PHP Deprecated:  Increment on non-alphanumeric string is deprecated 

$s = '1.5';
$s++; // float(2.5)
```

Deprecatedが今後使えなくなる記法です。
''には今後使えなくなるのにnullは0扱いのままだとか、いっぽうtrueやfalseには前から使えなかったりと、型によって挙動がバラバラです。
そもそも数値以外へのインクリメント・デクリメントは避けた方がいいでしょう。

### Calling get_class() and get_parent_class() without arguments is now deprecated

[get_class()](https://www.php.net/manual/ja/function.get-class.php)および[get_parent_class()](https://www.php.net/manual/ja/function.get-parent-class.php)の引数なしでの呼び出しが非推奨になりました。

```php
new class {
    public function __construct(){
        var_dump(get_class());
    }
}; // PHP Deprecated:  Calling get_class() without arguments is deprecated
```

`get_class()`は、引数があれば引数のクラス名を返す、引数がなければ`get_class()`が書かれているクラス名を返す、という極めて分かりにくい挙動になっています。
そのため引数を必須にすることにします。
今後も自身のクラス名を知りたい場合は`$this`を渡すようにしましょう。


## DBA

### Calling dba_fetch() with $dba as the 3rd argument is now deprecated

第三引数に`resource $dba`を渡す[dba_fetch()](https://www.php.net/manual/ja/function.dba-fetch)は非推奨になりました。

よく見たら[explode()](https://www.php.net/manual/ja/function.explode.php)と同じ問題じゃないか。

```php
dba_fetch(string|array $key, resource $dba, int $skip = 0): string|false
dba_fetch(string $key, int $skip, resource $dba): string
```

PHP9.0以降は前者の書き方しかできなくなります。

## FFI

### Calling FFI::cast(), FFI::new(), and FFI::type() statically is now deprecated

[FFI::cast()](https://www.php.net/manual/ja/ffi.cast)・[FFI::new()](https://www.php.net/manual/ja/ffi.new.php)・[FFI::type()](https://www.php.net/manual/ja/ffi.type.php)の静的メソッド呼び出しが非推奨になります。
今後はインスタンスメソッドで呼び出しましょう。


## Intl

### The U_MULTIPLE_DECIMAL_SEP*E*RATORS constant had been deprecated

定数U_MULTIPLE_DECIMAL_SEPERATORSは非推奨になりました。
かわりにU_MULTIPLE_DECIMAL_SEPARATORSを使いましょう。

[EとAの誤字](https://github.com/php/php-src/actions/runs/4566210698)です。
これは別にPHPが間違ったわけではなく、[元々の仕様から間違っていた](https://github.com/unicode-org/icu/blob/release-74-1/icu4c/source/common/unicode/utypes.h#L552)せいです。


### The NumberFormatter::TYPE_CURRENCY has been deprecated

[NumberFormatter::TYPE_CURRENCY](https://www.php.net/manual/ja/class.numberformatter.php#numberformatter.constants.type-currency)が非推奨になりました。

実は[元々サポートされていません](https://www.php.net/manual/ja/numberformatter.format.php)でした。
かわりに[NumberFormatter::parseCurrency()](https://www.php.net/manual/ja/numberformatter.parsecurrency.php)を使いましょう。


## LDAP

### Calling ldap_connect() with separate hostname and port is deprecated

2引数を渡す[ldap_connect()](https://www.php.net/ldap_connect)が[非推奨になりました](https://wiki.php.net/rfc/deprecations_php_8_3#deprecate_calling_ldap_connect_with_2_parameters)。

```php
ldap_connect(?string $uri = null): LDAP\Connection|false
ldap_connect(?string $host = null, int $port = 389): LDAP\Connection|false
```

今後は前者を使いましょう。


## MBString

### Passing a negative $width to mb_strimwidth() is now deprecated

[mb_strimwidth()](https://www.php.net/manual/ja/function.mb-strimwidth.php)の第三引数`$width`に負の数を渡すことが非推奨になりました。
文字列関数に負数を与えると大抵は[末尾から数えた値](https://www.php.net/manual/ja/function.mb-substr.php)になるのですが、mb_strimwidthはそもそも固定長にする関数であり、意図が合わないため削除されます。


## Phar

### Calling Phar::setStub() with a resource and a length is now deprecated

[Phar::setStub()](https://www.php.net/manual/ja/phar.setstub.php)に引数`stream $stub, int $len`を与える呼び出しが非推奨になりました。

この書式はそもそもシグネチャに書かれていないですね。
[英語版](https://www.php.net/manual/en/phar.setstub.php)には[2023/11/06に追加されている](https://github.com/php/doc-en/commit/9c828621cbce488cf6306b21c39e208f847eabd5)のですが、わざわざシグネチャを追加したりせず最初からなかったことにしていた方がいいのではと思わないでもない。

## Random

### The MT_RAND_PHP Mt19937 variant is deprecated

[mt_srand](https://www.php.net/manual/ja/function.mt-srand.php)の第二引数MT_RAND_PHPが[非推奨になりました](https://wiki.php.net/rfc/deprecations_php_8_3#mt_rand_php)。
これはPHP7.1で修正された[壊れたメルセンヌツイスター](https://sucrose.hatenablog.com/entry/2016/02/19/235506)をわざわざ再現するというオプションなのですが、いいかげんもういいだろうということで削除されます。


## Standard

### The assert_options() function is now deprecated
### The ASSERT_ACTIVE, ASSERT_BAIL, ASSERT_CALLBACK, ASSERT_EXCEPTION, and ASSERT_WARNING constants have been deprecated
関数[assert_options()](https://www.php.net/manual/ja/function.assert-options.php)および関連の定数が非推奨になりました。
[assertまわりのクリーンアップ](https://qiita.com/rana_kualu/items/91111443105a75a55e2f#deprecate-remains-of-string-evaluated-code-assertions)により不要になりました。

## SQLite3

### Using exceptions is now preferred, warnings will be removed in the future

[SQLite3拡張モジュール](https://www.php.net/manual/ja/book.sqlite3.php)において、[今後例外がデフォルトになります](https://qiita.com/rana_kualu/items/91111443105a75a55e2f#use-exceptions-by-default-in-sqlite3-extension)。
PHP8.3では[enableExceptions(false)](https://www.php.net/manual/ja/sqlite3.enableexceptions.php)するとE_DEPRECATEDが発生しますが、初期値はfalseのままです。
PHP9.0では初期値がtrueとなり、`enableExceptions(false)`することもできなくなります。
最後にPHP10.0で`enableExceptions()`メソッド自体が削除されます。


## Zip

### The ZipArchive::FL_RECOMPRESS constant is deprecated

定数[ZipArchive::FL_RECOMPRESS](https://www.php.net/manual/ja/zip.constants.php)が非推奨になりました。
これは[libzip](https://github.com/nih-at/libzip/blob/v1.10.1/lib/zip.h#L90)に従ったものです。

# Changed Functions

変更のあった関数。

## Core

### gc_status() has added the following 8 fields

関数[gc_status()](https://www.php.net/manual/ja/function.gc-status.php)の返り値に要素がたくさん追加されました。

```php
var_dump(gc_status());

/* 例
array(12) {
  ["running"]=>
  bool(false)
  ["protected"]=>
  bool(false)
  ["full"]=>
  bool(false)
  ["runs"]=>
  int(5)
  ["collected"]=>
  int(100002)
  ["threshold"]=>
  int(50001)
  ["buffer_size"]=>
  int(131072)
  ["roots"]=>
  int(0)
  ["application_time"]=>
  float(0.0540216)
  ["collector_time"]=>
  float(0.0392877)
  ["destructor_time"]=>
  float(0)
  ["free_time"]=>
  float(0.0065847)
}
*/
```

[Add four extra fields to gc_status()](https://github.com/php/php-src/pull/9336)と[Expose time spent collecting cycles in gc_status()](https://github.com/php/php-src/pull/11523)の改修によるものです。

### class_alias() now supports creating an alias of an internal class

[class_alias()](https://www.php.net/manual/ja/function.class-alias)がビルトインクラスにも使えるようになりました。

```php
class_alias('stdClass', 'HOGE');
$c = new HOGE();
  // PHP8.3 $cはstdClass
  // PHP8.2 Uncaught ValueError: class_alias(): Argument #1 ($class) must be a user-defined class name, internal class name given
```

これまではユーザが自作したクラスにしかエイリアスをつくることができませんでした。


### Setting `open_basedir` at runtime using `ini_set('open_basedir', ...);` no longer accepts paths containing the parent directory (`..`)

[ini_set('open_basedir')](https://www.php.net/manual/ja/ini.core.php#ini.open-basedir)で、`php.ini`の`open_basedir`より上に遡ることができなくなりました。

まず前提として`php.ini`で`open_basedir`が設定されている必要があります。
そのうえで、`ini_set('open_basedir', '../foo/');`という設定は当然ながら禁止されているのですが、チェックが甘くて`ini_set('open_basedir', './../foo/');`で遡ることができていたとかいうぶっちゃけバグです。

これ、ドキュメントでは

```
Setting open_basedir at runtime using ini_set('open_basedir', ...); no longer accepts paths containing the parent directory (..). Previously, only paths starting with .. were disallowed. This could easily be circumvented by prepending ./ to the path.

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

としか書かれていなくて`php.ini`の記載がないのでかなり意味不明です。


### User exception handlers now catch exceptions during shutdown

ユーザ定義例外がシャットダウン中の例外をキャッチできるようになりました。

```php
set_exception_handler(function($excxeption){
    echo $excxeption->getMessage();
    return $excxeption;
});
register_shutdown_function(function(){
    throw new Exception('hoge', 999);
});

  // PHP8.3 hoge
  // PHP8.2 Fatal error:  Uncaught Exception: hoge
```

シャットダウン中は`max_execution_time`の対象外になったりするなど色々と例外なところなので、この中から例外を出すのもやめておいたほうが基本的にはよいと思います。


### The resultant HTML of highlight_string and highlight_file has changed

関数[highlight_string](https://www.php.net/manual/ja/function.highlight-string.php)および[highlight_file](https://www.php.net/manual/ja/function.highlight-file)の出力に変更がありました。

```php
highlight_string('<html>  a</html>');

  // PHP8.3
  <pre><code style="color: #000000">&lt;html&gt;  a&lt;/html&gt;</code></pre>

  // PHP8.2
  <code><span style="color: #000000">
  &lt;html&gt;&nbsp;&lt;span&gt;foo&lt;/span&gt;&lt;/html&gt;</span>
  </code>
```

括りが`<code><span>`から`<pre><code>`になり、スペースや改行が`&nbsp;`・`<br />`に変換されずそのまま出力されるようになりました。
ハイライト自体は変わっていないみたいです。

理由は[このあたり](https://github.com/php/phd/pull/79#issuecomment-1669933579)で、HTML standardに従った変更のようです。


## Calendar

### easter_date() now supports years from 1970 to 2,000,000,000 on 64-bit systems

関数[easter_date](https://www.php.net/manual/ja/function.easter-date.php)が1970年から2000000000年まで対応しました。
これまでは2037年まででした。

そもそも`easter_date`なんて関数初めて知ったわ。

## Curl

### curl_getinfo() now supports two new constants

関数[curl_getinfo](https://www.php.net/manual/ja/function.curl-getinfo)の第二引数`$option`が定数CURLINFO_CAPATHとCURLINFO_CAINFOに対応しました。

Curlに[最近](https://curl.se/libcurl/c/CURLINFO_CAPATH.html)[追加された](https://curl.se/libcurl/c/CURLINFO_CAINFO.html)定数です。


## Dom

### Changed DOMCharacterData::appendData() tentative return type to true

[DOMCharacterData::appendData()](https://www.php.net/manual/ja/domcharacterdata.appenddata.php)はtrueを返すようになりました。
PHP7.2まではvoidでした。

### DOMDocument::loadHTML(), DOMDocument::loadHTMLFile(), DOMDocument::loadXML() and DOMDocument::loadXMLFile() now have a tentative return type of bool

同様に[DOMDocument::loadHTML()](https://www.php.net/manual/ja/domdocument.loadhtml)・[DOMDocument::loadHTMLFile()](https://www.php.net/manual/ja/domdocument.loadhtmlfile.php)・[DOMDocument::loadXML()](https://www.php.net/manual/ja/domdocument.loadxml.php)・[DOMDocument::loadXMLFile()](https://www.php.net/manual/ja/domdocument.loadxmlfile.php)もbool型を返すようになりました。
これまでは場合によって`DOMDocument`が返ることがありました。

## Gd

### Changed imagerotate signature, removed the `ignore_transparent` argument


関数[imagerotate](https://www.php.net/manual/ja/function.imagerotate.php)から、第四引数`$ignore_transparent`が削除されました。
ずっと前からこの引数は使われていませんでした。

## Intl

### datefmt_set_timezone now returns true on success

[datefmt_set_timezone](https://www.php.net/manual/ja/intldateformatter.settimezone.php)はbool型を返すようになりました。
これまでは成功時にnull、失敗時にfalseというよくわからない返り値でした。

### IntlBreakiterator::setText() now returns false on failure

[IntlBreakiterator::setText()](https://www.php.net/manual/ja/intlbreakiterator.settext.php)はbool型を返すようになりました。
これまでは常にnullでした。


### IntlChar::enumCharNames is now returning a boolean

[IntlChar::enumCharNames()](https://www.php.net/manual/ja/intlchar.enumcharnames)はbool型を返すようになりました。
これまでは成功時にnull、失敗時にfalseでした。

### IntlDateFormatter::construct throws an U_ILLEGAL_ARGUMENT_ERROR exception

[IntlDateFormatter::__construct()](https://www.php.net/manual/ja/intldateformatter.create)は、ロケールが不正な場合にU_ILLEGAL_ARGUMENT_ERRORの例外を出すようになりました。
ERRORって名前なのに実際は[IntlException](https://www.php.net/manual/ja/class.intlexception)です。

## MBString

### mb_strtolower, mb_strtotitle, and mb_convert_case implement conditional casing rules for the Greek letter sigma

[mb_strtolower](https://www.php.net/manual/ja/function.mb-strtolower)ほかのマルチバイト関数が、ギリシャ文字シグマに対応しました。

[とある](https://www.php.net/manual/ja/migration83.other-changes.php)んだけど、実際は[だいぶ前から対応してるみたい](https://3v4l.org/JbnkR)なんですよね。
この[Greek letter sigma](https://www.php.net/manual/en/migration83.other-changes.php)ってのは、`Σ`ではなく何かダイアクリティカルマーク的な意味だったりするんですかね？


### mb_decode_mimeheader interprets underscores in QPrint-encoded MIME encoded words

関数[mb_decode_mimeheader](https://www.php.net/manual/ja/function.mb-decode-mimeheader)が、QPrintエンコードのアンダースコア`_`を[RFC2047](https://tools.ietf.org/html/rfc2047)に従って正しく解釈するようになりました。

しかし[QPrintはPHP8.2で非推奨になっている](https://qiita.com/rana_kualu/items/64bd73ad3d314a1cccdc#mbstring)ので、どうして今さらって感じはしますね。

### In rare cases, mb_encode_mimeheader will transfer-encode its input string where it would pass it through as raw ASCII in PHP 8.2.

関数[mb_encode_mimeheader](https://www.php.net/manual/ja/function.mb-encode-mimeheader)において、PHP8.2ではASCIIのまま通っていたものが、稀に`transfer-encode`されるようになることがあります。

ということらしいですがそもそも`transfer-encode`ってなんだよ。


### mb_encode_mimeheader no longer drops NUL (zero) bytes when QPrint-encoding the input string

関数[mb_encode_mimeheader](https://www.php.net/manual/ja/function.mb-encode-mimeheader)において、QPrintエンコードする際にヌルバイトを削除しないようにしました。
このせいで、かつては`UTF-16`と`UTF-32`の文字列を渡したときに文字が壊れてしまっていたそうです。

文字コードはほんと大変ですね。


### mb_detect_encoding's "non-strict" mode now behaves as described in the documentation

関数[mb_detect_encoding](https://www.php.net/manual/ja/function.mb-detect-encoding)の第三引数`$strict`にfalseを渡したときの動作を改善しました。

マニュアルの表記は"最も近いものを返す"なのですが、実際の動作は"先頭〇バイトが合っていたらそれを正解とする"となっていました。
そのため、先頭が合わなかったらfalseが返るし、先頭の何バイトかさえ合っていればその後が全部正しくなくてもそのエンコーディングが返っていました。
[こちら](https://github.com/php/php-src/pull/11239)で実際に、最も近いものを返すように修正されました。

まあそもそもmb_detect_encodingに頼るのはやめようって話だな。

## mysqli

### mysqli_fetch_object now raises a ValueError instead of an Exception

関数[mysqli_fetch_object()](https://www.php.net/manual/ja/mysqli-result.fetch-object.php)は、第二引数`$constructor_args`を渡しているのにクラスにコンストラクタがない場合にValueErrorを出すようになりました。

```php
class C{}
((new mysqli())->query($sql))->fetch_object('C', ['dummy']);
  // PHP8.3  Fatal error: Uncaught ValueError
  // PHP8.2  Fatal error: Uncaught Exception
```

これまではExceptionでした。

ちなみにコンストラクタが存在するのに引数を渡さない場合はToo few argumentsの[ArgumentCountError](https://www.php.net/manual/ja/class.argumentcounterror.php)が発生します。


### mysqli_poll now raises a ValueError when the read nor error arguments are passed

関数[mysqli_poll()](https://www.php.net/manual/ja/mysqli.poll.php)は、第一引数`&$read`と第二引数`&$error`が両方必須になりました。
なんで？

### mysqli_field_seek and mysqli_result::field_seek now specify return type as true instead of bool

関数[mysqli_field_seek()](https://www.php.net/manual/ja/mysqli-result.field-seek.php)の返り値がtrue型になりました。

これまでは定義上はbool型でしたが、実際はPHP8.0以降常にtrueを返していました。

## ODBC

### odbc_autocommit() now accepts null for the $enable parameter

関数[odbc_autocommit()](https://www.php.net/manual/ja/function.odbc-autocommit)の第二引数`$enable`がnull許容型になりました。

これまでは必須でした。
未指定時は現在の設定が返ってきます。

なお、指定時は指定が成功したかどうかが返ってきます。
指定時と未指定時で挙動がかわるのやめようよ。

## PGSQL

### pg_fetch_object now raises a ValueError instead of an Exception when the constructor_args argument is non empty with the class not having constructor.

関数[pg_fetch_object()](https://www.php.net/manual/ja/function.pg-fetch-object)は、第四引数`$constructor_args`を渡しているのにクラスにコンストラクタがない場合にValueErrorを出すようになりました。

上記`mysqli_fetch_object`と同じです。

### pg_insert now raises a ValueError instead of a WARNING when the table specified is invalid

関数[pg_insert()](https://www.php.net/manual/ja/function.pg-insert)は、テーブルが正しくない場合にValueErrorの例外を出すようになりました。

これまではWarningでした。

### pg_insert and pg_convert raises a ValueError or a TypeError instead of a WARNING when the value/type of a field does not match properly with a PostGreSQL's type

関数[pg_insert()](https://www.php.net/manual/ja/function.pg-insert)と[pg_convert()](https://www.php.net/manual/ja/function.pg-convert.php)は、フィールドの型が正しくない場合にValueErrorの例外を出すようになりました。

これまではWarningでした。

### The $row param of pg_fetch_result(), pg_field_prtlen() and pg_field_is_null() is now nullable


関数[pg_fetch_result()](https://www.php.net/manual/ja/function.pg-fetch-result.php)・[pg_field_prtlen()](https://www.php.net/manual/ja/function.pg-field-prtlen.php)・[pg_field_is_null](https://www.php.net/manual/ja/function.pg-field-is-null.php)の引数`$row`がnull許容型になりました。

これまでは引数の省略が可能で、複数のシグネチャを持つという謎の関数でしたが、おそらく今後は前者のシグネチャに統一されていくことになると思います。

```php
// PHP8.3
pg_fetch_result(PgSql\Result $result, int $row, mixed $field): string|false|null
pg_fetch_result(PgSql\Result $result, mixed $field): string|false|null

// PHP9か10か
pg_fetch_result(PgSql\Result $result, ?int $row, mixed $field): string|false|null
```

## Random

### Changed mt_srand() and srand() to not check the number of arguments to determine whether a random seed should be used

関数[mt_srand()](https://www.php.net/manual/ja/function.mt-srand.php)の挙動が[Random\Engine\Mt19937::__construct()](https://www.php.net/manual/ja/random-engine-mt19937.construct.php)と同じになりました。


対象の[Issueはこちら](https://github.com/php/php-src/issues/10292)、[変更はこちら](https://github.com/php/php-src/commit/1f05d6ef8050803dd9ba4ae640697fff534e93a2)です。
これマイグレーションガイドが[not check the number of arguments](https://www.php.net/manual/en/migration83.other-changes.php)・[引数の数をチェックしなくなりました](https://www.php.net/manual/ja/migration83.other-changes.php)とか書いてあるんだけど、正直何を言いたいのかよくわかりません。

どちらかというと単に[マニュアル](https://www.php.net/manual/ja/function.srand.php)の間違いな気がする。

```php
seed に 0 を指定した場合はランダムな値が設定されます。
with a random value if seed is 0.
```

実際は0は0というシード値として扱われ、nullはランダムな値が設定されます。

## Reflection

### Return type of ReflectionClass::getStaticProperties() is no longer nullable

[ReflectionClass::getStaticProperties()](https://www.php.net/manual/ja/reflectionclass.getstaticproperties.php)の返り値が`?array`型から`array`型になりました。

これまでもstaticプロパティが無い場合は`[]`を返していたので、[単にシグネチャが変わっただけ](https://github.com/php/php-src/pull/10418)です。


### Calling ReflectionProperty::setValue() with only one parameter is deprecated

[ReflectionProperty::setValue()](https://www.php.net/manual/ja/reflectionproperty.setvalue.php)について、引数がひとつだけの呼び方が非推奨になりました。

```php
public ReflectionProperty::setValue(object $object, mixed $value): void
public ReflectionProperty::setValue(mixed $value): void
```

## Standard

### E_NOTICEs emitted by unserialize() have been promoted to E_WARNING

[unserializeの改善](https://qiita.com/rana_kualu/items/fc1a359b85118b4b7a76)です。
関数[unserialize](https://www.php.net/manual/ja/function.unserialize.php)で発生していたE_NOTICEは全てE_WARNINGになります。
また、それ以外にも様々な改善がなされています。

### unserialize() now emits a new E_WARNING if the input contains unconsumed bytes

関数[unserialize](https://www.php.net/manual/ja/function.unserialize.php)は、不正な入力にE_WARNINGを出すようになりました。

```php
unserialize('i:5;i:6;');
  // PHP8.3 PHP Warning:  unserialize(): Extra data starting at offset 4 of 8 bytes
  // PHP8.2 エラー出ない
```

CVE-2023-21036への対応だそうです。


### array_pad() is now only limited by the maximum number of elements an array can have

関数[array_pad()](https://www.php.net/manual/ja/function.array-pad.php)で追加することのできる配列要素数の上限がなくなりました。
これまでは1048576個という謎の上限がありました。

もちろん配列要素数を増やしすぎるとメモリが死ぬので、実質的な上限は存在します。


### strtok() raises a warning in the case token is not provided when starting tokenization

関数[strtok()](https://www.php.net/manual/ja/function.strtok)は、初回コール時に第二引数`$token`が存在しない場合にE_WARNINGを出すようになりました。

```php
strtok('a;b;c');
  // PHP8.3  PHP Warning:  strtok(): Both arguments must be provided when starting tokenization
  // PHP8.2  エラー出ない
```

そもそも`strtok()`はなんというか非常に気持ち悪い挙動をする関数なので、普通に[explode()](https://www.php.net/manual/ja/function.explode.php)とか[preg_split()](https://www.php.net/manual/ja/function.preg-split.php)とか使った方がいいと思います。


### password_hash() will now chain the underlying Random\RandomException as the ValueError's $previous Exception when salt generation fails

関数[password_hash()](https://www.php.net/manual/ja/function.password-hash)において、SALTの生成が失敗した場合に原因の[Random\RandomException](https://www.php.net/manual/ja/class.random-randomexception.php)が例外の[$previousに入ってくる](https://www.php.net/manual/ja/error.getprevious.php)ようになりました。

とあるのですが、そもそもどんなときにSALT生成が失敗するのかよくわからない。
なんか適当に`['cost'=>999]`とか入れるとSALT生成まで進まずNGされるみたいで、messageに`Invalid bcrypt cost parameter`が入ってきて`$previous`は空でした。

### proc_open() $command array must now have at least one non empty element

関数[proc_open()](https://www.php.net/manual/ja/function.proc-open)の第一引数`$command`に配列を渡す場合、少なくともひとつは空でない値を渡さないと動かなくなりました。

```php
proc_open(['', null, false, true], $descriptorspec, $pipes);
  // Uncaught ValueError: First element must contain a non-empty program name
```


### proc_open() returns false if $command array is invalid command

関数[proc_open()](https://www.php.net/manual/ja/function.proc-open)の第一引数`$command`が正しくない場合、リソースではなくfalseが返るようになりました。
これまではリソースが返ってくるものの、その後実行するとエラーになっていました。

Windowsでは元々そのような動作でしたが、Linux等でもそのようになったみたいです。


### array_sum() and array_product() now warn when values in the array cannot be converted to int/float

関数[array_sum()](https://www.php.net/manual/ja/function.array-sum.php)および[array_product()](https://www.php.net/manual/ja/function.array-product.php)の引数にint/float型に変換できない値が入っていた場合、警告が出るようになりました。

```php
array_sum([1, 2, 'a']);
  // PHP8.3  Warning: array_sum(): Addition is not supported on type string
  // PHP8.2  エラー出ない
```

これまでは単に無視されていました。
今のところ計算結果は同じです。


### number_format() $decimal parameter handles rounding to negative places

関数[number_format()](https://www.php.net/manual/ja/function.number-format)の第二引数`$decimals`にマイナス値を渡した場合、小数点を遡って丸め込まれるようになりました。

```php
number_format(34567, -2)
  // PHP8.3  "34,600"
  // PHP8.2  "34,567"
```

これまではマイナス値は無視されていました。
これは誰得なんだ…？


### The $before_needle argument added to strrchr()

関数[strrchr()](https://www.php.net/manual/ja/function.strrchr)に第三引数`$before_needle`が追加されました。

これは[strstr()](https://www.php.net/manual/ja/function.strstr.php)や[stristr()](https://www.php.net/manual/ja/function.stristr.php)と同じ引数になります。


### str_getcsv() and fgetcsv() return empty string instead of a string with a single zero byte for the last field which contains only unterminated enclosure

関数[str_getcsv](https://www.php.net/manual/ja/function.str-getcsv)および[fgetcsv](https://www.php.net/manual/ja/function.fgetcsv)は、閉じていないクォートにヌルバイトを返していたのを返さなくなりました。

```php
str_getcsv('"')
  // PHP8.3  [string(0) ""]
  // PHP8.2  [string(1) ""]
```

そもそも入力が不正なので、これが問題になることはないでしょう。


# 新しい関数

## Date

### [DatePeriod::createFromISO8601String()](https://www.php.net/manual/ja/dateperiod.createfromiso8601string.php)

[DatePeriod::__construct()](https://www.php.net/manual/ja/dateperiod.construct.php)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#dateperiod__construct)に伴い追加されました。

## DOM

### [DOMNode::contains()](https://www.php.net/manual/ja/domnode.contains.php)・[DOMNameSpaceNode::contains()](https://www.php.net/manual/ja/domnodespacenode.contains.php)・[DOMElement::getAttributeNames()](https://www.php.net/manual/ja/domelement.getattributenames.php)・[DOMNode::getRootNode()](https://www.php.net/manual/ja/domnode.getrootnode.php)・[DOMParentNode::replaceChildren()](https://www.php.net/manual/ja/domparentnode.replacechildren.php)・[DOMNode::isEqualNode()](https://www.php.net/manual/ja/domnode.isequalnode.php)・[DOMElement::insertAdjacentElement()](https://www.php.net/manual/ja/domelement.insertadjacentelement.php)・[DOMElement::insertAdjacentText()](https://www.php.net/manual/ja/domelement.insertadjacenttext.php)・[DOMElement::toggleAttribute()](https://www.php.net/manual/ja/domelement.toggleattribute.php)

各種DOM関数。


## Intl

### [IntlCalendar::setDate()](https://www.php.net/manual/ja/intlcalendar.setdate.php)・[IntlCalendar::setDateTime()](https://www.php.net/manual/ja/intlcalendar.setdatetime.php)

[IntlCalendar::set()](https://www.php.net/manual/ja/intlcalendar.set.php)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#intlcalendarset)に伴い追加されました。

### [IntlGregorianCalendar::createFromDate()](https://www.php.net/manual/ja/intlgregoriancalendar.createfromdate.php)・[IntlGregorianCalendar::createFromDateTime()](https://www.php.net/manual/ja/intlgregoriancalendar.createfromdatetime.php)

[IntlGregorianCalendar::__construct()](https://www.php.net/manual/ja/intlgregoriancalendar.construct.php)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#intlgregoriancalendar__construct)に伴い追加されました。

## JSON

### [json_validate()](https://www.php.net/manual/ja/function.json-validate.php)

[JSONが正しいかどうか判定できる関数](https://qiita.com/rana_kualu/items/e293a87342a94caf52a6)です。

## LDAP

### [ldap_connect_wallet()](https://www.php.net/manual/ja/function.ldap-connect-wallet.php)

[ldap_connect()](https://www.php.net/manual/ja/function.ldap-connect)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#ldap_connect)に伴い追加されました。


### [ldap_exop_sync()](https://www.php.net/manual/ja/function.ldap-exop-sync.php)

[ldap_exop()](https://www.php.net/manual/ja/function.ldap-exop)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#ldap_exop)に伴い追加されました。

## MBString

### [mb_str_pad()](https://www.php.net/manual/ja/function.mb-str-pad.php)

[マルチバイト版str_pad](https://qiita.com/rana_kualu/items/b2967c863cc882d072e7)です。

## Posix

### [posix_sysconf()](https://www.php.net/manual/ja/function.posix-sysconf.php)・[posix_pathconf()](https://www.php.net/manual/ja/function.posix-pathconf.php)・[posix_fpathconf()](https://www.php.net/manual/ja/function.posix-fpathconf.php)・[posix_eaccess()](https://www.php.net/manual/ja/function.posix-eaccess.php)

各種POSIX関連情報を取得できるみたいです。
Windowsは未対応なので不明。

## PGSQL

### [pg_set_error_context_visibility()](https://www.php.net/manual/ja/function.pg-set-error-context-visibility.php)

[pg_result_error()](https://www.php.net/manual/ja/function.pg-result-error.php)が返すメッセージにCONTEXTフィールドを入れるかどうかを制御できるみたいです。
よくわかりませんが単にPostgresの[PQsetErrorContextVisibility](https://www.postgresql.jp/docs/11/libpq-control.html)のラッパーみたい。

## Random

### [Randomizer::getBytesFromString()](https://www.php.net/manual/ja/random-randomizer.getbytesfromstring.php)

[ランダムな文字列生成](https://qiita.com/rana_kualu/items/0c6bb76736c952a5e217#getbytesfromstring)です。

### [Randomizer::nextFloat()](https://www.php.net/manual/ja/random-randomizer.nextfloat.php)・[Randomizer::getFloat()](https://www.php.net/manual/ja/random-randomizer.getfloat.php)

[ランダムな数値生成](https://qiita.com/rana_kualu/items/0c6bb76736c952a5e217#getfloat)です。

```php
$randomizer = new \Random\Randomizer();

// ランダムな文字列
echo $randomizer->getBytesFromString('abcdefghijklmnopqrstuvwxyz0123456789', 16);
// aが75%、bが25%で出現するランダム文字列
echo $randomizer->getBytesFromString('aaab', 10);

// [-90, 90]
echo $randomizer->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed);
// (-180, 180)
echo $randomizer->getFloat(-90, 90, \Random\IntervalBoundary::OpenClosed);

// getFloat(0, 1, ClosedOpen)のショートカット
echo $randomizer->nextFloat();
// 10%の確率
var_dump($randomizer->nextFloat() < 0.1);
```

何も考えずに実装すると実はランダムになってなかったというようなことがよくあるランダム文字列・数値生成が容易にできるようになりました。
また0は含むが1は含まない、という端処理が簡単にできるのも便利ですね。


## Reflection

### [ReflectionMethod::createFromMethodName()](https://www.php.net/manual/ja/reflectionmethod.createfrommethodname.php)


[ReflectionMethod::__construct()](https://www.php.net/manual/ja/reflectionmethod.construct.php)の[オーバーロード削除](https://qiita.com/rana_kualu/items/4d67ffdf4cb05d1ba86f#reflectionmethod__construct)に伴い追加されました。

## Sockets

### [socket_atmark()](https://www.php.net/manual/ja/function.socket-atmark.php)

ソケットに帯域外マークが付けられているかを調べます。

なんのことかさっぱりわかりませんが、実体は単なる[sockatmark](https://man7.org/linux/man-pages/man3/sockatmark.3.html)のラッパーです。

## Standard

### [str_increment()](https://www.php.net/manual/ja/function.str-increment.php)・[str_decrement()](https://www.php.net/manual/ja/function.str-decrement.php)

文字列のインクリメント・デクリメント関数です。

PHPはPerl由来の[加算子による文字列インクリメント](https://www.php.net/manual/ja/language.operators.increment.php)に対応しているのですが、デクリメントは効かなかったり算術演算子と異なる結果になったりと、色々半端な挙動になっています。
そこで専用の文字列関数を追加して、挙動を切り離すことにしました。

```php
$s = 'foo';
var_dump(str_decrement($s)); // "fon"
var_dump(str_increment($s)); // "fop"

$s++; // エラー出ない
```

加算子によるインクリメントは、当面削除される予定はありませんが推奨されなくなります。


### [stream_context_set_options()](https://www.php.net/manual/ja/function.stream-context-set-options.php)


[stream_context_set_option()](https://www.php.net/manual/ja/function.stream-context-set-option.php)の設定値をまとめて与えることができるようになりました。
まあ[curl_setopt()](https://www.php.net/manual/ja/function.curl-setopt.php)と[curl_setopt_array()](https://www.php.net/manual/ja/function.curl-setopt-array.php)みたいなものです。


## Zip

### [ZipArchive::setArchiveFlag()](https://www.php.net/manual/ja/ziparchive.setarchiveflag.php)・[ZipArchive::getArchiveFlag()](https://www.php.net/manual/ja/ziparchive.getarchiveflag.php)

グローバルフラグの値を設定・取得します。

よくわかりませんが[libzip](https://libzip.org/documentation/zip_get_archive_flag.html)のラッパーみたいです。
いまのところ[ZIP_AFL_IS_TORRENTZIP](https://libzip.org/documentation/zip_get_archive_flag.html#ZIP_AFL_IS_TORRENTZIP)・[ZIP_AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE](https://libzip.org/documentation/zip_get_archive_flag.html#ZIP_AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE)に対応しているようです。


# Other Changes to Extensions

## Core

### WeakMaps now have ephemeron-like behavior

WeakMapは[Ephemeron](https://en.wikipedia.org/wiki/Ephemeron)のような挙動になりました。

どういうことかというと、foreachで反復している最中に消えることがあります。
これまでは、反復開始後は終わるまで消えることはありませんでした。


### The ++ and -- operators now emit warnings when attempting to increment values of type bool

[インクリメント・デクリメント演算子の改善](https://wiki.php.net/rfc/saner-inc-dec-operators)です。

基本的にint・float型以外に使用できなくなります。

```php
$n = null;
var_dump(--$n);
  // PHP8.3  PHP Warning:  Decrement on type null has no effect, this will change in the next major version of PHP
  // PHP8.2  エラー出ない
```

文字列についてはかわりにできた`str_increment`を使いましょう。

## DOM

### The DOM lifetime mechanism has been reworked such that implicitly removed nodes can still be fetched

先に削除されたDOMノードもfetchできるようになりました。
これまでは例外が発生していました。

これ必要ある？

## SQLite3

### The SQLite3 class now throws \SQLite3Exception (extends \Exception) instead of \Exception

SQLite3が発生する例外が`\SQLite3Exception`になりました。
これまでは`\Exception`でした。

### The SQLite error code is now passed in the exception error code instead of being included in the error message

SQLiteのエラーコードが、例外のエラーコードに出力されるようになりました。
これまではエラーメッセージをパースしないと取り出すことができませんでした。

# New Global Constants

新しい定数。

基本的には使用しているライブラリの定数をそのまま取り込んでいるだけです。

## Curl

CURLINFO_CAPATH
CURLINFO_CAINFO
CURLOPT_MIME_OPTIONS
CURLMIMEOPT_FORMESCAPE
CURLOPT_WS_OPTIONS
CURLWS_RAW_MODE
CURLOPT_SSH_HOSTKEYFUNCTION
CURLOPT_PROTOCOLS_STR
CURLOPT_REDIR_PROTOCOLS_STR
CURLOPT_CA_CACHE_TIMEOUT
CURLOPT_QUICK_EXIT
CURLKHMATCH_OK
CURLKHMATCH_MISMATCH
CURLKHMATCH_MISSING
CURLKHMATCH_LAST

## Intl

MIXED_NUMBERS
HIDDEN_OVERLAY

## OpenSSL

OPENSSL_CMS_OLDMIMETYPE
PKCS7_NOOLDMIMETYPE

## PCNTL

SIGINFO

## PDO_ODBC

PDO_ODBC_TYPE

## PGSQL

PGSQL_TRACE_SUPPRESS_TIMESTAMPS
PGSQL_TRACE_REGRESS_MODE
PGSQL_ERRORS_SQLSTATE
PGSQL_SHOW_CONTEXT_NEVER
PGSQL_SHOW_CONTEXT_ERRORS
PGSQL_SHOW_CONTEXT_ALWAYS

## Posix

POSIX_SC_ARG_MAX
POSIX_SC_PAGESIZE
POSIX_SC_NPROCESSORS_CONF
POSIX_SC_NPROCESSORS_ONLN
POSIX_PC_LINK_MAX
POSIX_PC_MAX_CANON
POSIX_PC_MAX_INPUT
POSIX_PC_NAME_MAX
POSIX_PC_PATH_MAX
POSIX_PC_PIPE_BUF
POSIX_PC_CHOWN_RESTRICTED
POSIX_PC_NO_TRUNC
POSIX_PC_ALLOC_SIZE_MIN
POSIX_PC_SYMLINK_MAX

## Sockets

SO_ATTACH_REUSEPORT_CBPF
SO_DETACH_BPF
SO_DETACH_FILTER
TCP_QUICKACK
IP_DONTFRAG
IP_MTU_DISCOVER
IP_PMTUDISC_DO
IP_PMTUDISC_DONT
IP_PMTUDISC_WANT
IP_PMTUDISC_PROBE
IP_PMTUDISC_INTERFACE
IP_PMTUDISC_OMIT
AF_DIVERT
SOL_UDPLITE.
UDPLITE_RECV_CSCOV.
UDPLITE_SEND_CSCOV.
SO_RERROR (NetBSD only).
SO_ZEROIZE (OpenBSD only).
SO_SPLICE (OpenBSD only).
TCP_REPAIR
SO_REUSEPORT_LB
IP_BIND_ADDRESS_NO_PORT

## Zip

ZipArchive::ER_DATA_LENGTH
ZipArchive::ER_NOT_ALLOWED
ZipArchive::AFL_RDONLY
ZipArchive::AFL_IS_TORRENTZIP
ZipArchive::AFL_WANT_TORRENTZIP
ZipArchive::AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE
ZipArchive::FL_OPEN_FILE_NOW
ZipArchive::LENGTH_TO_END
ZipArchive::LENGTH_UNCHECKED

# Changes to INI File Handling

iniファイルの変更

## assert.*

以下のini設定は削除されました。
assert.active
assert.bail
assert.callback
assert.exception
assert.warning

## zend.max_allowed_stack_size

最大スタックサイズを設定します。
デフォルトは0で、0もしくは-1もしくは正の値を設定します。

## zend.reserved_stack_size

予約済みスタックサイズを設定します。

スタックサイズが何なのか正直ちょっとよくわからない。


# Windows Support

サポートされるWindowsの最小バージョンがWindows8・Windows Server 2012になりました。


# Other Changes

## Core

### An Error is now thrown when the process call stack exceeds a certain size

プロセス呼び出しのスタックサイズが一定値を超えるとエラーが出るようになりました。
最大許容サイズはini設定`zend.max_allowed_stack_size`、および`zend.reserved_stack_size`・`fiber.stack_size`で決まります。

## FFI

### FFI::load() is now allowed during preloading when opcache.preload_user is the current system user

実行ユーザが[opcache.preload_user](https://www.php.net/manual/ja/opcache.configuration.php#ini.opcache.preload-user)である場合、プリロード中に[FFI::load()](https://www.php.net/manual/ja/ffi.load.php)できるようになりました。

これまでは`opcache.preload_user`が設定されていた場合は一律不可でした。

## FPM

### FPM CLI test now fails if the socket path is longer than supported by OS

php-fpmのソケットパスが、OSのサポートしているものより長い場合にエラーを出すようになりました。

とあるのですが、このソケットパスってファイル長のことですかね？
composerファイルをWindowsローカルに持ってきたらファイルパスが長すぎて動かねえみたいなことがたまにありますが、それと同じようなかんじ？

## Opcache

### In the cli and phpdbg SAPIs, preloading does not require the opcache.preload_user directive to be set anymore when running as root.

CLIからrootで実行した場合、`opcache.preload_user`を設定しなくてもrootユーザでのプリロードが許可されます。

Web等からPHPを実行したときにrootユーザでプリロードされるとセキュリティ的に危ないので、`opcache.preload_user=root`を明示しないかぎり禁止されています。


## Streams

### Blocking fread() on socket connection returns immediately if there are any buffered data instead of waiting for more data

ソケットに対する[fread()](https://www.php.net/manual/ja/function.fread.php)をブロックすると、続きを待たずにバッファリングデータを即座に返すようになりました。

とあるのだけどどういう場合に起きるのかさっぱりわかりません。

### Memory stream no longer fails if seek offset is past the end

メモリストリームに対してファイル終端を超えるオフセットを指定した場合、これまでは失敗していましたが失敗しなくなりました。
さらにその状態で書き込みを行うと、その間が0で埋められます。

### stat() access operations like file_exists() and similar will now use real path instead of the actual stream path

[stat()](https://www.php.net/manual/ja/function.stat)は、ファイルパスとしてストリームのパスではなくリアルパスを使うようになりました。
このアクセスは[file_exists()](https://www.php.net/manual/ja/function.file-exists.php)などと同じ方法です。

とあるのですが、`ストリームのパス`って何だ？


# Performance Improvements

パフォーマンスの改善。

## DOM

### Looping over a DOMNodeList now uses caching

DOMNodeListのループにキャッシュを使うようになりました。
これまではループに二次関数オーダーの時間がかかっていました。

### Getting text content from nodes now avoids an allocation

ノードからテキストを取得する際に、メモリを確保しなくなりました。

### DOMChildNode::remove() now runs in O(1) performance

[DOMChildNode::remove()](https://www.php.net/manual/ja/domnode.removechild.php)が`O(1)`で動くようになりました。

## Standard

### The file() flags error check is now about 7% faster

関数[file()](https://www.php.net/manual/ja/function.file.php)の第二引数`$flags`のエラーチェックが7%高速化しました。

って何そのピンポイントな高速化。
おそらく他の関数にもこのように手が加えられているとは思いますが、そのあたりはUPGRADINGに記載がありません。

## SPL

### RecursiveDirectoryIterator now performs less I/O when looping over a directory

[RecursiveDirectoryIterator](https://www.php.net/manual/ja/class.recursivedirectoryiterator.php)を使った際のディスクアクセスを減少しました。


# 感想

めちゃ多い！

けど致命的に互換が崩れるほどの変更は見当たらないように思えます。

今回は劇的に書き心地が変わるような大きな変更というのは見当たらず、少し便利になる小さな変更がいくつもあるというかんじですね。
移行にそこまで負担はなさそうなので、とりあえず試してみる勢いで行ってしまっていいのではないでしょうか。

次バージョンのPHP8.4で導入される機能も早々に幾つか決まっており、これからもPHPの進化は続いていきます。