owned this note
owned this note
Published
Linked with GitHub
PHPerのための「PHP8.2のリリース」を祝い合うPHP TechCafe
=================
# PHPer's NEWS
::: info
**[PHPerKaigi 2023 トーク採択通知実施!!](https://fortee.jp/phperkaigi-2023/proposal/all)**
みなさん、採択されましたか??
:::
::: info
**[PHPのカレンダー | Advent Calendar 2022 - Qiita](https://qiita.com/advent-calendar/2022/php)**
PHPerの皆様におかれましては、もちろんチェックしていますよね?
:::
::: info
**[PHP Core Roundup #8](https://thephp.foundation/blog/2022/11/30/php-core-roundup-8/)**
:::
::: info
**[ちょうぜつソフトウェア設計入門――PHPで理解するオブジェクト指向の活用 発売](https://www.amazon.co.jp/dp/B0BNH1J2W2)**
[#ちょうぜつ本](https://twitter.com/tanakahisateru/status/1602342440379117568?s=20&t=8XLKJIk79YChzjj_gn3GvQ) で感想をつぶやこう!
:::
::: info
**[商用でも無償の改変・利用権付与するPHP教材公開-PHP技術者認定機構](https://news.mynavi.jp/techplus/article/20221206-2530692/)**
:::
::: info
**[PhpStorm 2022.3 リリース](https://www.jetbrains.com/phpstorm/whatsnew/2022-3/)**
Array shapes アノテーション対応 便利そう!
:::
::: info
**[2022/11/26 PSR-20 ClockInterface is accepted!!](https://www.php-fig.org/psr/psr-20/)**
まだ今いち使い所を見いだせていない…
:::
::: info
**[【PHP8.2】PHPの乱数がすごい改善される](https://qiita.com/rana_kualu/items/ab82a9c6dbd2e7c6edd0)**
PHP8.2の話
Tech Cafeの次回テーマで掘り下げられたときに使えるかも?
:::
::: info
**[CLIツールを簡単に作れる、Console](https://qiita.com/ippey_s/items/6ef5d22e455bf8e78c17)**
PHPでCLIツールを作りたい需要があるのか?
という疑問はあるものの、覚えてたら楽にCLIを作れそう
:::
::: info
**[PHP/Laravelとクリーンアーキテクチャでマイクロサービスを作っている話](https://qiita.com/t-sato-adv/items/186e646545d2ad92cd15)**
やっぱテストって大事だなぁ……
:::
::: info
**[PHP技術者認定機構、PHP8教材集「PHP Open Textbook」を無償公開](https://www.imagazine.co.jp/php-open-textbook/)**
:::
::: info
**[デジタル庁、ウェブアクセシビリティに関する初心者向けガイドブックを公開](https://www.digital.go.jp/resources/introduction-to-web-accessibility-guidebook/)**
:::
# 特集:「PHP8.2のリリース」を祝い合う
* 参考資料
* [PHPerのための「PHP8.2の新機能を語り合う」PHP TechCafe](https://hackmd.io/lXd5EtZZSSmiJahrGr3zBg)
* [PHP8.2 php.net](https://www.php.net/releases/8.2/ja.php)
## [Readonly classes](https://wiki.php.net/rfc/readonly_classes)
```php=
readonly class Foo {
public int $bar;
public function __construct() {
$this->bar = 1;
}
}
$foo = new Foo();
$foo->bar = 2; // Fatal Error: プロパティの上書きはダメ
$foo->baz = 1; // Fatal Error: もちろん動的プロパティもダメ
```
Deprecate dynamic properties(動的プロパティの廃止)によって追加されるアトリビュート `#[AllowDynamicProperties]` はエラーになる
```php=
#[AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
```
* プロパティへの型宣言は必須
* staticなプロパティは不可
* readonlyなクラスを継承して上書き可能なクラスを定義するのはNG
* 上書き可能なクラスを継承してreadonlyなクラスを定義するのもNG
不変な値オブジェクトとして利用可能?
## [Disjunctive Normal Form Types](https://wiki.php.net/rfc/dnf_types)
DNF 型を使うと、[union 型](https://www.php.net/manual/ja/language.types.type-system.php#language.types.type-system.composite.union) と [交差型](https://www.php.net/manual/ja/language.types.type-system.php#language.types.type-system.composite.intersection) を組み合わせることができます。
これらを組み合わせるときは、交差型は括弧で囲まなければいけません。
### PHP8.2 以前
```php=
class Foo {
public function bar(mixed $entity) {
if ((($entity instanceof A) && ($entity instanceof B)) || ($entity === null)) {
return $entity;
}
throw new Exception('Invalid entity');
}
}
```
### PHP8.2 以降
```php=
class Foo {
public function bar((A&B)|null $entity) {
return $entity;
}
}
```
## [Add true type](https://wiki.php.net/rfc/true-type)
型宣言が許可されている全ての場所において、true型が使用可能になる。
```php=
class Truthy {
public true $truthy = true;
public function foo(true $v): true { /* ... */ }
}
```
- false型が追加されたのでtrue型も一緒に追加
- trueだけを返す関数があるので、そういった関数の戻り値をチェックしたいときに使える?
- エラー時にfalseを返す関数を考えると、型チェックでExceptionを出せる……?
## [Allow null and false as stand-alone types](https://wiki.php.net/rfc/null-false-standalone-types)
型宣言が許される位置全てにおいて、false型とnull型が使用可能になる。
```php=
class Nil {
public null $nil = null;
public function foo(null $v): null { /* ... */ *}
}
class Falsy {
public false $nil = false;
public function foo(false $v): false { /* ... */ *}
}
```
- もともとはUNION型のみで利用可能だったが、今回から単独で使用できるように
- 静的解析で便利になる?
- 関数がエラーであることを示すために、返り値として歴史的にfalseが使われてきました
- レガシーシステムと相性がいい?
- あまりfalseのみ、nullのみの型チェックをしたい機会が思いつかない……
* [Expand deprecation notice scope for partially supported callables](https://wiki.php.net/rfc/partially-supported-callables-expand-deprecation-notices)
## [Random Extension 5.x](https://wiki.php.net/rfc/rng_extension)
PHPで乱数を使用する関数の実装に問題があることが以前から指摘されていた。
* メルセンヌ・ツイスター(疑似乱数ジェネレーター)の実装が壊れている。
* メルセンヌ・ツイスターの状態は PHP のグローバル領域に暗黙的に格納されるため、外部の影響を受けて乱数が乱れたりといった問題が発生する。
* `shuffle()`, `str_shuffle()`, `array_rand()`,`random_int()`といった組み込み関数ではメルセンヌ・ツイスターが乱数ソースとして使用されるため、暗号的に安全な乱数が必要な場合は危険。
* PHP での乱数の実装は、歴史的な理由から標準モジュール内に散らばっている。
* そもそも乱数生成のコストが高い。
そのためさまざまなランダム化メソッドを提供する単一のクラス、`Random\Randomizer`クラスが追加されます。
```php=
__construct(Random\Engine $engine = new Random\Engine\Secure())
getInt(): int // replaces mt_rand()
getInt(int $min, int $max) // replaces mt_rand() and random_int()
getBytes(int length): string // replaces random_bytes()
shuffleArray(array $array): array // replaces shuffle()
shuffleString(string $string): string // replaces str_shuffle()
__serialize(): array
__unserialize(array $data): void
```
現状のPHPの乱数は危険なようなので、使用する際は注意した方がよさそう。
参考)
* [乱数と向き合う 〜PHP によるゲームアプリケーションの実装と運用〜/ColoplTech-04-02](https://speakerdeck.com/colopl/colopltech-04-02)
* [いちユーザーが PHP に新機能を追加するまで - Random Extension 5.x](https://fortee.jp/phpcon-2022/proposal/c39b64af-506c-4b05-996c-bdb6df21ddc6)
* [PHP の乱数実装がグダグダな話](https://zenn.dev/zeriyoshi/articles/abd808d1c6d31b)
## [Random Extension Improvement](https://wiki.php.net/rfc/random_extension_improvement)
上で記載した`Random Extension`の補足。
後に発覚した問題の修正が行われています。
* `array_rand()`に相当するメソッドの追加(`pickArrayKeys()`)
* `shuffleString()`をより適切な名称である`shuffleBytes()`に変更
など
```php=
$engine = new Random\Engine\Mt19937(1234, MT_RAND_PHP);
$randomizer = new Random\Randomizer($engine);
$array = ['foo', 'bar', 'baz'];
$randomizer->pickArrayKeys($array, 1)[0]; // (int) 0
$randomizer->shuffleBytes('abc');
```
## [Constants in Traits](https://wiki.php.net/rfc/constants_in_traits)
トレイトで定数を定義できるようになる
```php=
trait Foo {
public const FLAG_1 = 1;
protected const FLAG_2 = 2;
private const FLAG_3 = 2;
public function doFoo(int $flags): void {
if ($flags & self::FLAG_1) {
echo 'Got flag 1';
}
if ($flags & self::FLAG_2) {
echo 'Got flag 2';
}
if ($flags & self::FLAG_3) {
echo 'Got flag 3';
}
}
}
```
* これまではトレイトをクラスかインターフェースで定義された定数を利用するしかなかった
* クラスに定数を定義する場合の難点
* トレイトの実装がクラスに漏れてしまっている
* トレイトを利用するための定数定義がトレイト自体によって提供されていない
* インターフェースで定数が定義されている場合の難点
* インターフェース側で定義していることは実装上自然
* しかし、トレイトを利用するクラスが特定のインターフェースを実装していることを判別することはできない
* クラスにもインターフェースにも依存しない状態でトレイトを利用できるようになれば、モジュールとしてのトレイトの完全性が向上することからこの提案が出された
* 使いやすさだけが向上した素敵なアップデート
* もともとトレイトで定数が利用できなかった理由は[特にない模様](https://externals.io/message/118039#118059)
### 前回の PHP TechCafe で話していたこと
* 単純な機能改善に見えるが実は最初の投票では半々くらいだった
* そもそもTraitが嫌われていたことが原因
* その後、Nikita さんやその他の有識者の議論によって可決
* すべてのNoコメントに丁寧に回答して可決に持って行った
* PHP Conference 2022 でもトークされた
* [導入から 10 年、PHP の trait は滅びるべきなのか ーーその適切な使いどころと弱点、将来について ](https://fortee.jp/phpcon-2022/proposal/b85ca73f-6383-4485-b2ae-4ec3e0913e72)
## [Deprecate dynamic properties](https://wiki.php.net/rfc/deprecate_dynamic_properties)
未定義のプロパティに値を代入した時の動作が変更されます。
- v8.1まで: プロパティが生成される
- v8.2: Warningが発生するが、プロパティが生成される。
- v9.0: 例外が発生する。
意図しないプロパティが生成されるとバグの原因になりそうなのでこの変更は良いと思います。
(静的解析で検知できるから変更しなくてもいいじゃんという意見もあったようですが...)
```php=
class User {
public $name;
}
$user = new User;
// Assigns declared property User::$name.
$user->name = "foo";
// Oops, a typo:
$user->nane = "foo";
// PHP <= 8.1: Silently creates dynamic $user->nane property.
// PHP 8.2: Raises deprecation warning, still creates dynamic property.
// PHP 9.0: Throws Error exception.
```
ただし、`#[AllowDynamicProperties]` を付けることで例外を回避できます。
```php
#[AllowDynamicProperties]
class User() {}
$user = new User();
$user->foo = 'bar';
```
## [Deprecate ${} string interpolation](https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation)
文字列で変数展開する際の ${} 表記の挙動を非推奨にする
```php=
$foo = "bar";
$bar = "foo";
var_dump("$foo"); // OK
var_dump("{$foo}"); // OK
var_dump("${foo}"); // 非推奨: 文字列での ${} の使用は非推奨
var_dump("${$foo}"); // 非推奨: 文字列での ${} (可変変数) の使用は非推奨
```
* ${} の表記を許容しているがゆえに動作が理解しづらくなっている
パターン1:配列
```php=
$foo = ['bar' => 'bar'];
var_dump("$foo[bar]");
var_dump("{$foo['bar']}");
var_dump("${foo['bar']}");
// すべて "bar" が出力されるが最後の挙動も "bar" になることが理解しづらい
// しかも、可変変数は利用できない
```
パターン2:オブジェクトのプロパティ
```php=
$foo = (object)['bar' => 'bar'];
var_dump("$foo->bar");
var_dump("{$foo->bar}");
// このパターンでは ${} パターンが利用できない
```
パターン3:メソッドコール
```php=
class Foo {
public function bar() {
return 'bar';
}
}
$foo = new Foo();
var_dump("{$foo->bar()}");
// メソッドコールで許容されているのは上記のみ
```
* ${} が許容されていることによって、どの挙動が正しく動作するのかが分かりにくくなっている
* PHP8.2 で非推奨
* PHP9.0 でエラーになる
### 前回の PHP TechCafe で話していたこと
* 調査しやすそうな内容なので特に問題なさそう
* `==` の挙動変更に比べれば全然軽微
## [Deprecate and remove utf8_decode() and utf8_encode()](https://wiki.php.net/rfc/remove_utf8_decode_and_utf8_encode)
utf8_decodeとutf8_encodeが非推奨になりました。
任意の文字列をUTF-8にエンコードデコードできるかと思いきや、変換相手はLatin 1固定です。
誤解を招く関数名であり、容易に間違った実装をしてしまいがちです。
```php=
function isUTF8($string) {
return (utf8_encode(utf8_decode($string)) == $string);
}
```
かわりに[mb_convert_encoding](https://www.php.net/manual/ja/function.mb-convert-encoding.php)を使いましょう。
## [Fetch properties of enums in const expressions](https://wiki.php.net/rfc/fetch_property_in_const_expressions)
enum:列挙型。性別とか独自の型を定義する時に便利。可読性の向上とタイプヒントで使うイメージ。
enumのcaseはオブジェクトの為、連想配列のキーに使うことができなかった。
`->`を使って定数式の中でenumのプロパティを取得できるようになった。
配列のキーなど、enumオブジェクトを使用できない部分でenumのnameやプロパティを取得できるようにしたいことが出発点。
```php=
enum A: string {
case B = 'B';
// 現在コレはダメ
const C = [self::B->value => self::B];
}
enum E: string {
case Foo = 'foo';
}
// 'Foo'が取れる
const C = E::Foo->name;
```
```php=
enum E {
case Foo;
}
const C = E::Foo->c; // 未定義のプロパティへのアクセスはnullになります(※php9でerror扱い)
// 警告: Attempt to read property "e" on null
const C = (null)->e; // NULL
// 警告: Attempt to read property "" on null
// Fatal error: Uncaught Error: Object of class A could not be converted to string
const C = (null)->{new A};
// Error: Fetching properties on non-enums in constant expressions is not allowed
const C = (new A)->foo;
```
### 前回の PHP TechCafe で話していたこと
* イマイチ使いどころが分からない
* constにenumのオブジェクトを設定したいケースとしては、ライブラリの作者などが安全なコードを書くときに需要がある
* voteが割れている理由として、全員がライブラリ作者ではないため利用用途が分からないことも考えられる(12/1 追記)。
[issue](https://github.com/php/php-src/issues/8344)
[実装](https://github.com/php/php-src/pull/8625)
## [Locale-independent case conversion](https://wiki.php.net/rfc/strtolower-ascii)
strtolowerとstrtoupperはロケールの影響を受けなくなります。
```php=
strtolower(string $string):string
```
マニュアルには『たとえばデフォルトの「C」ロケールである場合は、 A ウムラウト (Ä) のような文字は変換されません。』とありますが、そもそもロケールを変えたら変更できる、という挙動のほうがわかりにくくて危険です。
そのため、これらの関数はロケールの影響を受けないようにします。
### 前回の PHP TechCafe で話していたこと
日本語のみ使用可能な、[mb-convert-kana](https://www.php.net/manual/ja/function.mb-convert-kana.php)も以前のPHPTechCafeで話題にでました。
## [Make the iterator_*() family accept all iterables](https://wiki.php.net/rfc/iterator_xyz_accept_array)
iterator:反復可能なオブジェクト。foreachに渡せる。
iterator_to_arrayとiterator_countにiterable型の変数を渡せるようなる。
```php=
function before(iterable $foo) {
if (!is_array($foo)) {
$foo = iterator_to_array($foo);
}
return array_map(strlen(...), $foo);
}
function after(iterable $foo) {
$foo = iterator_to_array($foo); ★is_arrayのチェックが不要になる
return array_map(strlen(...), $foo);
}
```
* iterator_to_array
```php=
iterator_to_array($array, true) == $array
iterator_to_array($array, false) == array_values($array)
```
* iterator_count
```php=
iterator_count($array) == count($array)
```
変更点
[iterator_to_array](https://www.php.net/manual/ja/function.iterator-to-array.php)
```php=
- iterator_to_array(Traversable $iterator, bool $preserve_keys = true): array
+ iterator_to_array(iterable $iterator, bool $preserve_keys = true): array
```
[iterator_to_count](https://www.php.net/manual/ja/function.iterator-count.php)
```php=
- iterator_count(Traversable $iterator): int
+ iterator_count(iterable $iterator): int
```
引数の型がTraversableのためiterable型の変数を渡すとエラーとなっているが
8.2からは`Traversable|array`を渡せばよくなる。
https://github.com/php/php-src/pull/8819
- メソッド名に反してiterable型を受け付けなかったが、命名通りの動作をするようになった。
- イテレータをarrayに変換する独自メソッドを書く必要がなくなった。
- 後方互換性が保たれている。
## [MySQLi Execute Query](https://wiki.php.net/rfc/mysqli_execute_query)
MySQLiに新しい関数mysqli_execute_queryを追加
満場一致で採用された
### mysqli_execute_queryとは
下記3つの関数をまとめた関数です。
1. mysqli_prepare()
2. mysqli_execute()
3. mysqli_stmt_get_result()
### 使い方
今まで(PHP8.1以降)
```php=
$statement = $db->prepare('SELECT * FROM user WHERE name LIKE ? AND type_id IN (?, ?)');
$statement->execute([$name, $type1, $type2]);
foreach ($statement->get_result() as $row) {
print_r($row);
}
```
これから
```php=
foreach ($db->execute_query('SELECT * FROM user WHERE name LIKE ? AND type_id IN (?, ?)', [$name, $type1, $type2]) as $row) {
print_r($row);
}
```
- [PDO](https://www.php.net/manual/ja/class.pdo.php)にはないのだろうか
## [Remove support for libmysql from mysqli](https://wiki.php.net/rfc/mysqli_support_for_libmysql)
MySQLのモジュールはmysqlndとlibmysqlの2種類が存在します。
このうちlibmysqlを削除しようという話
これも満場一致で採用
理由
- mysqlndはPHP5.4以降ずっとデフォルト
- PHP同梱、libmysqlに存在しない機能がある、パフォーマンス上有利
- libmysqlを選ぶ利点がほぼない
## [Redacting parameters in back traces](https://wiki.php.net/rfc/redact_parameters_in_back_traces)
* スタックトレースに引数を出力しないようにするアトリビュート
* DBの接続情報などが画面に表示されることによる情報流出を防ぐ
* 本番ではそもそも出力しないようにしたいが...
* 比較的新しい機能"アトリビュート"がバンバン使われていく感じがすごい
```php=
function test(
$foo,
#[\SensitiveParameter] $bar,
$baz
) {
throw new \Exception('Error');
}
```
## [Deprecate partially supported callables](https://wiki.php.net/rfc/deprecate_partially_supported_callables)
call_user_func(\$callable_func)では実行できるが、$callable_func()では実行できない構文が将来的に廃止されます。
例:
```php=
"self::method"
"parent::method"
"static::method"
["self", "method"]
["parent", "method"]
["static", "method"]
["Foo", "Bar::method"]
[new Foo, "Bar::method"]
```
v8.2でこれらの構文が非推奨になり、v9.0で廃止されます。こちらは32対0で廃止が可決されました。
## [Expand deprecation notice scope for partially supported callables](https://wiki.php.net/rfc/partially-supported-callables-expand-deprecation-notices)