前回の変数管理の基礎(PHPの参照とは何か) でシンボルテーブルについて説明した。これがわかっていないとPHPの変数管理はわからない。
今回は、もっとも基本的な変数の値の代入、について説明する。
code1
$a=$b
なぜ、こんな単純なことをいちいち説明しなければならないのか。
それは、
PHPの「値の代入」は、まず参照代入の形をとって、後に値をコピー
だからだ。いったん、参照代入をするので、話が少しややこしい。
figure1 $a='one';
シンボルテーブル
| 変数名 | 格納庫(zval)No. |
| a | #0001 |
格納庫(zval)
| No. | 値 | type |
| #0001 | one | string |
通常の値の代入なのだから、値がコピーされて、次のようになると想像しては「いけない」
| この塗りつぶしは、追加された部分 | この塗りつぶしは、変化があった部分 | この塗りつぶしは、削除された部分 |
figure2
シンボルテーブル
| 変数名 | 格納庫(zval)No. |
| a | #0001 |
| b | #0002 |
格納庫(zval)
| No. | 値 | type |
| #0001 | one | string |
| #0002 | one | string |
※こうはならない
以下のようになる。
figure3-1
シンボルテーブル
| 変数名 | 格納庫(zval)No. |
| a | #0001 |
| b | #0001 |
格納庫(zval)
| No. | 値 | type | is_ref |
| #0001 | one | string | 0 |
おや、これでは参照代入 $b
= & $a; と同じではないか。このままだと、 $a='two'; としたら、$b の値も変わってしまうではないか。参照:
変数管理の基礎(PHPの参照とは何か)
その疑問は的を射ている。しかし、PHPはちゃんと解決手段を持っている。上のzvalの表にいつの間にか加わったis_ref項目をみてほしい。内部に格納庫(zval)No毎に is_ref というフラグを持っていて、参照代入ならis_ref=1、 通常代入なら is_ref=0 とセットしている。
今回の場合は、 is_ref=0 である。
figure3-2
| is_ref | 0 | 参照代入されたことはない |
| 1 | 過去に参照代入されている |
この時点でようやく、値がコピーされて以下のようになる。(上の「想像してはいけない」と同じ状態になる)
figure4
シンボルテーブル
| 変数名 | 格納庫(zval)No.(zval) |
| a | #0001 |
| b | #0002 |
格納庫(zval)
| No. | 値 | type |
| #0001 | two | string |
| #0002 | one | string |
通常の代入であれば、最初っから値をコピーすればいいじゃん、というのは、未来にはそうなるかもしれないが、メモリという大切なリソースを効率的に使うための方策としてこのような形式となっている。通常代入時($b=$a時)に値をコピーしてしまうと、同じ内容のものをメモリに持っておくことになる。それは非効率だから、今後、違うことになるときまでは一緒にいましょうね、ということだ。さらに、違う値になることがないのであれば、値のコピーそのものがいらないということもあり得よう。
このような方式を copy-on-write(またはリファレンスカウンティングreferences-counting) 方式という。
参照代入とメモリの貴重さを知ると、どうしても、参照代入を多用したくなる。しかし、それは浅はかというものだ。むしろ、参照代入は、「何かと何かを結びつける」「ある変数名とある変数名を結びつける」という点において威力を発揮させるものである。多くの場合メモリの効率的運用はPHPに任してしまったほうが、楽だし、実際に効率がよくなろう。むしろ、理解不足のまま参照代入を使うと、かえって非効率、低速のプログラムを知らず知らずのうちに組んでしまうこともある。私も反省している。
参照代入の中途半端使いがダメな理由は、具体的には、ここの記事が参考になる。
(
参照を多用することの危険性のページの後ろの方。)
■unsetの働き
前回、unsetは、シンボルテーブルから変数名と格納庫(zval)No.のひも付きを消去するだけだと述べた。すると、通常代入の際、格納庫(zval)No.に実体のゴミが残ってしまう。
figure5
シンボルテーブル
| 変数名 | 格納庫(zval)No.(zval) |
| b | #0002 |
格納庫(zval)
| 格納庫(zval)No.(zval) | 値 | refcount |
| #0001 | two | 0 |
| #0002 | one | 1 |
このように#0001番地のように、すでに unset された 元の$a の値がゴミとして残るのはまずい。そこで、PHPでは、格納庫(zval)No毎に、refcountというのを持っている。refcountはいくつの変数名と紐ついているか、という数字である。参照されるごとに増えていき、参照が解除されるごとに減っていく。これがゼロになると、その格納庫(zval)No.は解放される仕組みとなっている。したがって、ゴミはきちんと掃き掃除され、以下のようになる。
figure6
シンボルテーブル
| 変数名 | 格納庫(zval)No.(zval) |
| b | #0002 |
格納庫(zval)
| 格納庫(zval)No.(zval) | 値 | refcount |
| #0002 | one | 1 |
次回は配列型(array)の管理方法について説明したい。
PHP変数管理の目次はこちら