y_uti のブログ

統計、機械学習、自然言語処理などに興味を持つエンジニアの技術ブログです

PHP Conference 2014 を聞いてきました

PHP Conference 2014 というイベントに参加してきました。このような大規模なイベントに参加するのは初めてだったのですが、どのセッションもとても面白い話が聞けて、楽しく過ごさせてもらいました。

私はどちらかというと、フレームワークやサービスの話よりも言語処理系そのものに興味があるので、最初はこちらのセッションに参加しました。
PHPコアから読み解くPHP5.5

配列リテラルデリファレンス

最初の方のスライドで、PHP 5.5 の新機能の一覧の中に配列リテラルデリファレンスがあったのですが、これが PHP 5.4 まで出来なかったとは知りませんでした。以下のようなコードを書いて試してみます。

<?php
print array(1, 2, 3)[0] . "\n";

たしかに、PHP 5.4 だとシンタックスエラーになってしまいました。

$ php -v
PHP 5.4.16 (cli) (built: Sep 30 2014 09:44:39)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
$ php array_derefence.php
PHP Parse error:  syntax error, unexpected '[' in /home/y-uti/array_derefence.php on line 2

このコードは、PHP 5.5 ではエラーにならず 1 が出力されます。

$ php -v
PHP 5.5.17 (cli) (built: Oct 10 2014 22:18:22)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
$ php array_derefence.php
1

これはパーザの問題で、PHP 5.4 では以下のように実装されているところが、

expr_without_variable:
  ...
	| T_ARRAY '(' array_pair_list ')' { $$ = $3; }

(https://github.com/php/php-src/blob/php-5.4.33/Zend/zend_language_parser.y#L794)

PHP 5.5 では以下のように変更されていました。

expr_without_variable:
  ...
	| combined_scalar_offset { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); }

(https://github.com/php/php-src/blob/PHP-5.5.18/Zend/zend_language_parser.y#L807)

combined_scalar_offset の定義は次のようになっていて、これで、配列リテラルデリファレンスが受理されます。

combined_scalar_offset:
          combined_scalar '[' dim_offset ']' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
        | combined_scalar_offset '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
        | T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' { $1.EA = 0; zend_do_begin_variable_parse(TSRMLS_C); fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }

combined_scalar:
          T_ARRAY '(' array_pair_list ')' { $$ = $3; }
        | '[' array_pair_list ']' { $$ = $2; }

(https://github.com/php/php-src/blob/PHP-5.5.18/Zend/zend_language_parser.y#L827)

似たような話として、PHP 5.3 までは、配列を戻す関数の戻り値を直接デリファレンスできない問題がありました。以下のようなコードです。

<?php
function make_array() { return array(1, 2, 3); }
print make_array()[0] . "\n";

PHP 5.3 で実行するとシンタックスエラーになります。

$ php -v
PHP 5.3.29 (cli) (built: Sep 27 2014 21:58:30)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans
$ php array_retval_dereference.php
PHP Parse error:  syntax error, unexpected '[' in /home/y-uti/array_retval_dereference.php on line 3

私は、これには本当にイライラしていて、個人的には PHP 5.4 の最大の改善はこれを修正したことだったと思っているのですが、配列リテラルに関してまだ問題が残っていたとは思ってもみませんでした。

phpenv 管理下の PHP に vld を導入する

さて、このセッションでは、PHP 5.5 の新機能のいくつかについて性能比較を含めた紹介がありました。発表資料にはバイトコード命令列が掲載されていましたが、PHP では、vld という PECL 拡張モジュールをインストールすることで、バイトコード命令を出力できます。

ところが、私は anyenv + phpenv + phpbuild で複数のバージョンの PHP を切り替えられるようにしているのですが、この環境では PECL がインストールされません。そのため、拡張モジュールを導入するには、phpize を使ってソースコードからビルドする必要がありました。vld の場合であれば、次の手順で phpenv の環境に導入できました。

まず、ウェブサイトから vld のソースコードをダウンロードして展開します。

$ wget http://pecl.php.net/get/vld-0.12.0.tgz
$ tar zxf vld-0.12.0.tgz

phpenv を使って、PHP のバージョンを選択しておきます。

$ phpenv global 5.5.17
phpenv v0.0.4-dev

5.5.17

ソースコードを展開したディレクトリで phpize を実行します。

$ cd vld-0.12.0
$ phpize
Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121212
Zend Extension Api No:   220121212

あとは、普通の make の手順どおりです。なお、通常は make install するときには sudo などを使いますが、phpenv の環境に導入するので、一般ユーザのままで install できます。

$ ./configure
...
$ make
...
$ make install
...

インストール後、vld.ini を作成します。ディレクトリ階層は phpenv の導入形態によって異なると思いますが、私の環境では以下の場所に作成すればよいようです。

$ cd ~/.anyenv/envs/phpenv/versions/5.5.17/etc/conf.d
$ vi vld.ini
extension="/home/y-uti/.anyenv/envs/phpenv/versions/5.5.17/lib/php/extensions/no-debug-non-zts-20121212/vld.so"

これで、phpenv 管理下の PHP でも vld を利用できます。ためしに、セッションでも紹介された boolval を使うコードを書いてみました。

<?php
print boolval(1.0) . "\n";

実行すると以下のようなバイトコード命令列が表示されます。

$ php -dvld.active=1 boolval.php
Finding entry points
Branch analysis from position: 0
Return found
filename:       /home/y-uti/boolval.php
function name:  (null)
number of ops:  9
compiled vars:  none
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   2     0  >   EXT_STMT
         1      EXT_FCALL_BEGIN
         2      SEND_VAL                                                 1
         3      DO_FCALL                                      1  $0      'boolval'
         4      EXT_FCALL_END
         5      CONCAT                                           ~1      $0, '%0A'
         6      PRINT                                            ~2      ~1
         7      FREE                                                     ~2
   3     8    > RETURN                                                   1

branch: #  0; line:     2-    3; sop:     0; eop:     8
path #1: 0,
1

なお、現在公開されている vld 0.12.0 は、PHP 5.5 までの対応のようです。PHP 5.6.1 で phpize して make を試みたところ、コンパイルエラーになってしまいました。