ユアマイスター(https://yourmystar.jp/)のインターンの京極です。
CakePHPを使っていると多用する、compact関数について調べてみました。
compact関数とは
与えられた文字列の配列から、その文字列がキーで、文字列の一致する変数の値の配列を返してくれる関数です。
$a = 3;
$b = 'hogehoge';
var_dump(compact('a', 'b'));
と書くと、以下の結果が得られます。
array(2) {
["a"]=>
int(3)
["b"]=>
string(8) "hogehoge"
}
PHPのソースコードでどのように値が展開されているのか少し気になったのでコードを読んでみました。
compact関数の内部
調べてみるとPHPのソース内で、ext/standard/array.cファイルにcompact関数の実体がありました。
compact関数に配列自体が渡ってきた場合の対応(compact([['a']])のような)等がありましたが、compact関数としてポイントとなるコードは以下の部分です。
if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
ZVAL_DEREF(value_ptr);
ZVAL_COPY(&data, value_ptr);
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
zend_hash_find_ind関数で与えられた変数名の値を取り出してそれをリターン用の配列に入れている、そのものずばりな実装でした。
eg_active_symbol_table変数は、その時点のスコープに応じた変数テーブルで、zend_rebuild_symbol_table関数を使って生成されていました。
PHPのコードを書いていてcompact関数は変数の値を取り出す処理をどのように実装しているのか?と疑問に思ったのですが、PHP自体の実装では、変数へのアクセスがハッシュテーブルを通してそもそもできるので、単にハッシュテーブルから特定のキー(今回で言えば変数名の文字列)から取り出した値を配列にするというシンプルなものでした。
今回コードを読んで分かったことは、"this"という文字列が来た時、$thisの値を入れてくれる仕組みがcompact関数に用意されていたことでした。
実際に使うケースは無さそうですが、以下のコードで動作を確認できました。
class A
{
function hoge() {
$a = 3;
var_dump(compact('this', 'a'));
}
}
(new A())->hoge();
'this'にAクラスのインスタンスが入っていることを確認できます。
array(2) {
["this"]=>
object(A)#1 (0) {
}
["a"]=>
int(3)
}
PHPでcompact
蛇足ですが、PHP自体でcompact関数を実装するとどうなるか書いてみました。$GLOBALSが用意されているので割と簡単に書くことができます。
以下のコードではあまりに簡略化しすぎて基本的な要件も満たせていませんでした。下記でコメントいただきました、@tadsanさんのGistのコードをご参照ください。
function compact2(...$array) {
$ret = [];
foreach ($array as $v) {
$ret[$v] = $GLOBALS[$v];
}
return $ret;
}
$a = 3;
$b = 'hogehoge';
var_dump(compact('a', 'b'));
まとめ
PHPをソースコードから読むという経験は勉強になりました。気になった関数があったら気軽に読んでみたいと思います。