アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

PHP5.5 Alpha1リリース! 新機能を俯瞰する

こんにちは、久保田です。
PHP5.5 Alpha1が11/15日付けでリリースされました。この記事では以下のような新機能や変更を紹介します。

・ジェネレータとコルーチンの追加
・finallyキーワードの追加
・配列や文字列のデリファレンスのサポート
・foreachの中でのlist表現
・PCRE正規表現での/e修飾子が非推奨化
・NEWS翻訳

ジェネレータとコルーチンの追加



PHP5.5での一番大きな新機能は、ジェネレータとコルーチンの追加です。文法にyield構文が追加されました。

まずは、ジェネレータを説明します。例えば以下の様なコードが動きます。



<?php

function hoge()
{
    yield "hoge";
    yield "fuga";
    yield "piyo";
}

foreach (hoge() as $str) {
    var_dump($str);
}


これをPHP5.5で実行すると以下のように出力されます。



string(4) "hoge"
string(4) "fuga"
string(4) "piyo"


ジェネレータでは、yield構文を使ってforeachなどで利用できる値の列を生成することができます。

このコード例を見ただけだと、関数で配列を返したりイテレータを使うのと何が違うのかわからないと思います。PHP5.5で実装されたジェネレータの一番の特徴は、ジェネレータ内で一度値を生成すると計算が途中で止まって呼び出し元へ制御が復帰し、再びジェネレータ内に制御が戻ると以前の計算の位置から処理が再開されるということです。

以下のような例を実行します。



<?php

function hoge()
{
    echo "generating a\n";
    yield 'a';
    echo "generating b\n";
    yield 'b';
    echo "generating c\n";
    yield 'c';
}

function fuga()
{
    $ret = [];
    echo "add a\n";
    $ret[] = 'a';
    echo "add b\n";
    $ret[] = 'b';
    echo "add c\n";
    $ret[] = 'c';
    return $ret;
}

foreach (hoge() as $char) {
    echo $char . "\n";
}

echo "\n";

foreach (fuga() as $char) {
    echo $char . "\n";
}


実行すると以下のように出力されます。配列で返すやり方とは違って、ジェネレータ内のyieldで値を返すごとにforeachのループに制御が戻ることがわかります。



generating a
a
generating b
b 
generating c
c
 
add a
add b
add c
a
b 
c


ジェネレータを利用すると、イテレータで実装するのとは違って状態をオブジェクトのプロパティなどで管理する必要がなくなり、コード量が劇的に減ります。また、配列を直接返す形式よりもメモリ使用量を抑えることもできます。

無限のフィボナッチ数列も簡単に生成できます。



<?php

function fib()
{
    yield $prev = 1;
    yield $n = 1;
    for (;;) {
        list($prev, $n) = [$n, $prev + $n];
        yield $n; 
    }   
}

foreach (fib() as $n) {
    if ($n > 100) {
        break;
    }   
    echo $n . "\n";
}


0から100までの間のフィボナッチ数列が生成できました。



1
1
2
3
5
8
13
21
34
55
89


次はコルーチンを説明します。コルーチンもジェネレータと同様にyield構文を利用します。



<?php

function hoge()
{
    for (;;) {
        var_dump(yield);
    }
}

$hoge = hoge();

$hoge->send("fuga");
$hoge->send("piyo");
$hoge->send("foo");


これを実行すると出力が以下です。



string(4) "fuga"
string(4) "piyo"
string(6) "foobar"


コルーチンでは、計算の途中で呼び出し元から引数を受け取る事ができます。ジェネレータが値を一度生成すると計算がその場で止まるのと同様に、コルーチンの中では呼び出し元から引数を受け取るまで計算が止まります。ジェネレータと違うところは、ジェネレータが値の列を返し続けるものであるのに対して、コルーチンは値の列を取り続けるところです。

参考:

PHP5.5新機能「ジェネレータ」初心者入門
PHP: rfc:generators [PHP Wiki]

finallyキーワードの追加



try構文のとともに利用するfinallyキーワードがサポートされるようになりました。finally構文を置くと、try-catchブロックを抜ける際に確実にfinallyブロックの中が実行されます。例えばファイルハンドルやリソースの後片付けのコードを正しく書けるようになります。



<?php
$db = mysqli_connect();
try {
   call_some_function($db);
} catch (Exception $e) {
   throw $e;
} finally {
   mysqli_close($db);
}


参考:

Request for Comments: Supports finally keywordを和訳してみた

配列や文字列でのデリファレンスのサポート



PHP5.4では関数の戻り値からのデリファレンスがサポートされましたがリテラルからはできませんでした。



<?php

function hoge() {
    return ["hoge", "fuga", "foo"];
}

var_dump(hoge()[1]);  // PHP5.4からこれが大丈夫になった

["hoge", "fuga", "foo"][1]; // PHP5.4ではパースエラー


PHP5.5では配列や文字列のリテラルから直接デリファレンスできるようになりました。



<?php

var_dump(["hoge", "fuga", "foo"][2]); // => "foo"

var_dump("hoge"[2]); // => "g"


foreachの中でのlist表現



foreach構文で要素を受け取る部分でのlist表現がサポートされました。



<?php

list($a, $b) = [1, 2]; // PHP5.5以前のlist表現の使い方

$arr = [
    ["hoge", "fuga"],
    ["foo", "bar"]
];

foreach ($arr as list($left, $right)) { // PHP5.5でサポートされる書き方
    echo $left . ":" . $right;
}


PCRE正規表現での/e修飾子の非推奨化



セキュリティ脆弱性を作ってしまいがちであるPCRE正規表現での/e修飾子が非推奨となりました。preg_replace関数で文字列を置換する際の加工のためにPHPの関数を用いたい場合は、pref_replace_callback関数を利用しましょう。

参考:

http://php.net/reference.pcre.pattern.modifiers

NEWS翻訳



最後に、PHP5.5 Alpha1のNEWSファイルの簡単な翻訳です。細かな変更はこれを確認してみてください。



PHP                                                                        NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
13 Nov 2012, PHP 5.5.0 Alpha 1

- 一般的な改善:
  General improvements:

  . ジェネレータの追加。
    Added support for generators. (Nikita Popov)
  . パスワードハッシュ化のための単純化されたAPIの追加
    Add simplified password hashing API 
    (https://wiki.php.net/rfc/password_hash). (Anthony Ferrara)
  . ジェネレータとコルーチンの追加。
    Add generators and coroutines (https://wiki.php.net/rfc/generators).
    (Nikita Popov)
  . foreach文の中でのlist表現のサポート(https://wiki.php.net/rfc/foreachlist)
    Support list in foreach (https://wiki.php.net/rfc/foreachlist). (Laruence)
  . 'finally'の実装(https://wiki.php.net/rfc/finally) (Laruence)
    Implemented 'finally' keyword (https://wiki.php.net/rfc/finally). (Laruence)
  . Windows XPとWindows 2003のサポート廃止 (Pierre)
    Drop Windows XP and 2003 support. (Pierre)
  . reset中のset_exception_handlerの改善 (Laruence)
    Improve set_exception_handler while doing reset.(Laruence)
  . 配列や文字列リテラルのデリファレンスのサポート
    Support constant array/string dereferencing. (Laruence)
  . 関数呼び出しの結果をempty()に渡せるようにした
    Add support for using empty() on the result of function calls and
    other expressions (https://wiki.php.net/rfc/empty_isset_exprs).
    (Nikita Popov)
  . php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid(), zend_logo_guid()関数の削除
    Remove php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid(),
    zend_logo_guid(). (Adnrew Faulds)

- カレンダー:
  Calendar:
  . #54254バグの修正(Adar(訳注: ヘブライ暦の12番目の月)のみが残っている時、cal_from_jdがmonth = 6を返す) 
    Fixed bug #54254 (cal_from_jd returns month = 6 when there is only one Adar)
    (Stas, Eitan Mosenkis).

- Core:
  . boolval()関数の追加。
    Added boolval(). (Jille Timmermans).
  . pack/unpack関数に"Z"オプションを追加。
    Added "Z" option to pack/unpack. (Gustavo)
  . 機能要望#60738のの実装('set_error_handler'へnullを設定することを許可)
    Implemented FR #60738 (Allow 'set_error_handler' to handle NULL).
    (Laruence, Nikita Popov)
  . assert関数にカスタムメッセージを設定するための第二オプション引数を追加。
    Added optional second argument for assert() to specify custom message. Patch
    by Lonny Kapelushnik (lonny@lonnylot.com). (Lars)
  . バグ#18556の修正(エンジンがクラス名を扱う時に設定されたロケールのルールを使うバグ) (訳注: ロケールによっては宣言したクラス名が正しく認識されないバグ)
    Fixed bug #18556 (Engine uses locale rules to handle class names). (Stas)
  . バグ#61681の修正(不恰好な文法) (訳注: 文字列内の変数表現の中で@を使うと、関数呼び出しができてしまうバグ)
    Fixed bug #61681 (Malformed grammar). (Nikita Popov, Etienne, Laruence).
  . バグ#61038の修正(unpack("a5", "str\0\0") が期待通りに動かない)
    Fixed bug #61038 (unpack("a5", "str\0\0") does not work as expected).
    (srgoogleguy, Gustavo)
  . set_error_handler関数やset_exception_handler関数にNULLを渡した時に以前のハンドラを返す
    Return previous handler when passing NULL to set_error_handler and
    set_exception_handler. (Nikita Popov)
    

- cURL:
  . オプションのサポート: 
    Added support for CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_APPEND, 
    CURLOPT_DIRLISTONLY, CURLOPT_NEW_DIRECTORY_PERMS, CURLOPT_NEW_FILE_PERMS, 
    CURLOPT_NETRC_FILE, CURLOPT_PREQUOTE, CURLOPT_KRBLEVEL, CURLOPT_MAXFILESIZE,
    CURLOPT_FTP_ACCOUNT, CURLOPT_COOKIELIST, CURLOPT_IGNORE_CONTENT_LENGTH,
    CURLOPT_CONNECT_ONLY, CURLOPT_LOCALPORT, CURLOPT_LOCALPORTRANGE, 
    CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPT_SSL_SESSIONID_CACHE, 
    CURLOPT_FTP_SSL_CCC, CURLOPT_HTTP_CONTENT_DECODING, 
    CURLOPT_HTTP_TRANSFER_DECODING, CURLOPT_PROXY_TRANSFER_MODE, 
    CURLOPT_ADDRESS_SCOPE, CURLOPT_CRLFILE, CURLOPT_ISSUERCERT, 
    CURLOPT_USERNAME, CURLOPT_PASSWORD, CURLOPT_PROXYUSERNAME, 
    CURLOPT_PROXYPASSWORD, CURLOPT_NOPROXY, CURLOPT_SOCKS5_GSSAPI_NEC, 
    CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPT_TFTP_BLKSIZE, 
    CURLOPT_SSH_KNOWNHOSTS, CURLOPT_FTP_USE_PRET, CURLOPT_MAIL_FROM, 
    CURLOPT_MAIL_RCPT, CURLOPT_RTSP_CLIENT_CSEQ, CURLOPT_RTSP_SERVER_CSEQ, 
    CURLOPT_RTSP_SESSION_ID, CURLOPT_RTSP_STREAM_URI, CURLOPT_RTSP_TRANSPORT,
    CURLOPT_RTSP_REQUEST, CURLOPT_RESOLVE, CURLOPT_ACCEPT_ENCODING, 
    CURLOPT_TRANSFER_ENCODING, CURLOPT_DNS_SERVERS and CURLOPT_USE_SSL.
    (Pierrick)
  . バグ#55635の修正 (CURLOPT_BINARYTRANSFERはこれからは利用されない。
    定数は後方互換性のため残されるが、これはなにもしない。). (Pierrick)
    Fixed bug #55635 (CURLOPT_BINARYTRANSFER no longer used. The constant
    still exists for backward compatibility but is doing nothing). (Pierrick)
  . #54995バグを修正(CURLINFO_RESPONSE_CODEへのサポートが無い)
    Fixed bug #54995 (Missing CURLINFO_RESPONSE_CODE support). (Pierrick)

- Datetime
  . バグ#61642の修正(modify("+5 weekdays")が月曜を返す)
    Fixed bug #61642 (modify("+5 weekdays") returns Sunday). 
    (Dmitri Iouchtchenko) 

- Hash
  . hash_pbkdf2()関数でPBKDF2のサポートを追加
    Added support for PBKDF2 via hash_pbkdf2(). (Anthony Ferrara)

- Intl
  . intlエクステンションがICU4.0以上を要求するようになった。
    The intl extension now requires ICU 4.0+.
  . intl.error_levelとともにグローバルなエラーがセットされた時の挙動を
    コントロールするためのintl.use_exceptions INIディレクティブの追加。
    Added intl.use_exceptions INI directive, which controls what happens when
    global errors are set together with intl.error_level. (Gustavo)
  . MessageFormatter::format()とそれに関連する関数で、ICU4.8以上の場合に
    名前付き引数や数字と名前付きのものをまぜた引数を受け入れるようになった。
    MessageFormatter::format() and related functions now accepted named
    arguments and mixed numeric/named arguments in ICU 4.8+. (Gustavo)
  . MessageFormatter::format()とそれに関連する関数で、十分でない数の引数を渡した時に
    エラーを出さなくなった。その代わり、そのプレースホルダは置換されずにそのままになる。
    MessageFormatter::format() and related functions now don't error out when
    an insufficient argument count is provided. Instead, the placeholders will
    remain unsubstituted. (Gustavo)
  . MessageFormatter::parse()とMessageFormat::format()(と静的に同じそれらの関数)は引数の二番目の精度を使い捨てない (訳注: ???)
    MessageFormatter::parse() and MessageFormat::format() (and their static
    equivalents) don't throw away better than second precision in the arguments.
    (Gustavo)
  . IntlDateFormatter::__constructとdatefmt_create()が
    IntelTimeZoneオブジェクト、DateTimeZoneオブジェクトやNULLなどのタイムゾーンを
    指定する$timezone引数を受け入れるようになった。
    IntlDateFormatter::__construct and datefmt_create() now accept for the
    $timezone argument time zone identifiers, IntlTimeZone objects, DateTimeZone
    objects and NULL. (Gustavo)
  . IntlDateFormatter::__constructとdatefmt_create()関数は、
    無効なtimezon識別子や空の文字列を受け付けなくなった。
    IntlDateFormatter::__construct and datefmt_create() no longer accept invalid
    timezone identifiers or empty strings. (Gustavo)
  . デフォルトのタイムゾーンは、IntlDateFormatter::__constructや
    datefmt_create()関数では対応する引数が渡されなかったり、NULLが渡された場合に利用されるデフォルト
    のタイムゾーンが、ICUのデフォルトのタイムゾーンではなくdate_default_timezone_get()関数から渡されるものとなった。
    The default time zone used in IntlDateFormatter::__construct and
    datefmt_create() (when the corresponding argument is not passed or NULL is
    passed) is now the one given by date_default_timezone_get(), not the
    default ICU time zone. (Gustavo)
  . IntlDateFormatterへ渡されるタイムゾーンは、もしそれがNULLであったり、
    渡されたカレンダーがIntlCalendarオブジェクトだった場合無視される。
    この場合、IntlCalendarのタイムゾーンが代わりに利用される。そうでなければ、
    $timezone引数で指定されたタイムゾーンが代わりに利用される。
    IntlCalendarオブジェクトはこのバージョンで登場するので、この変更は古いコードに影響を与えない。
    The time zone passed to the IntlDateFormatter is ignored if it is NULL and
    if the calendar passed is an IntlCalendar object -- in this case, the
    IntlCalendar's time zone will be used instead. Otherwise, the time zone
    specified in the $timezone argument is used instead. This does not affect
    old code, as IntlCalendar was introduced in this version. (Gustavo)
  . IntlDateFormatter::__constructとdatefmt_create()は、IntlCalendarオブジェクトの入った
    $calendar引数を受け入れるようになった。
    IntlDateFormatter::__construct and datefmt_create() now accept for the
    $calendar argument also IntlCalendar objects. (Gustavo)
  . IntlDateFormatter::getCalendar()とdatefmt_get_calendar()関数は
    IntlDateFormatterがIntlDateFormatter::GREGORIAN/TRADITIONAL定数の代わりにIntlCalendarで
    設定された場合にfalseを返す。IntlCalendarはこのバージョン以前には存在しない。
    IntlDateFormatter::getCalendar() and datefmt_get_calendar() return false
    if the IntlDateFormatter was set up with an IntlCalendar instead of the
    constants IntlDateFormatter::GREGORIAN/TRADITIONAL. IntlCalendar did not
    exist before this version. (Gustavo)
  . IntlDateFormatter::setCalendar()とdatefmt_set_calendar()関数は
    タイムゾーンが与えられた場合のIntlCalendarオブジェクトを受け付けるようになった。
    IntlDateFormatter::setCalendar() and datefmt_set_calendar() now also accept
    an IntlCalendar object, in which case its time zone is taken. Passing a
    constant is still allowed, and still keeps the time zone. (Gustavo)
  . IntlDateFormatter::setTimeZoneID()とdatefmt_set_timezone_id()関数は非推奨となった。
    IntlDateFormatter::setTimeZone()かdatefmt_set_timezone()関数を代わりに使うこと。
    IntlDateFormatter::setTimeZoneID() and datefmt_set_timezone_id() are
    deprecated. Use IntlDateFormatter::setTimeZone() or datefmt_set_timezone()
    instead. (Gustavo)
  . IntlDateFormatter::format()とdatefmt_format()関数はフォーマットのために
    IntlCalendarオブジェクトも受け付ける様になった。
    IntlDateFormatter::format() and datefmt_format() now also accept an
    IntlCalendar object for formatting. (Gustavo)
  . 以下のクラスが追加された。Added the classes: IntlCalendar, IntlGregorianCalendar, IntlTimeZone,
    IntlBreakIterator, IntlRuleBasedBreakIterator and
    IntlCodePointBreakIterator. (Gustavo)
  . 以下の関数が追加された。Added the functions: intlcal_get_keyword_values_for_locale(),
    intlcal_get_now(), intlcal_get_available_locales(), intlcal_get(),
    intlcal_get_time(), intlcal_set_time(), intlcal_add(),
    intlcal_set_time_zone(), intlcal_after(), intlcal_before(), intlcal_set(),
    intlcal_roll(), intlcal_clear(), intlcal_field_difference(),
    intlcal_get_actual_maximum(), intlcal_get_actual_minimum(),
    intlcal_get_day_of_week_type(), intlcal_get_first_day_of_week(),
    intlcal_get_greatest_minimum(), intlcal_get_least_maximum(),
    intlcal_get_locale(), intlcal_get_maximum(),
    intlcal_get_minimal_days_in_first_week(), intlcal_get_minimum(),
    intlcal_get_time_zone(), intlcal_get_type(),
    intlcal_get_weekend_transition(), intlcal_in_daylight_time(),
    intlcal_is_equivalent_to(), intlcal_is_lenient(), intlcal_is_set(),
    intlcal_is_weekend(), intlcal_set_first_day_of_week(),
    intlcal_set_lenient(), intlcal_equals(),
    intlcal_get_repeated_wall_time_option(),
    intlcal_get_skipped_wall_time_option(),
    intlcal_set_repeated_wall_time_option(),
    intlcal_set_skipped_wall_time_option(), intlcal_from_date_time(),
    intlcal_to_date_time(), intlcal_get_error_code(),
    intlcal_get_error_message(), intlgregcal_create_instance(),
    intlgregcal_set_gregorian_change(), intlgregcal_get_gregorian_change() and
    intlgregcal_is_leap_year(). (Gustavo)
  . 以下の関数が追加された。
    Added the functions: intltz_create_time_zone(), intltz_create_default(),
    intltz_get_id(), intltz_get_gmt(), intltz_get_unknown(),
    intltz_create_enumeration(), intltz_count_equivalent_ids(),
    intltz_create_time_zone_id_enumeration(), intltz_get_canonical_id(),
    intltz_get_region(), intltz_get_tz_data_version(),
    intltz_get_equivalent_id(), intltz_use_daylight_time(), intltz_get_offset(),
    intltz_get_raw_offset(), intltz_has_same_rules(), intltz_get_display_name(),
    intltz_get_dst_savings(), intltz_from_date_time_zone(),
    intltz_to_date_time_zone(), intltz_get_error_code(),
    intltz_get_error_message(). (Gustavo)
  . 以下のメソッドが追加された。
    Added the methods: IntlDateFormatter::formatObject(),
    IntlDateFormatter::getCalendarObject(), IntlDateFormatter::getTimeZone(),
    IntlDateFormatter::setTimeZone(). (Gustavo)
  . 以下の関数が追加された。
    Added the functions: datefmt_format_object(), datefmt_get_calendar_object(),
    datefmt_get_timezone(), datefmt_set_timezone(),
    datefmt_get_calendar_object(), intlcal_create_instance(). (Gustavo)

- MCrypt
  . mcrypt_ecb()、mcrypt_cbc(), mcrypt_cfb(), mcrypt_ofb()関数がE_DEPRECATEDエラーを出すようにした
    mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb() now throw
    E_DEPRECATED. (GoogleGuy)

- MySQLi
  . libmysqlを使うときのLOAD DT LOCAL INFILEハンドラのサポートの放棄
    Dropped support for LOAD DATA LOCAL INFILE handlers when using libmysql.
    Known for stability problems. (Andrey)
  . MySQL 5.6.6以上で利用できるSHA256認証のサポートの追加。
    Added support for SHA256 authentication available with MySQL 5.6.6+. (Andrey)

- PCRE:
  . /e 修飾子を非推奨にした
    Deprecated the /e modifier
    (https://wiki.php.net/rfc/remove_preg_replace_eval_modifier). (Nikita Popov)
  . バグ#63284の修正
    Fixed bug #63284 (Upgrade PCRE to 8.31). (Anatoliy)

- pgsql
  . pg_escape_literal関数とpg_escape_identifier関数の追加
    Added pg_escape_literal() and pg_escape_identifier() (Yasuo)

- SPL
  . #60560バグを修正(シリアライズしてアンシリアライズしたSplFixedArrayのgetSize(), count() が0を返し、キーが文字列になる)
    Fix bug #60560 (SplFixedArray un-/serialize, getSize(), count() return 0,
    keys are strings). (Adam)

- Tokenizer:
  . バグ#60097の修正(token_get_all関数が、ヒアドキュメントをネストする時に失敗する)
    Fixed bug #60097 (token_get_all fails to lex nested heredoc). (Nikita Popov)

- Zip:
  . libzipを0.10.1にアップグレード
    Upgraded libzip to 0.10.1 (Anatoliy)

- Fileinfo:
  . バグ#63248の修正(Windowsで、ディレクトリ下の複数のマジックファイルを読み込む)
    Fixed bug #63248 (Load multiple magic files from a directory under Windows).
      (Anatoliy)


終わりに



traitが追加されたPHP5.4に引き続き、PHP5.5でもジェネレータとコルーチンという強力な機能が追加されました。正式リリースが楽しみですね。以上PHP5.5の新機能のまとめでした。