Edited at
PHPDay 10

PHP7.2の新機能

PHP7.3.0 / PHP7.3.0α1 / PHP7.2 / PHP7.1

つい先日PHP7.1.0が出たばっかりですが、早くもPHP7.2に取り込まれる機能が幾つか決まってるみたいなので見てみます。


Convert numeric keys in object/array casts

賛成21/反対1で受理。

配列のキーに数値型文字列を入れた場合、自動的に数値になります。

    $arr['1'] = 2; // $arr[1]=2になる

で、どのような場合にもこのような変換が徹底していればそれはそれで問題ないのですが、実は突破口が色々と存在します。

    $obj = new stdClass();

$obj->{1} = 2;
$arr = (array)$obj; // $arr['1'] = 2になる

var_dump($arr[1], $arr['1']); // どちらもNotice: Undefined offset: 1

オブジェクトをsettype()で配列にしたり、(array)キャストやget_object_vars()した場合、数値型文字列キーが数値に変換されず文字列のままキーになり、普通にアクセスすることができなくなってしまいます。

逆もまた然りで、

    $arr = [1=>2];

$obj = (object)$arr; // $obj::1 に値が入る

var_dump($obj->{1}); // Notice: Undefined property: stdClass::$1

(object)キャストするとstdClassになりますが、プロパティ'1'ではなくプロパティ1に値が入ってしまい、これまた通常ではアクセス不可能です。

ということでそのあたりをどうにかするため、settype()やget_object_vars()などにもキーの型自動変換を導入する提案です。

変換速度は多少落ちますが、仕方ないコストとして許容されるようです。

    $obj = new stdClass();

$obj->{1} = 2;
$arr = (array)$obj; // $arr[1] = 2になる
var_dump($arr[1], $arr['1']); // どちらも2になる

まあ実際こういうのって、わざわざ変な組み方でもしない限り出会うことなんてまずないですけどね。

というかsettype()なんて関数初めて知ったわ。


get_class() disallow null parameter

賛成15/反対3で受理。

    class Foo{

function bar($repository){
$result = $repository->find(1);
echo get_class($result);
}
}

class Repository1{
function find($id){
return new stdClass();
}
}
class Repository2{
function find($id){
return 1;
}
}
class Repository3{
function find($id){
return null;
}
}

$foo = new Foo();
$foo->bar(new Repository1()); // stdClass
$foo->bar(new Repository2()); // Warning: get_class() expects parameter 1 to be object, integer given
$foo->bar(new Repository3()); // Foo ← !?

いやこれRFCとかじゃなくてただのバグだろ、と思いたいのですが、実はマニュアルに思いきり書いてある仕様なんですよね。

しかしさすがにこの挙動はヤバいだろ、ということで、今後は引数がnullであればE_WARNINGが発生するようになります。

なお、引数無しのget_class()は今後もFooを返す模様。

正直、引数を必須にして引数無しは問答無用でエラーにしろと言いたい。


Counting of non-countable objects

賛成31/反対0で受理。

Countableではない値をcount()に渡すとE_WARNINGが発生するようになります。

返り値そのものは変更ありません。

元々スカラ値とかを渡すと1になってたのがおかしいのでIllegalArgumentExceptionあたりにした方がいいと思うのですが、今さらそうするのはさすがに影響が大きすぎるので、控えめに警告を発するだけにしたようです。


Deprecate png2wbmp() and jpeg2wbmp()

賛成20/反対0で受理。

いったい誰が使っているのか謎すぎるjpeg2wbmpおよびpng2wbmpがE_DEPRECATEDになります。

他の画像フォーマットはいったんリソースにしてからimagepngなりimagegifなりで出力する形なのですが、何故かWBMPだけ直通の変換関数がありました。

ここだけ他と作りが異なっていたので、他と揃えた形になります。

PHP7.2でE_DEPRECATEDになり、PHP8.0で削除される予定です。

そもそもWBMPなんて今時使ってるところあるんですかね。

あとGDは全体的に作りが野暮ったいのをどうにかしてほしい。


Implement socket_getaddrinfo()

賛成9/反対0で受理。

glibcのgetaddrinfoをPHPから呼べるようにしてくれ、という提案

    // よくわからん

$addrinfo = socket_addrinfo_lookup('localhost', 2000, ['ai_family' => AF_INET, 'ai_socktype' => SOCK_STREAM ]);
$sockaddr = reset($addrinfo);
// ソケットを取得
$sock = socket_addrinfo_bind($sockaddr);
// 詳細を配列で取得
$explain = socket_addrinfo_explain($sockaddr);

socket_addrinfo_lookup()、socket_addrinfo_connect()、socket_addrinfo_bind()、socket_addrinfo_explain()の4関数が追加されます。

getaddrinfoといえばCVE-2015-7547を思い出しますが、PHP7.2を導入できるようなところなら引っかかるようなことはないでしょう。


Argon2 in password hash

賛成12/反対0で受理。

password_hash()関数のアルゴリズムにArgon2が追加されます。

日本語情報が全く見当たらないのでよくわからないのですが、メモリや時間使用量などを決めることでサーバ負荷に応じた強度を設定できるハッシュアルゴリズムのようです。

さらにArgon2と一言で言っても、実際はArgon2iとArgon2dの2種類があって、password_hash()の主な使い道であるパスワードハッシュについてはArgon2iを使うべきだそうです。

よくわかりませんが、Argon2を選択した際のデフォルトはArgon2iになってて、Argon2dは実装されてないみたいなので気にする必要はないようです。

    // 第3引数はオプション

$hash = password_hash('password', PASSWORD_ARGON2I, [
'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS
]);

// 普通にverifyできる
$verify = password_verify('password', $hash);

元々はPHP7.4でpassword_hash()のデフォルトをPASSWORD_ARGON2Iにするぞ、という主張もあったみたいですが、RFCではその提案は無くなっていました。

その甲斐もあってか満場一致で採択となりました。


Debugging PDO Prepared Statement Emulation

賛成9/反対7で受理。

PDOStatement::activeQueryString()を追加するという提案。

当然みんなPDOを使うときはプリペアドステートメントを発行しているはずですが、実はデフォルト設定ではデータベースのプリペアドステートメント機能を使わず、PDOが内部でエスケープ処理を行っています。

マニュアルには「もしドライバが サポートしていなくても、例外的に PDO がこの機能をエミュレートします」とか書いてますが嘘っぱちで、あえてPDO::ATTR_EMULATE_PREPARES=falseを指定しない限り勝手にエミュレートされます。

まあそこは今回の主題ではないのでパスするとして、PDOがSQLをエミュレートした場合、実際にどのようなSQLが発行されたかがわかりません。

PDOStatement::debugDumpParamsはバインド形式なので、実際に発行された値ではありません。

そこで実際に発行したSQLそのものを返すメソッドを追加します。


その他

色々な古い書き方をDeprecateにしようというRFCが議論中で、これが通ればcreate_functionやら$php_errormsgやらの過去の遺物が抹消される目出度いRFCです。

が、これ元々PHP7.1の予定だったのが延期されただけで、さらに議論も止まってるので今後も期待薄っぽいです。


まとめ

2016/12/19時点では7個のRFC追加が確定しています。

PHP7.1ではマイナーバージョンアップにもかかわらず28個もの機能追加があったので、PHP7.2でもきっとこれからも色々な新機能がお目見えすることでしょう。

とりあえずvar消してプロパティ型指定入れようぜ。