# PHP8.1 の新機能について語り合う PHP TechCafe
## PHPers News
::: info
[セキュアコーディングのeラーニング「KENRO」がPHPに対応。株式会社PR TIMESでの導入も決定](https://prtimes.jp/main/html/rd/p/000000030.000027502.html)
:::
コメント:脆弱性対策の学習によさそう。
::: info
[Laravel 8 + Livewire でメールフォームを作るチュートリアル](https://zenn.dev/hdmt/articles/a587636cf9e2a2)
:::
コメント:バージョン2からようやく使えるようになりました。煩わしいJavaScriptでの非同期処理の実装から解放されます。
::: info
[Laravel 9 Release Date](https://blog.laravel.com/laravel-9-release-date)
:::
コメント:Symfony のアップデートに追従する形のアップデートとなるとのこと。考え方として分かりやすくて良いかと思いました。
::: info
[PHPバージョンアップけもの道](https://speakerdeck.com/uzulla/phpbaziyonatupukemofalsedao)
:::
コメント:非常に内容が豊富で参考になります。
::: info
[PHP8.1でenumが使えるようになる!](https://qiita.com/kudota/items/419abc212de4be74fca6)
:::
::: info
[What's new in PHP 8.1](https://stitcher.io/blog/new-in-php-81)
:::
::: info
[Readonly properties are coming in PHP 8.1](https://www.amitmerchant.com/readonly-properties-are-coming-in-php-81/)
:::
::: info
[Webサービスで使用される開発言語 JavaScriptとPHPが1位、Pythonは3位](https://ledge.ai/en-japan-web-development-ranking/)
:::
コメント:Javaが5位と以外と低かったのが意外。各言語全体でのFW利用例でもLaravelが4位と健闘していたのはさすがだなと思った。
::: info
[【Laravel6.x〜】デバッグするならdd()?いやいや、ddd()を使おう!!](https://zenn.dev/shimotaroo/articles/738d8e01796bc9)
:::
コメント:知らなかった。ddって何の略だろう?Debug Dumpかな?
## PHP8.1 新機能
### ★Enums
複数の定数をひとつにまとめることができる`列挙型`がサポート
(2010年頃から提案されていたがようやくリリース)
[なぜPHPにはEnumがないのか](https://speakerdeck.com/takayukifujisawa/nazephpnihaenumganaifalseka?slide=32)
以下のように定数を定義してまとめておくことができる
``` php=
enum CardsMark
{
case DIAMOND;
case HEART;
case SPADE;
case CLOVER;
}
```
上記のように列挙しておくことで、クラス内のプロパティ設定時などで挿入される値を制御できます。
``` php=
class Card
{
public function __construct(
public CardsMark $mark,
) {}
}
$card = new Card(CardsMark::DIAMOND);
```
Enumにメソッドを定義することも可能です
(静的メソッドも可能)
``` php=
enum CardsMark
{
case DIAMOND;
case HEART;
case SPADE;
case CLOVER;
public function mark(): string
{
return match($this)
{
CardsMark::DIAMOND => '♢',
CardsMark::HEART => '♡',
CardsMark::SPADE => '♤',
CardsMark::CLOVER => '♧',
};
}
}
```
Enumのメソッドは以下のように利用できます
``` php=
$card = CardsMark::DIAMOND;
$card->mark(); // return '♢'
```
列挙型はインターフェースを実装することも可能です
``` php=
interface HasMark
{
public function mark(): string;
}
enum CardsMark implements HasMark
{
case DIAMOND;
case HEART;
case SPADE;
case CLOVER;
public function mark(): string
{ ... }
}
```
**社内からのコメント**
より意図が伝わりやすい実装ができるようになると思います
その他詳しい内容は以下
[Enums in PHP 8.1](https://stitcher.io/blog/php-enums)
### Fibers
- PHPで非同期処理を実装するための仕組みが導入されます。
- PHPはシングルスレッドで実行されるため、実行に時間がかかる処理が複数あれば、その分だけ実行時間が伸びる形になります。
- 処理時間を短縮するために、重い処理を並列に実行したくなるところですが、これまでは例えば``exec``関数で別のプロセスを立ち上げて実行するなど、擬似的に非同期処理を実装していました。
### Performance improvements
ZendEngineのパフォーマンス向上。
いくつかの調整を行うことで、ベンチマークのパフォーマンスが20%向上します。
[Zend Engine Performance Improvements](https://wiki.php.net/rfc/performanceimprovements)
### Array unpacking with string keys
PHP 7.4 より配列のアンパックが許容されるようになったため、以下のような書き方が可能でした。
```php=
$array = [...$array1, ...$array2];
//以前は以下のように記載されていました
$array = array_merge($array1, $array2);
```
PHP 8.0 から名前付き引数がサポートされるようになったため、PHP 8.1 では文字列のキーでも配列のマージができるようになりました
```php=
$array1 = ["a" => 1];
$array2 = ["a" => 2];
$array = ["a" => 0, ...$array1, ...$array2];
var_dump($array); // ["a" => 2]
```
### ★`new` in initializers
- コンストラクタや関数の引数のデフォルト値でオブジェクトをNewできるようになりました。
- 従来は「一度nullにしておいて、nullだったらNewする」みたいな書き方で、冗長でありコードの意図がわかりにくかったのですが、これが解消されることになります。
- v8.1以降
```php=
class Test {
public function __construct(
private Logger $logger = new NullLogger,
) {}
}
```
- v8.0以前
```php=
class Test {
private Logger $logger;
public function __construct(
?Logger $logger = null,
) {
$this->logger = $logger ?? new NullLogger;
}
}
```
### ★Readonly properties
宣言後、一度だけ初期化できるプロパティができます。
ただし、型つきのプロパティのみに利用可能です。(型がないプロパティの初期値はnullになってしまうため)
```php
class Test {
public readonly string $prop;
public function __construct(string $prop) {
$this->prop = $prop; // 最初の初期化
}
}
$test = new Test("foobar");
// 読み込み(OK)
var_dump($test->prop); // string(6) "foobar"
// 上書き(NG)
$test->prop = "foobar";
// Error: Cannot modify readonly property Test::$prop
```
**社内からのコメント**
PHP8.1の変更の中で、使い所の多そうなものの一つ。
元から予約語の final ではあかんかったんかな…というのは多少気になります
[Readonly properties 2.0](https://wiki.php.net/rfc/readonly_properties_v2)
### First-class callable syntax
callable な関数に `...` という引数を与えることでクロージャが作成可能。
引数は名前付き引数で指定できるようになる。
```php=
function foo(int $a, int $b) { /* … */ }
$foo = foo(...);
$foo(a: 1, b: 2);
```
詳しくは理解できませんでしたが、このような書き方ができることで静的解析に役立つとのこと。(引数の呼び出しの可視性が向上することが要因)
### ★Pure intersection types
- ``交差型 ``と呼ばれるもの。
- 型``A``であり、かつ型``B``を表現する際``A&B``と記述します。
- 逆に、``A``または``B``を表現する際は、v8.0で導入された[UnionType(共用型)](https://wiki.php.net/rfc/union_types_v2)を用います。
- Interfaceを多用している場合などに有用で、例えば、``Traversable``と``Countable``の両方の振る舞いを持つことを保証するには、両方を継承した新しいInterfaceを定義する必要があり、管理が難しくなる傾向がありました。
```php=
interface TraversableCountableIterator extends Traversable, Countable {}
```
- 交差型を用いることで、新しくInterfaceを作る必要がなくなります。
```php=
class A {
private Traversable&Countable $countableIterator;
public function setIterator(Traversable&Countable $countableIterator): void {
$this->countableIterator = $countableIterator;
}
public function getIterator(): Traversable&Countable {
return $this->countableIterator;
}
}
```
**社内からのコメント**
型の AND ができるようになる。ふつうの静的型付きの言語でもあまりない機能。(どちらかといえば動的型付けの言語に拡張で導入される例が多いような気もします。)
多重継承が前提の機能なのでいきなり使うようなものではないですが、使えると便利です。
正直、ここまでやるならHaskell系の言語やScalaとか、多重継承が普通にできる設計かつ強い静的型付きの言語を使うほうがいい気もしますが、Typescriptでも導入されているので、動的型付きの言語ならではの使いどころがあるのでしょうか?
### ★New `never` type
常に例外を返したり、`exit()`で処理を終わってしまうような関数の返り値として`never`型を描けるようになります。
型の名前は`never`と`noreturn`の2案が提案されていましたが、`never`が採用されました。
```php
function redirect(string $uri): never {
header('Location: ' . $uri);
exit();
}
function redirectToLoginPage(): never {
redirect('/login');
}
```
### New `array_is_list` function
配列のキーが0番目からスタートする配列なのか、連想配列なのかを判定する組み込み関数が追加されました。
```php=
$list = ["a", "b", "c"];
array_is_list($list); // true
$notAList = [1 => "a", 2 => "b", 3 => "c"];
array_is_list($notAList); // false
$alsoNotAList = ["a" => "a", "b" => "b", "c" => "c"];
array_is_list($alsoNotAList); // false
```
実は結構議論があった模様
[RFC: Discussion](https://wiki.php.net/rfc/is_list#discussion)
### Final class constants
- 継承したクラスの定数をオーバーライドすることを禁止することができるようになります。
- これまでは、子クラス側で定数を書き換えることができるため、親クラスの設計者の意図に反して「一定ではない」状態が起こりうる状態でした。
```php=
class Foo
{
final public const X = "foo";
}
class Bar extends Foo
{
public const X = "bar";
}
// Fatal error: Bar::X cannot override final constant Foo::X
```
### New `fsync` function
ファイルの書き込み内容を、メモリから実際にファイルへ書き出す。
(`fflush()`と同じだが、内部的に少し異なる。)
### ★Explicit octal integer literal notation
実は `16 === 016` は FALSE になる。
※016 が 8進数と解釈されてしまうため
8進数リテラルを表す接頭辞0oがサポートされるようになり、8進数であることが視覚的にわかりやすくなります。
※0o は "ゼロ オー" です
```
0o16 === 14; // true
0o123 === 83; // true
0O16 === 14; // true
0O123 === 83; // true
016 === 0o16; // true
016 === 0O16; // true
```
## BreakingChange
### ★Restrict `$GLOBALS` usage
- `$GLOBALS`は特殊な変数で、下記のような記述をすると、他の変数に対してアクセスすることができます。
```php=
$a = 1 ;
$GLOBALS ['a'] = 2 ;
var_dump($a); // int(2)
```
- このような使われ方をすることは少ない割に、この機能を実現することで、PHPの全ての配列操作にパフォーマンスに影響しています。
- このRFCでは、一部の``$GLOBALS``の使用方法を制限することで、パフォーマンス改善を行った、というものです。
- v8.1以降、エラーになる記述は以下の通りです。
```php=
// Generates compile-time error:
$GLOBALS = [];
$GLOBALS += [];
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);
```
### Resource to object migrations
リソースと呼ばれる特殊な変数を、オブジェクトに置き換えるPHP開発の長期戦略に向けた改修です。
いくつかの関数がこの戦略のために書き換えられたようです。
詳しくは[こちら](https://php.watch/articles/resource-object)
### ★Deprecate passing null to non-nullable arguments of internal functions
PHP8 で導入された `str_contains` の第二引数に null を指定した場合、非推奨になります。
※現在はTRUEが返されている
※PHP9 では警告になる
```php=
// PHP8.1 では非推奨、PHP9 では警告が出力される
str_contains("string", null);
```
### Deprecate Autovivification on `false`
- ``false``の値を持つ変数から配列の自動生成をしないようにする、という変更です。
- 何言ってるかわからないかもしれませんが、従来は下記のような記述で、それぞれ配列を生成することができました。
```php=
// From undefined
$arr[] = 'some value';
$arr['doesNotExist'][] = 2;
// From false
$arr = false;
$arr[] = 2;
// From null
$arr = null;
$arr[] = 2;
```
- 上記の例のうち、``From false``からの配列の自動生成を禁止しようというものです。
- 背景としては以下の2つの点が指摘されています。
- v8.0以前も、``true``、``0``、``''``が代入されている変数からの配列の自動生成はできないため、一貫性がない。
- PHPの関数は、処理成功時に配列を、失敗時に``false``を返すものなどがあり、``false``からの配列の自動生成を禁止することでバグを防ぐことができる。
- なお、この変更は、v8.1で``非推奨``となり、v9.0で``致命的エラー``になります。
## 参考URL
[What's new in PHP 8.1](https://stitcher.io/blog/new-in-php-81)
[リリーススケジュール](https://wiki.php.net/todo/php81)