明日になったら本気出せる

底辺Web系エンジニアの日記

PHP5.5の新機能をビルトインサーバを使って遊んでみた[php]

概要

PHP5.5の正式がついこの間リリースされたと思ったらもう5.5.6なんだ・・・
なんかこのままだと時代に置いてけぼりにされそうなので新機能を中心に軽く遊んでみた。

新機能(公式より)

  • ジェネレータの追加
  • finally キーワードの追加
  • 新しいパスワードハッシュAPI
  • foreach が list() に対応
  • empty() が任意の式に対応
  • array リテラルと string リテラルデリファレンス
  • ::class によるクラス名の解決
  • OPcache 拡張モジュールの追加
  • foreach が非スカラーのキーに対応
  • GD の改良

インストール

PHP5.5.6をソースからインストールする。既にPHP5.4.22がApacheのモジュールとして動いてるので、バージョン違いとしてインストール、共存させたい。

ダウンロード & configure

# wget http://jp1.php.net/get/php-5.5.6.tar.gz/from/this/mirror
# tar xvfz php-5.5.6.tar.gz
# cd php-5.5.6
# ./configure --prefix=/usr/local/php-5.5.6 --enable-mbstring --with-gd --with-pdo-mysql=mysqlnd --with-jpeg-dir --with-iconv

configureの内容は適当。configureでミスったら必要なものを入れる。
僕の環境だとlibxml2-develとlibjpeg-develが足りなかった。

xml2-config not found libxml2-develがない
jpeglib.h not found libjpeg-develがない
# yum install -y libxml2-devel
# yum install -y libjpeg-devel

configureが通ったらmake

# make
# make install

シンボリックリンクを作る

# ln -s /usr/local/php-5.5.6/bin/php /usr/local/bin/php56
# php56 -v
PHP 5.5.6 (cli) (built: Dec  3 2013 18:50:20)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies

# php -v
PHP 5.4.22 (cli) (built: Nov 13 2013 09:28:56)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

これで共存はOKだね。

Webサーバの起動

今回はApacheのモジュールで動かさない。CLIで動かすのもいいけどせっかくなのでPHP5.4から導入されたビルトインサーバを使ってみたい。PHP5.4以降であればPHP自体に組み込みのWebサーバが入っているのでそちらで動作させる。

ビルトインウェブサーバー
http://php.net/manual/ja/features.commandline.webserver.php

アクセス用のディレクトリ、ファイルを作る。

# mkdir php_test
# cd php_test/
# echo "<?php phpinfo();" > phpinfo.php

Webサーバの起動

# php56 -S localhost:8000
PHP 5.5.6 Development Server started at Thu Dec  5 11:38:55 2013
Listening on http://localhost:8000
Document root is /opt/php_test
Press Ctrl-C to quit.

Ctrl+C で終了。

ただこのままだと外部からWebサーバをアクセスできないので(ホストオンリーの状態)
リモートアクセスを許可してWebサーバを起動する。
リモートアクセスを許可するにはホスト部に0.0.0.0を指定する。

# php56 -S 0.0.0.0:8000
PHP 5.5.6 Development Server started at Thu Dec  5 11:51:08 2013
Listening on http://0.0.0.0:8000
Document root is /opt/php_test
Press Ctrl-C to quit.

ブラウザからアクセスしてみる。(http://ip_addr:8000/phpinfo.php)
正しく表示できればOK。

新機能を使ってみる

ジェネレータの追加

ジェネレータとは
http://www.php.net/manual/ja/language.generators.overview.php

ここの解説が非常にまとまっていて分かりやすい。

PHP5.5新機能「ジェネレータ」初心者入門
http://www.slideshare.net/kwatch/php55

とりあえず使って見ないとよく分からんのでテストコード書いてみた。

<?php
function generator_test()
{
    for ($i = 1; $i <= 3; $i++) {
        yield $i;
    }
};

foreach (generator_test() as $key => $count) {
    var_dump($count);
}
int(1)
int(2)
int(3)

まぁこれは思ったとおりの結果が出てきた。

今度は直接呼んでみた。

<?php
var_dump(generator_test());
object(Generator)#1 (0) { } 

ちょっとこの挙動が気になったので色々通してみた。

<?php
var_dump(gettype(generator_test()));
var_dump(get_class(generator_test()));
var_dump(generator_test() instanceof Generator);
string(6) "object"
string(9) "Generator"
bool(true)

なるほど、Generatorというクラスのインスタンスになるのか。と思ったらマニュアルにあった。

Generatorクラス
http://php.net/manual/ja/class.generator.php

普通にクラスメソッドを呼ぶように使えそうだね。

クロージャ化したパターン。

<?php
$generator_test = function() 
{
    for ($i = 1; $i <= 3; $i++) {
            yield $i;
    }
};

var_dump(gettype($generator_test()));
var_dump(get_class($generator_test()));
var_dump($generator_test() instanceof Generator);
var_dump($generator_test()->current()); // Generatorクラスのメソッド呼び出し

var_dump(gettype($generator_test));
var_dump(get_class($generator_test));
var_dump($generator_test instanceof Closure);
string(6) "object"
string(9) "Generator"
bool(true)
int(1)
string(6) "object"
string(7) "Closure"
bool(true)

なんか思ったとおりだった。

まとめるとこんな感じかな

  • yield というキーワードが増え、こいつが使われている関数を呼び出すとジェネレータオブジェクトが返る。
  • ジェネレターオブジェクトはイテレータとして使い、yield で値を返すと同時に状態を保存、次回呼び出し時はその地点から再開する。
  • イテレータの繰り返し回数としては分岐しない限り yield が出てくる回数とイコール。

今までのイテレータの構文でも十分必要に耐えるのでなかなか上手く使っていくパターンが思いつかないが、うまく使うときれいにコードがかけるといった印象。こういったことは積極的に使っていかないと中々慣れないのでチャンスがあれば使ってみようと思う。

finally キーワードの追加

ようやく導入されたかー。まぁfinallyは他の言語で割とメジャーなのであえてここで詳しく書くこともないか。

<?php
function throwException($flg)
{
    try
    {
        echo "try block\n";
        
        if($flg)
        {
            throw new Exception();
        }

        echo "return in try\n";
        return;
    }
    catch(Exception $e)
    {
         echo "catch block\n";
         throw $e;
    }
    finally
    {
        echo "finally block\n";
    }
};

try
{
    echo "call throwException(false)\n";
    throwException(false);
    echo "call throwException(true)\n";
    throwException(true);
}
catch(Exception $e)
{
    echo "global catch\n";
}
finally
{
    echo "global finally\n";
}
call throwException(false)
try block
return in try
finally block
call throwException(true)
try block
catch block
finally block
global catch
global finally

新しいパスワードハッシュAPI

Password Hashing関数郡が増えたそう。今まで自前でsaltとか使ってた人ががんばる必要がなくなった?程度の印象。

Password Hashing 関数
http://www.php.net/manual/ja/ref.password.php

foreach が list() に対応

こうやって使う発想はなかった。list()って関数じゃないからできるんだろうね。

<?php
$array = [
    [1, 2],
    [3, 4],
];

foreach ($array as list($a, $b)) {
    echo "A: $a; B: $b\n";
}
A: 1; B: 2
A: 3; B: 4

empty() が任意の式に対応

そういえばemptyの引数は変数じゃないとエラーになったね。すっかり忘れていた。ただ個人的にはempty()は非推奨なので今後もあまり使う機会はないと思う。

array リテラルと string リテラルデリファレンス

できるようになったとしてもこんな可読性が下がりそうなこと絶対しないと思うなぁ。よく見ると分かるんだけど一瞬ここで手が止まりそう。

<?php
echo 'Array dereferencing: ';
echo [1, 2, 3][0];
echo "\n";

echo 'String dereferencing: ';
echo 'PHP'[0];
echo "\n";
Array dereferencing: 1
String dereferencing: P

::class によるクラス名の解決

全部のクラスにclassっていう定数が暗黙に付いたという理解でいいのかな?

<?php
namespace hoge\fuga;
class TestClass{};

echo TestClass::class;
hoge\fuga\TestClass

再宣言したら怒られた。

<?php
namespace hoge\fuga;
class TestClass
{
    const class = 'hoge';
};

echo TestClass::class;
Parse error: syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING)

OPcache 拡張モジュールの追加

標準で XCache みたいなプリコンパイル機能がついたらしい。

foreach が非スカラーのキーに対応

こんなこと普通しないので省略。

GD の改良

今後GDを使うことがあれば書くかもしれない。

感想

新機能のメインとしてはgeneratorかな。うまく使っていくには慣れが必要そう。ちょっと今のところうまい使い方は思いつかないなぁ。今更感があるがfinallyキーワードが追加されたので追加機能の中ではこれが一番使用頻度が高くなると思う。

読み返して思ったけどこれ別にビルトインサーバのくだりは一切必要ないな・・・