dockerを使用してさくっとPHP8を触ってみた

# dockerを使用してさくっとPHP8を触ってみた

[PARONYM Advent Calendar 2020 - Qiita](https://qiita.com/advent-calendar/2020/paronym) の2日目です。

## はじめに

2020/11/26にPHP8がリリースされました。
ということで、自身のローカル環境をなるべく汚さずにPHP8の新機能を触ってみることにしました。

## 前提条件

- dockerがインストールされていること

## きっかけ

PHP8の新機能を触ってみたい！でも自身のPCを汚したくない！
PHP8を試す環境として、当然ながらプロダクトの開発環境で試すわけにはいきません。（本番環境なんて以ての外）
となるとローカルPCで試すのが妥当ですが、インストールや設定の手間、もしもの時のバージョンの切り替えを考えると一気に面倒になります。
今回はPHP8の機能を触るのが目的であり速度性能を試すわけではないため、さくっとdockerを使用してPHP8を実行することにしました。

## コード

今回試したコードは全てGithubにコミットしています。
https://github.com/y-kakinuma-paronym/php8-docker

## dockerでPHP8の実行

```bash
$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php {実行したいファイル}
```

- PHPの実行後、コンテナは削除する : `--rm`
- カレントディレクトリをコンテナの/appにマウント : ``-v `pwd`:/app``
- コンテナ内の作業用ディレクトリは/app : `-w /app`
- dockerイメージは `php:8.0-rc` を使用
  - [php Tags - Docker Hub](https://hub.docker.com/_/php?tab=tags&page=1&name=8.0-rc-&ordering=last_updated)
- php実行 : `php {実行したいファイル}`

毎回コマンドを打つのは面倒なのでシェルスクリプトを作成しておくと楽です。

```shell:exec_php.php
#!/bin/sh
docker run --rm -v `pwd`:/app -w /app php:8.0-rc php $1

# ./exec_php.sh match_expression.php   
```

## PHP8新機能

PHP8の新機能はここから抜粋しています。
[PHP: PHP 8.0.0 Release Announcement](https://www.php.net/releases/8.0/en.php)

### Named arguments

引数を渡すときに名前を指定できるようになりました。
https://wiki.php.net/rfc/named_params

```php
<?php
// Named arguments
// https://wiki.php.net/rfc/named_params

$str = "<a href='test'>Test</a>";

// PHP7
// $result = htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

// PHP8
// double_encodeの引数のみをデフォルト引数から変更したい場合、引数の名前を指定する
$result = htmlspecialchars($str, double_encode: false);
echo $result . "\n"; // &lt;a href='test'&gt;Test&lt;/a&gt;

?>
```

### Constructor property promotion

コンストラクタでのプロパティ初期化が簡略化できるようになりました。
https://wiki.php.net/rfc/constructor_promotion

```php
<?php
// Constructor property promotion
// https://wiki.php.net/rfc/constructor_promotion

class Point {
    // PHP8
    public function __construct(
        public int $x = 0,
        public int $y = 1,
        public int $z = 2,
    ) {
        print "Point constructor\n";
    }

    // PHP7
    // public int $x;
    // public int $y;
    // public int $z;
    // public function __construct(
    //     int $x = 1,
    //     int $y = 2,
    //     int $z = 3
    // ) {
    //     $this->x = $x;
    //     $this->y = $y;
    //     $this->z = $z;
    // }

    public function showClassParameter() {
        print_r([$this->x, $this->y, $this->z]);
    }
}

$obj = new Point();
$obj->showClassParameter();

?>
```

### Union types

共用体型（union type)が追加されました。
https://wiki.php.net/rfc/union_types_v2

```php
<?php
declare(strict_types=1);

// int と string を型指定
$f = function (int|string $v) {
    var_dump($v);
};

$f(100); // ok
$f("abc"); // ok
$f(true); // ng
```

### Match expression

https://wiki.php.net/rfc/match_expression_v2

```php
<?php
// Match expression
// https://wiki.php.net/rfc/match_expression_v2

switch (1) {
    case 0:
        $result = "Foo\n";
        break;
    case 1:
        $result = "Bar\n";
        break;
    case 2:
        $result = "Baz\n";
        break;
}
echo $result; // Bar

/**
 * switchとの違い
 * ・一致した結果を変数に代入することが可能
 * ・一致は一つのみをサポート。breakなどは必要なし
 * ・一致は厳密な比較
 */

echo match(1) {
    0 => "Foo\n",
    1 => "Bar\n",
    2 => "Baz\n"
};
// Bar

echo match (8.0) {
    '8.0' => "Oh no!",
    8.0 => "This is what I expected",
};
// This is what I expected
// ↑厳密な比較のため

?>
```

switchとの違いについての説明はコード内に記載しています。特に注意が必要なのが、一致は厳密な比較であるところです。



### Nullsafe operator

null演算子が追加されました。
https://wiki.php.net/rfc/nullsafe_operator

```php
<?php
// Nullsafe operator
// https://wiki.php.net/rfc/nullsafe_operator

class Session
{
    public $user = null;
}

$session = new Session();

/**
 * nullsafe演算子「?」を用いることでチェーン内の1つの要素で評価が失敗すると、
 * チェーン全体の実行が中断され、nullを返す
 */
$country = $session->user?->getAddress()?->country;
var_dump($country) // null

?>
```

`$session->user?->getAddress()?->country;` 
例では↑の`user`がnullのため失敗となり、nullsafe演算子は`null`を返します。

## PHP8の所感

PHP8の目玉はなんと言ってもJITコンパイラーによる処理の高速化ですが、こういった細かい機能も実装が楽になるため嬉しい追加でした。
特にMatch expressionは、JSONを作成するためにarrayを組み立てるときに三項演算子より複雑な分岐ができるため非常に便利だと思います。

```php
$result = 1;
$json = [
    "resutl" => match($result) {
        0 => "Failed",
        1 => "Success",
        2 => "No Updated"
    },
];

echo json_encode($json); // {"resutl":"Success"}
```

Nullsafe operatorもうまく利用できれば、if文が減りコードの可読性を向上させることができそうです。

## まとめ

今回はPHP8の新規機能をDockerを使用して自身のPCをなるべく汚さずに試してみました。
今回紹介していないPHP8の新機能はまだまだありますので、公式サイトを参照してください。
[PHP: PHP 8.0.0 Release Announcement](https://www.php.net/releases/8.0/en.php)

Dockerを利用すれば、PHP8に限らず様々な環境を楽に構築できるのでこれからもどんどん利用していきたいと思います。

## 参考

- [PHP 8 をオフィシャル Docker イメージで試す - Shin x Blog](https://blog.shin1x1.com/entry/php8-on-docker)
- [php Tags - Docker Hub](https://hub.docker.com/_/php?tab=tags&page=1&name=8.0-rc-&ordering=last_updated)