phpの参照引数
実態の代入時では、$a,$bは同じhogeを参照しています。ただし、$aを更新するとき、新規にhogeを割り当て、refcount=1として(この時、$bのrefcountも1になります。)更新(Copy On Write)します。
なお、参照カウンターは変数に属するカウンターとして見るより、'hoge'という実態に付属する参照されるカウンターとして理解するほうがいいかと思います。
参照引数とする関数で、参照変数でコールした場合です。
参照変数としてtest($a)がコールされ、test()の宣言は参照引数のため、テンポラリ変数に参照変数のまま$aを設定します(この時refcount=3)。次にテンポラリ参照変数を、参照変数$cに設定することです。従って'hoge'を参照している変数は4となり、$cの参照カウンタは4となります。
test($a)で参照型のテンポラリ変数に$aを設定する場合、$aは実態型変数ですので、新規に'hoge'を割り当て、$aはその割り当てた'hoge'にリンクします。この時点でrefcount=1($bもrefcount=1となっているはずです。test()からコールした後の$aはrefcount=1となっています。)となります。それをテンポラリ変数、テンポラリ変数を$cへとセットするため、$cの参照カウンタは3になっています。
Copy On Writeにより、すべての変数は実態型ゆえ、同じ'hoge'をリンクしておけばいいわけです。
$aは参照型で、しかも参照カウンターは$bとの絡みで、2となっているため、テンプ変数に$aを設定する場合、test()の宣言は実態ゆえ、新規に'hoge'を割り当て、実態型テンプ変数はそこにリンクします。それを実態型変数$cに設定するため、refcount=2となっているわけです。この場合、COWとするのでなく、関数内で更新される前提で処理されるようです。
なお、参照カウンターは変数に属するカウンターとして見るより、'hoge'という実態に付属する参照されるカウンターとして理解するほうがいいかと思います。
<?php $a = 'hoge'; $b = $a; xdebug_debug_zval('a'); xdebug_debug_zval('b'); ?> [root@localhost test]# php c.php a: (refcount=2, is_ref=0)='hoge' b: (refcount=2, is_ref=0)='hoge'参照の代入時では、$a,$bは同じhogeを参照(refcount=2)しています。$a = 'hoge'ではis_ref=0ですが、=&する事は、$bに参照として$aを設定するでけでなく、$aをも参照型にすることでもあります。
<?php $a = 'hoge'; $b = &$a; xdebug_debug_zval('a'); xdebug_debug_zval('b'); ?> [root@localhost test]# php c.php a: (refcount=2, is_ref=1)='hoge' b: (refcount=2, is_ref=1)='hoge'
本題
で、関数引数のそれを見てみたいと思います。関数引数の取り扱い実装は、内部的にテンポラリ変数をアロケートし(Cでのスタック領域みたいのもの。でないと再帰処理ができない。)、コール先関数で、テンポラリ変数を引数変数に設定している。という解釈です。この前提でみてみると、つじつまが合うようなので・・・。参照引数とする関数で、参照変数でコールした場合です。
参照変数としてtest($a)がコールされ、test()の宣言は参照引数のため、テンポラリ変数に参照変数のまま$aを設定します(この時refcount=3)。次にテンポラリ参照変数を、参照変数$cに設定することです。従って'hoge'を参照している変数は4となり、$cの参照カウンタは4となります。
<?php $a = 'hoge'; $b = &$a; xdebug_debug_zval('a'); print "\n"; test($a); xdebug_debug_zval('a'); function test(&$c) { xdebug_debug_zval('c'); print "\n"; } ?> [root@localhost test]# php a.php a: (refcount=2, is_ref=1)='hoge' c: (refcount=4, is_ref=1)='hoge' a: (refcount=2, is_ref=1)='hoge'参照引数とする関数で、実態変数でコールした場合です。
test($a)で参照型のテンポラリ変数に$aを設定する場合、$aは実態型変数ですので、新規に'hoge'を割り当て、$aはその割り当てた'hoge'にリンクします。この時点でrefcount=1($bもrefcount=1となっているはずです。test()からコールした後の$aはrefcount=1となっています。)となります。それをテンポラリ変数、テンポラリ変数を$cへとセットするため、$cの参照カウンタは3になっています。
<?php $a = 'hoge'; $b = $a; xdebug_debug_zval('a'); print "\n"; test($a); xdebug_debug_zval('a'); function test(&$c) { xdebug_debug_zval('c'); print "\n"; } ?> [root@localhost test]# php a.php a: (refcount=2, is_ref=0)='hoge' c: (refcount=3, is_ref=1)='hoge' a: (refcount=1, is_ref=0)='hoge'実態引数とする関数で、実態変数でコールした場合です。
Copy On Writeにより、すべての変数は実態型ゆえ、同じ'hoge'をリンクしておけばいいわけです。
<?php $a = 'hoge'; $b = $a; xdebug_debug_zval('a'); print "\n"; test($a); xdebug_debug_zval('a'); function test($c) { xdebug_debug_zval('c'); print "\n"; } ?> [root@localhost test]# php a.php a: (refcount=2, is_ref=0)='hoge' c: (refcount=4, is_ref=0)='hoge' a: (refcount=2, is_ref=0)='hoge'実態引数とする関数で、参照変数でコールした場合です。
$aは参照型で、しかも参照カウンターは$bとの絡みで、2となっているため、テンプ変数に$aを設定する場合、test()の宣言は実態ゆえ、新規に'hoge'を割り当て、実態型テンプ変数はそこにリンクします。それを実態型変数$cに設定するため、refcount=2となっているわけです。この場合、COWとするのでなく、関数内で更新される前提で処理されるようです。
<?php $a = 'hoge'; $b = &$a; xdebug_debug_zval('a'); print "\n"; test($a); xdebug_debug_zval('a'); function test($c) { xdebug_debug_zval('c'); print "\n"; } ?> [root@localhost test]# php a.php a: (refcount=2, is_ref=1)='hoge' c: (refcount=2, is_ref=0)='hoge' a: (refcount=2, is_ref=1)='hoge'