PHP8.3の新機能について（part2）

以前PHP8.3の新機能について（part1）という内容でQiitaに投稿をしました。
その続きの内容を投稿したいと思います。
[前回の記事](https://qiita.com/HaruKado777/items/54e74936406262b2b39c)はこちらをご確認ください。

## [Randomizer Additions](https://wiki.php.net/rfc/randomizer_additions)
Randomizerクラスに以下の関数が追加されます。

① getBytesFromString()
与えられた文字列、文字列長を参照してランダムに文字列を生成します。
　第一引数$string：選択対象の文字列
　第二引数length：返り値の文字列長

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

// ランダムな文字列を生成する
$randomizer->getBytesFromString('abcdefghijklmnopqrstuvwxyz0123456789', 16);

// aが75%、bが25%のランダム文字列を生成する
$randomizer->getBytesFromString('aaab', 16);
```

② getFloat()
引数$minと$maxの間の浮動小数点数を返します。
$minと$maxの区間境界を変更するには、第三引数の$boundaryを指定する必要があります。

- 第一引数$min：最小値
- 第二引数$max：最大値
- 第三引数$boundary：$min,$max の区間境界を決める
    - \Random\IntervalBoundary::ClosedOpen：[$min, $max)（デフォルト）
    - ClosedClosed：[$ming, $max]
    - ClosedOpen：($min, $max]
    - OpenOpen：($min, $max)

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

// 90/-90どちらも可
$randomizer->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),
// 180はあるけど, -180はない
$randomizer->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),    
)); 
```

③ nextFload()
getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen)と同等の処理を実行します。
内部実装がgetFloat()よりも単純にまとまっているので、処理速度が速いみたいです。

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

// 50%の確率
var_dump(
    $randomizer->nextFloat() < 0.5
); // bool(true)

// 10%の確率
var_dump(
    $randomizer->nextFloat() < 0.1
); // bool(false)
```

## [json_validate function()](https://wiki.php.net/rfc/json_validate)
文字列が正しいJSON文字列であるかを検証する関数が追加されます。
現状でも[json_decode()](https://www.php.net/manual/ja/function.json-decode.php)や正規表現でも確認はできますが、検証以外の不要な処理が入っていてパフォーマンスに影響があったり、処理が複雑になったりといった問題があるため追加されてようです。
参考：https://externals.io/message/118310

使用例
``` php
json_validate('{ "test": { "foo": "bar" } }'); // true
json_validate('{ "": "": "" } }'); // false
```

## [Define proper semantics for range() function](https://wiki.php.net/rfc/proper-range-semantics)
range()関数に以下の改善が実施されます。

・不自然な挙動の修正
・不正な引数が渡された際のエラーハンドリング追加
``` php
var_dump(range(0, 3, -1));
// PHP8.2まで [0, 1, 2, 3]
// PHP8.3以降 ValueError

var_dump(range('9', 'A'));
// PHP8.2まで [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
// PHP8.3以降 [9, :, ;, <, =, >, ?, @, A]

var_dump(range('', 0));
// PHP8.2まで [0]
// PHP8.3以降 [0]  Warning: range(): Argument #1 ($start) must not be empty, casted to 0
```

## [More Appropriate Date/Time Exceptions](https://wiki.php.net/rfc/datetime-exceptions)
DateTimeのエラーハンドリング機能が改善されます。

これまでDateTimeはException、Errorなどのエラーを発生させていたが、より具体的な例外を投げるよう改善されているようです。

``` php
$i = DateInterval::createFromDateString('last Monday of');

// 不正な値（相対時間）を渡す
// オブジェクト型の書き方の場合は例外発生
$sub = (new DateTimeImmutable())->sub($i);

// PHP8.2  Warning: DateTimeImmutable::sub(): Only non-special relative time specifications are supported for subtraction
// PHP8.3  Fatal error: DateMalformedIntervalStringException 

// 手続き型の場合は依然と同じ挙動
$sub = date_sub(new DateTime(), $i);

// PHP8.2  Warning: date_sub(): Only non-special relative time specifications are supported for subtraction
// PHP8.3  Warning: date_sub(): Only non-special relative time specifications are supported for subtraction
```

## [Improve unserialize() error handling](https://wiki.php.net/rfc/improve_unserialize_error_handling)
unserialize関数のエラーハンドリング機能を強化するための準備が行われます。

これまで様々なエラーや例外を発生させいましたが、PHP9からはUnserializationFailedExceptionで処理できるようになる予定で、その準備が実施されます。

``` php
unserialize('foo'); 
// PHP8.2  E_NOTICE
// PHP8.3  E_WARNING
// PHP9.0  UnserializationFailedException

unserialize('O:19:"SplDoublyLinkedList":3:{i:0;i:0;i:1;N;i:2;a:0:{}}'); 
// PHP8.2  UnexpectedValueException
// PHP9.0  UnserializationFailedException
```

## [Make unserialize() emit a warning for trailing data](https://wiki.php.net/rfc/unserialize_warn_on_trailing_data)

unserialize関数の末尾に不要なバイトがある場合は警告を表示するようになります。
``` php
<?php
 
var_dump(unserialize('i:5;i:6;'));
// Warning: unserialize(): Extra data starting at offset 4 of 8 bytes in %s on line %d
// int(5)
```

## [Saner array_(sum|product)()](https://wiki.php.net/rfc/saner-array-sum-product)
array_sum関数とarray_product関数が改善されます。
これまで一部の動作が算術演算子と異なっていましたが、PHP8.3以降は同じ動作になります。
``` php
echo gmp_init("123") + 456;
// PHP8.2  579
// PHP8.3  579

echo array_sum([gmp_init("123"), 456]);
// PHP8.2  456 ←
// PHP8.3  579
```

## [Path to Saner Increment/Decrement operators](https://wiki.php.net/rfc/saner-inc-dec-operators)
インクリメント・デクリメント演算子を改善されます。
現状bool型の場合は何も動作しませんが、string型の場合はキャストされたのちに処理が実行されます。
``` php
$int  =  10 ; 
var_dump ( ++ $int ) ;  // int(11) 
$int  =  10 ; 
var_dump ( - $int ) ;  // int(9)
 
$float  =  5.7 ; 
var_dump ( ++ $float ) ;  // float(6.7) 
$float  =  5.7 ; 
var_dump ( - $float ) ;  // float(4.7)
 
$false  =  false ; 
var_dump ( ++ $false ) ;  // bool(false) 
var_dump ( -- $false ) ;  // bool(false) 
$true  =  true ; 
var_dump ( ++ $true ) ;  // bool(true) 
var_dump ( -- $true ) ;  // bool(true)
 
$stringInt  =  "10" ; 
var_dump ( ++ $stringInt ) ;  // int(11) 
var_dump ( -- $stringInt ) ;  // int(9) 
$stringFloat  =  "5.7" ; 
var_dump ( ++ $stringFloat ) ;  // float(6.7) 
var_dump ( -- $stringFloat ) ;  // float(4.7)
```
また、nullに対する処理は何も実行されません。
空文字をインクリメントすると文字列"1"になります。

``` php
$n = null;
++$n;
var_dump($n); // int(1)
 
$s1 = $s2 = "";
var_dump(++$s1, ++$s1, --$s2, --$s2);
/* this results in
string(1) "1"
int(2)
int(-1)
int(-2)
*/
```

8.3以降では、以下のようになります。

nullに対するデクリメント
→ 警告 + nullのまま
``` php
$n = null;
--$n; // Warning: Decrement on type null has no effect, this will change in the next major version of PHP
var_dump($n); // NULL
```

falseに対するインクリメント／デクリメント
→ 警告 + falseのまま
``` php
$false = false;
--$false; // Warning: Decrement on type bool has no effect, this will change in the next major version of PHP
var_dump($false); // bool(false)
++$false; // Warning: Increment on type bool has no effect, this will change in the next major version of PHP
var_dump($false); // bool(false)
```

trueに対するインクリメント／デクリメント
→ 警告 + falseのまま
``` php
$true = true;
--$true; // Warning: Decrement on type bool has no effect, this will change in the next major version of PHP
var_dump($true); // bool(true)
++$true; // Warning: Increment on type bool has no effect, this will change in the next major version of PHP
var_dump($true); // bool(true)
```

数値以外の文字列でのデクリメント演算子
→ 非推奨


``` php
$empty = "";
--$empty // Deprecated: Decrement on empty string is deprecated as non-numeric
var_dump($empty); // int(-1)
 
$s = "foo";
--$s; // Deprecated: Decrement on non-numeric string has no effect and is deprecated
var_dump($s); // string(3) "foo"
```

英数字ではない文字列に対するインクリメントも非推奨になります。

文字列に対するインクリメントはstr_increment、デクリメント処理はstr_decrement を仕様することになります。


## [Use exceptions by default in SQLite3 extension](https://wiki.php.net/rfc/sqlite3_exceptions)

SQLite3拡張モジュールは、デフォルトでは例外を使わないようになっています。
しかしPDOもMySQLiもとっくにデフォルト例外に移行しているため、SQLiteも例外をデフォルトにすることにします。
``` php
var_dump($sqlite3->enableExceptions());
// PHP8.2 false
// PHP8.3 false
// PHP9.0 true
// PHP10.0 Fatal error Undefined method

$sqlite3->enableExceptions(false);
// PHP8.2 OK
// PHP8.3 Deprecated
// PHP9.0 Fatal error
```
まずPHP8.3ではenableExceptions(false)するとE_DEPRECATEDが発生します。
ただし初期値はfalseのまま変わりません。

PHP9.0ではenableExceptions(false)することができなくなり、デフォルトもtrueになります。

最後にPHP10ではenableExceptions()メソッド自体がなくなります。

## [Deprecate remains of string evaluated code assertions](https://wiki.php.net/rfc/assert-string-eval-cleanup)

assert_options()関数、および関連する定数を削除します。
``` php
assert_options(ASSERT_BAIL, 1);

// PHP8.2 エラー出ない
// PHP8.3 Deprecated
```
昔はassert()の引数$assertionに文字列を渡すとevalされて評価されていたのですが、この使い方はPHP7.2で非推奨となりPHP8.0で削除されました。
しかし機能自体は削除されたものの、関連するini設定が一部残っていました。

またassert_options()は元よりini_set()を使えとドキュメントに書かれているくらいだったのですが、正式に非推奨となります。

## [Deprecate functions with overloaded signatures](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
標準関数におけるシグネチャのオーバーロードを廃止します。
なお、ここで言うオーバーロードはPHPのオーバーロードではなく、一般的な意味のオーバーロードです。
どうして同じ言葉にしてしまったのか。

例としてDatePeriod::__construct()はシグネチャが3種類もあります。
``` php
class DatePeriod
{
    public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface $end, int $options = 0) {}
    public function __construct(DateTimeInterface $start, DateInterval $interval, int $recurrences, int $options = 0) {}
    public function __construct(string $isostr, int $options = 0) {}
}
```
このうち、一番上のシグネチャだけを残します。
その他のシグネチャはPHP8.3でDeprecateとし、PHP9で削除します。

```php
PHP9
class DatePeriod
{
    public function __construct(DateTimeInterface $start, DateInterval $interval, DateTimeInterface|int $end, int $options = 0) {}
    public static function createFromISO8601String(string $specification, int $options = 0): static {}
}
```
削除されるシグネチャのうち、今後も必要そうなものには代替関数なりメソッドなりが用意されます。

他にもたくさんの関数やメソッドについての提案があり、それぞれ受理されたり却下されたりしています。

## [Deprecations for PHP 8.3](https://wiki.php.net/rfc/deprecations_php_8_3)
- mb_strimwidth()
    - 指定した幅で文字列を丸める関数ですが、負の値を与えられなれなくなります。
- 定数 NumberFormatter::TYPE_CURRENCY の削除
- 定数MT_RAND_PHPの削除


以上です。