PHP7.2の新機能

[PHP7.3.0](https://qiita.com/rana_kualu/items/48e6e4baaeb265ab9fa7) / [PHP7.3.0α1](https://qiita.com/rana_kualu/items/a7c6be77e165bca0f3fc) / [PHP7.2](http://qiita.com/rana_kualu/items/40ebed78742bfdbd1065) / [PHP7.1](http://qiita.com/rana_kualu/items/f8baf30752928346bef8)

つい先日[PHP7.1.0が出たばっかり](http://qiita.com/rana_kualu/items/f8baf30752928346bef8)ですが、早くも[PHP7.2に取り込まれる機能](https://wiki.php.net/rfc#php_next_72)が幾つか決まってるみたいなので見てみます。

## [Convert numeric keys in object/array casts](https://wiki.php.net/rfc/convert_numeric_keys_in_object_array_casts)

賛成21/反対1で受理。

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

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

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

```php
	$obj = new stdClass();
	$obj->{1} = 2;
	$arr = (array)$obj; // $arr['1'] = 2になる
	
	var_dump($arr[1], $arr['1']); // どちらもNotice: Undefined offset: 1
```

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

逆もまた然りで、

```php
	$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()などにもキーの型自動変換を導入する提案です。
変換速度は多少落ちますが、仕方ないコストとして許容されるようです。

```php
	$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](https://wiki.php.net/rfc/get_class_disallow_null_parameter)

賛成15/反対3で受理。

```php
	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とかじゃなくてただのバグだろ、と思いたいのですが、実は[マニュアルに思いきり書いてある仕様](http://www.php.net/get_class)なんですよね。
しかしさすがにこの挙動はヤバいだろ、ということで、今後は引数がnullであればE_WARNINGが発生するようになります。

なお、引数無しの`get_class()`は今後もFooを返す模様。
正直、引数を必須にして引数無しは問答無用でエラーにしろと言いたい。


## [Counting of non-countable objects](https://wiki.php.net/rfc/counting_non_countables)

賛成31/反対0で受理。

[Countable](http://php.net/manual/ja/class.countable.php)ではない値をcount()に渡すとE_WARNINGが発生するようになります。
返り値そのものは変更ありません。

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


## [Deprecate png2wbmp() and jpeg2wbmp()](https://wiki.php.net/rfc/deprecate-png-jpeg-2wbmp)

賛成20/反対0で受理。

いったい誰が使っているのか謎すぎる[jpeg2wbmp](http://php.net/manual/ja/function.jpeg2wbmp.php)および[png2wbmp](http://php.net/manual/ja/function.png2wbmp.php)がE_DEPRECATEDになります。

他の画像フォーマットはいったんリソースにしてから[imagepng](http://php.net/manual/ja/function.imagepng.php)なり[imagegif](http://php.net/manual/ja/function.imagegif.php)なりで出力する形なのですが、何故かWBMPだけ直通の変換関数がありました。
ここだけ他と作りが異なっていたので、他と揃えた形になります。
PHP7.2でE_DEPRECATEDになり、PHP8.0で削除される予定です。

そもそもWBMPなんて今時使ってるところあるんですかね。
あとGDは全体的に作りが野暮ったいのをどうにかしてほしい。


## [Implement socket_getaddrinfo()](https://wiki.php.net/rfc/socket_getaddrinfo)

賛成9/反対0で受理。

[glibc](https://www.gnu.org/software/libc/)のgetaddrinfoをPHPから呼べるようにしてくれ、という[提案](https://bugs.php.net/bug.php?id=72733)。

```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](https://www.jpcert.or.jp/at/2016/at160009.html)を思い出しますが、PHP7.2を導入できるようなところなら引っかかるようなことはないでしょう。


## [Argon2 in password hash](https://wiki.php.net/rfc/argon2_password_hash)

賛成12/反対0で受理。

password_hash()関数のアルゴリズムに[Argon2](https://github.com/p-h-c/phc-winner-argon2)が追加されます。
日本語情報が全く見当たらないのでよくわからないのですが、メモリや時間使用量などを決めることでサーバ負荷に応じた強度を設定できるハッシュアルゴリズムのようです。

さらにArgon2と一言で言っても、実際はArgon2iとArgon2dの2種類があって、password_hash()の主な使い道であるパスワードハッシュについてはArgon2iを使うべきだそうです。
よくわかりませんが、Argon2を選択した際のデフォルトはArgon2iになってて、Argon2dは実装されてないみたいなので気にする必要はないようです。

```php
	// 第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](https://wiki.php.net/rfc/debugging_pdo_prepared_statement_emulation)

賛成9/反対7で受理。

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

当然みんなPDOを使うときはプリペアドステートメントを発行しているはずですが、実はデフォルト設定ではデータベースのプリペアドステートメント機能を使わず、PDOが内部でエスケープ処理を行っています。
[マニュアル](http://php.net/manual/ja/pdo.prepared-statements.php)には「もしドライバが サポートしていなくても、例外的に PDO がこの機能をエミュレートします」とか書いてますが嘘っぱちで、あえて`PDO::ATTR_EMULATE_PREPARES=false`を指定しない限り勝手にエミュレートされます。

まあそこは今回の主題ではないのでパスするとして、PDOがSQLをエミュレートした場合、実際にどのようなSQLが発行されたかがわかりません。
[PDOStatement::debugDumpParams](http://php.net/manual/ja/pdostatement.debugdumpparams.php)はバインド形式なので、実際に発行された値ではありません。
そこで実際に発行したSQLそのものを返すメソッドを追加します。


## その他

色々な古い書き方を[Deprecateにしよう](https://wiki.php.net/rfc/deprecations_php_7_2)というRFCが議論中で、これが通れば[create_function](http://php.net/manual/ja/function.create-function.php)やら[$php_errormsg](http://php.net/manual/ja/reserved.variables.phperrormsg.php)やらの過去の遺物が抹消される目出度いRFCです。
が、これ元々[PHP7.1の予定](https://wiki.php.net/rfc/deprecations_php_7_1)だったのが延期されただけで、さらに議論も[止まってる](http://markmail.org/message/axruwwohcjwxsjem)ので今後も期待薄っぽいです。


## まとめ

2016/12/19時点では7個のRFC追加が確定しています。
PHP7.1ではマイナーバージョンアップにもかかわらず28個もの機能追加があったので、PHP7.2でもきっとこれからも色々な新機能がお目見えすることでしょう。
とりあえずvar消してプロパティ型指定入れようぜ。