Shin x Blog

PHPをメインにWebシステムを開発してます。Webシステム開発チームの技術サポートも行っています。

PHP 8 をオフィシャル Docker イメージで試す

2020 年末にリリース予定 の PHP 8 の オフィシャル Docker イメージ(library/php)が Docker Hub に公開されました。

https://hub.docker.com/_/php?tab=tags&page=1&name=8.0-rc-

イメージタグに 8.0-rc8.0-rc-fpm8.0-rc-apacheなど 8.0-rcrc-のタグが付いているものが現時点では PHP 8 のビルドとなっています。

この Docker イメージを利用して PHP 8 を試してみました。

Hello PHP 8

docker コマンドで php:8.0-rc イメージを実行するとバージョンに PHP 8.0.0alpha1となっています!

$ docker run --rm php:8.0-rc php -v
PHP 8.0.0alpha1 (cli) (built: Jun 26 2020 19:38:47) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

Apache httpd + mod_php のイメージである php:8.0-rc-apache を起動して、phpinfo() を表示してみます。

$ echo "<?php phpinfo();" > index.php
$ docker run --rm -p 8000:80 -v `pwd`:/var/www/html php:8.0-rc-apache

http://localhost:8000 にブラウザでアクセスすると PHP 8 の phpinfo() が表示されました。

f:id:shin1x1:20200628144014p:plain

新機能のお試し

PHP 8 環境ができたので、新機能を試してみましょう。

Union Types

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

型宣言で指定する型を | で複数指定できる機能です。良し悪しは別にしても、これまで厳密に1つの型にだけ限定できなかったケースが現実的にあるのでそれらに型が付けられるのは良いです。

<?php
declare(strict_types=1);

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

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

実行結果

$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php union_types.php
int(100)
string(3) "abc"

Fatal error: Uncaught TypeError: {closure}(): Argument #1 ($v) must be of type string|int, bool given, called in /app/union_types.php on line 10 and defined in /app/union_types.php:4
Stack trace:
#0 /app/union_types.php(10): {closure}(true)
#1 {main}
  thrown in /app/union_types.php on line 4

Constructor Property Promotion

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

コンストラクタ仮引数に可視性を指定することで、プロパティ宣言と引数の省略できる記法です。TypeScript や Scala などにありますね。シンプルに書けるのがうれしいところ。

なお、(1) のように最後の仮引数の後ろにカンマを付けても良いのも PHP 8 の新機能です。( PHP: rfc:trailing_comma_in_parameter_list

<?php
declare(strict_types=1);

final class User
{
    public function __construct(
        public int $id,
        private string $name, // (1)
    ){}
}

// ↓と同じ
//final class User
//{
//    public int $id;
//    private string $name;
//
//    public function __construct(
//        int $id,
//        string $name
//    ){
//        $this->id = $id;
//        $this->name = $name;
//    }
//}

var_dump(new User(1, 'Foo'));

実行結果

$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php constructor-property-promotion.php
object(User)#1 (2) {
  ["id"]=>
  int(1)
  ["name":"User":private]=>
  string(3) "Foo"
}

Attributes

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

クラスやプロパティ、メソッド(関数)などに付与できるアノテーションです。これまで PHPDoc でやりくりしていたのが言語機能としてサポートされるようになりました。リフレクションで指定された Attributes を取得して処理できます。なお、PHP の Attributes は TypeScript や Python にあるデコレータではないのでご注意を。

<?php
declare(strict_types=1);

<<PhpAttribute>>
final class Attr1 {
    public function __construct(private string $value) {}
}

final class Attr2 {
    public function __construct() {}
}

<<Attr1("Hello World")>>
<<Attr2>>
<<NotFound>>
class Foo {
}

$reflectionClass = new \ReflectionClass(Foo::class);
$attributes = $reflectionClass->getAttributes();

foreach ($attributes as $attr) {
    var_dump($attr->getName());
    var_dump($attr->getArguments());
    // newInstance() は <<PhpAttribute>> が付いたクラスで無いと Fatal Error になる
    //    var_dump($attr->newInstance());
}

実行結果

$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php attributes.php
string(5) "Attr1"
array(1) {
  [0]=>
  string(11) "Hello World"
}
string(5) "Attr2"
array(0) {
}
string(8) "NotFound"
array(0) {
}

Attribute は << >> 以外のシンタックスも検討されているので書き方は今後変わる可能性があります。

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

候補は下記です。今のところ @@が優勢っぽいですが、どうなるか。

  • @@
  • #[]
  • << >>

JIT コンパイラ

PHP 8 注目機能の1つでもある JIT コンパイラも試してみました。ここでは、下記のようにフィボナッチ数を再帰で算出するロジックを実行しました。

<?php
declare(strict_types=1);

$fib = function (int $i) use (&$fib) {
    return $i < 2 ? $i : $fib($i - 1) + $fib($i - 2);
};
var_dump($fib(40));

PHP 8 の JIT コンパイラ機能を有効にするためにいくつか設定が必要なので、それを含めて下記のようなオプションで実行しています。JIT コンパイラは OPCache の機能拡張なので OPCache を有効にする必要があります。また、opcache.jitopcache.jit_buffer_size などで指定する値は PHP: rfc:jit を参考にしています。

$ docker run --rm -v `pwd`:/app -w /app php:8.0-rc php -dzend_extension=opcache \
                      -dopcache.enable=1 -dopcache.enable_cli=1 \
                      -dopcache.jit=1235  -dopcache.jit_buffer_size=64M fib.php

実行結果は下記です。JIT on の場合、OPCache on のみより実行時間が 79 % まで短縮しました!

実際の Web アプリケーションでは IO がボトルネックになりがちなので、どれだけパフォーマンスに寄与するか計測は必要ですが、PHP の適用領域を広げる意味でも期待したい機能です。

実行環境 実行時間
[参考] PHP 7.4 (OPCache on) 12.227s ---
PHP 8.0 alpha1 12.099s 106%
PHP 8.0 alpha1(OPCache on) 11.398s 100%
PHP 8.0 alpha1(JIT/OPCache on) 9.006s 79%
# 参考: PHP 7.4 with OPCache
$ time docker run --rm -v `pwd`:/app -w /app php:7.4 php -dzend_extension=opcache \
                       -dopcache.enable=1 -dopcache.enable_cli=1 fib.php
int(102334155)
docker run --rm -v `pwd`:/app -w /app php:7.4 php -dzend_extension=opcache     0.03s user 0.02s system 0% cpu 12.227 total

# OPCache / JIT off
$ time docker run --rm -v `pwd`:/app -w /app php:8.0-rc php fib.php
int(102334155)
docker run --rm -v `pwd`:/app -w /app php:8.0-rc php fib.php  0.03s user 0.01s system 0% cpu 12.099 total

# OPCache on
$ time docker run --rm -v `pwd`:/app -w /app php:8.0-rc php -dzend_extension=opcache \
                       -dopcache.enable=1 -dopcache.enable_cli=1 fib.php
int(102334155)
docker run --rm -v `pwd`:/app -w /app php:8.0-rc php -dzend_extension=opcache  0.03s user 0.01s system 0% cpu 11.398 total

# OPCache / JIT on
$ time docker run --rm -v `pwd`:/app -w /app php:8.0-rc php -dzend_extension=opcache \
                      -dopcache.enable=1 -dopcache.enable_cli=1 \
                      -dopcache.jit=1235  -dopcache.jit_buffer_size=64M fib.php
int(102334155)
docker run --rm -v `pwd`:/app -w /app php:8.0-rc php -dzend_extension=opcache  0.03s user 0.01s system 0% cpu 9.006 total

リリーススケジュール

PHP 8 のリリーススケジュールは下記で公開されています。現在の予定では2020/11/26に GA リリースとなります。

https://wiki.php.net/todo/php80

さいごに

オフィシャル PHP にて PHP 8 のイメージが公開されたので触ってみました。こうして alpha 版リリースの段階でイメージが公開されることは気軽に試すことができてありがたいですね。以前に比べても Docker の活用がさらに広がってきた感じがします。

PHP 8 はまだ Feature freeze されていないので、いくつかの RFC が提案されています。match 式 も通りそうなので今後のリリースが楽しみですね。Docker があれば簡単に利用できるので、PHP 8 を試してみてはどうでしょう。

参考