18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP7調査(27)foreachでの内部ポインタの使い方を変更・整理

Last updated at Posted at 2015-05-02

PHP5までのforeachでは、配列の内部ポインタに関連して説明しづらい未定義の挙動がありました。これがPHP7で整理されました。

値渡しのforeachループで配列の内部ポインタを使わなくなった

PHPの配列は、「内部ポインタ」を持った構造になっています。これは「この配列をどこまで読んだか」を管理するもので、current(), next(), reset()といった滅多に使わない関数を実現するのに使われています。

ところで、PHP5まではforeachでも内部ポインタが利用されていました。

<?php
$a = [1,2,3];
foreach ($a as $v) {
    echo $v . " - " . current($a) . "\n";
}
/*
PHP5での出力
1 - 2
2 - 2
3 - 2
PHP7での出力
1 - 1
2 - 1
3 - 1
*/

この例ではforeachが配列の内部ポインタを使っているため、初回のcurrent()のタイミングで配列のコピーオンライトが起こり、上のような出力になります。

PHP7では同じ状況で配列の内部ポインタとは別のポインタが使われるようになりました。そのため、foreachループの内側でcurrent()関数などを使った場合の挙動がPHP5までとは変わっています。

また、この変更により配列のコピーオンライトが減り、性能面でもプラスに働くようです。

参照渡しのforeachループでは配列の内部ポインタを使う(PHP5と同じ)

foreachループで配列を参照渡しした場合は基本的にPHP5と同じで、内部ポインタが使われます。大半の挙動はPHP5と変わっていませんが、一部のエッジケースでは挙動が変わることもあるようです。個人的にforeachの参照渡しは怖くて使わない派なので詳細は追いかけていませんが、興味がある方はRFCを確認してみてください。

また、TraversableなオブジェクトについてはPHP5までと何も変わっていません。Traversable以外のオブジェクトのforeachは配列の参照渡しと同様の挙動です。

foreach関連opcodeの新設

PHP5ではforeachに対応するopcodeはFE_RESET FE_FETCHでした。これが、PHP7では値渡しのforeach用のFE_RESET_R FE_FETCH_Rおよび参照渡しのforeach用のFE_RESET_RW FE_FETCH_RWに分かれました。

また、PHP5では配列に対応するテンポラリ変数の解放にFREE opcodeが使われていましたが、PHP7では新設されたFE_FREE opcodeを使うようになりました。

参照

18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?