はじめに
PHPの変数は「値のコピー」ではなく、「値への参照」を通じて扱われる場面が多くあります。
特に&
演算子で参照を作成したとき、内部的にどのような構造になっているのかを確認できるのが xdebug_debug_zval()
関数です。
今回は、リファレンスカウント(refcount) と is_ref の変化を、実際のコードで追いかけながら理解していきましょう。
目次
💡 xdebug_debug_zval()とは?
xdebug_debug_zval()
は、指定した変数の内部情報(値・リファレンスカウント・参照フラグ)を出力する関数です。変数がどのようにメモリ上で扱われているのかを「見える化」できる、PHPの学習において非常に有用なデバッグツールです。
🧪 実験コード
<?php
declare(strict_types=1);
error_reporting(-1);
$num = 123;
xdebug_debug_zval('num');
$num2 = &$num;
xdebug_debug_zval('num');
xdebug_debug_zval('num2');
$num3 = &$num;
xdebug_debug_zval('num');
xdebug_debug_zval('num2');
xdebug_debug_zval('num3');
unset($num3);
xdebug_debug_zval('num');
xdebug_debug_zval('num2');
📊 実行結果
num: (refcount=0, is_ref=0)=123
num: (refcount=2, is_ref=1)=123
num2: (refcount=2, is_ref=1)=123
num: (refcount=3, is_ref=1)=123
num2: (refcount=3, is_ref=1)=123
num3: (refcount=3, is_ref=1)=123
num: (refcount=2, is_ref=1)=123
num2: (refcount=2, is_ref=1)=123
✅ この結果は 正解。PHP8 + Xdebugの仕様に沿った正しい挙動です。
🔍 解説:各ステップで何が起きているか
① $num = 123;
まだ参照は存在せず:
(refcount=0, is_ref=0)
👉 単体のスカラー値。最適化の都合上、refcountは0になることがあります。
② $num2 = &$num;
$num2
が $num
の参照になり、2変数が同じ値を共有。
(refcount=2, is_ref=1)
③ $num3 = &$num;
参照グループに $num3
が加わり、3つが同一値を共有。
(refcount=3, is_ref=1)
④ unset($num3);
$num3
が破棄され、残りは $num
と $num2
。
(refcount=2, is_ref=1)
🧠 図解:リファレンス関係のイメージ
初期状態
┌──────────┐
│ $num=123 │ refcount=0 is_ref=0
└──────────┘
参照を作成 ($num2 = &$num)
┌──────────────┐
│ $num ↔ $num2 │ refcount=2 is_ref=1
└──────────────┘
さらに参照 ($num3 = &$num)
┌──────────────────────┐
│ $num ↔ $num2 ↔ $num3 │ refcount=3 is_ref=1
└──────────────────────┘
解除 (unset($num3))
┌──────────────┐
│ $num ↔ $num2 │ refcount=2 is_ref=1
└──────────────┘
🧩 まとめ
ステップ | 状況 | refcount | is_ref | 意味 |
---|---|---|---|---|
$num=123 | 単独の値 | 0 | 0 | 通常の変数 |
$num2=&$num | 参照1つ追加 | 2 | 1 | 2変数が共有 |
$num3=&$num | 参照さらに追加 | 3 | 1 | 3変数が共有 |
unset($num3) | 参照1つ解除 | 2 | 1 | 残り2変数が共有 |
✅ 試験対策ポイント
&
演算子で参照を作ると、同一の値コンテナを共有する。- このとき is_ref=1 になり、refcount が共有数を示す。
unset()
で参照を1つ破棄しても、他の変数が残っていれば値は保持される。- Xdebugを使えば、これらを実際に確認できる。