PHPのドキュメントを見てると、『シンボルテーブル』という聞き慣れないワードが出てきます。
これはコンピュータの基礎知識とかで勉強してると出てくるものなんですが、プログラミングをするかぎりでは何のことか分かりません。
プログラミング的に言うと、宣言されてる変数のリストのことです。
『シンボルテーブル』はプログラムが実行されるときに処理に使う変数をまとめたデータ構造のことを言います。一般的にハッシュテーブルであることが多い。
このワードはPHPプログラミングのものではなく、コンピュータがプログラムをどのように実行するのか? という、もっと根源的なメカニズムを表現するときに使われるものです。
だから、コンピュータの基礎知識などを勉強して試験を受けるときは出てくるんですが、ほとんどの人にはプログラミングしていても触れることはあまりありません。
僕自身、『変数を格納するハッシュテーブル』という覚え方をしていたので正直さっぱり分からなかった。
プログラミングでは『宣言されている変数のリスト』だと思ってていいです。
さっそくPHPでシンボルテーブルの内容を見てみましょう。
get_defined_vars() で確認。
PHPにかぎらずプログラミング言語には使ってる変数を取得する関数などなんらかの方法が用意されています。
PHPには get_defined_vars() があるのでそれで確認してみましょう。
<?php
$val1 = 'sample1';
$val2 = 'sample2';
var_dump(get_defined_vars());
array(9) {
["_GET"]=>
array(0) {
}
["_POST"]=>
array(0) {
}
["_COOKIE"]=>
array(0) {
}
["_FILES"]=>
array(0) {
}
["argv"]=>
array(1) {
[0]=>
string(8) "prog.php"
}
["argc"]=>
int(1)
["_SERVER"]=>
array(17) {
・
省略
・
}
["val1"]=>
string(7) "sample1"
["val2"]=>
string(7) "sample2"
}
$_GET などのおなじみのグローバル変数も出てきましたね? この関数はシンボルテーブルにある情報を配列に格納して返すだけのシンプルなもの。
スコープの情報も持っているシンボルテーブル
シンボルテーブルには変数に関する情報がすべて詰まっています。変数は有効範囲(スコープ)がありますが、もちろんスコープ情報もある。
今度は、関数内で変数リストを取得してみましょう。
<?php
$g_val1 = 'sample1';
$g_val2 = 'sample2';
function test($param1, $param2){
$l_val1 = 'sample3';
$l_val2 = 'sample4';
var_dump(get_defined_vars());
}
test('sample5', 'sample6');
array(4) {
["param1"]=>
string(7) "sample5"
["param2"]=>
string(7) "sample6"
["l_val1"]=>
string(7) "sample3"
["l_val2"]=>
string(7) "sample4"
}
今度は、関数内だけで有効な変数のリストが表示されました。get_defined_vars() がシンボルテーブルにあるスコープ情報を見て、その関数内だけで有効な変数からリストを作ったからです。
関数のパラメータも変数の一種なのでそれも入ってる。
言い忘れてましたがシンボルテーブルはデータの型情報ももってます。
ここでちょっとサンプルに修正を入れて、関数内でグローバル変数を参照できるようにしてみましょう。
<?php
$g_val1 = 'sample1';
$g_val2 = 'sample2';
function test($param1, $param2){
global $g_val1;
$l_val1 = 'sample3';
$l_val2 = 'sample4';
var_dump(get_defined_vars());
}
test('sample5', 'sample6');
array(5) {
["param1"]=>
string(7) "sample5"
["param2"]=>
string(7) "sample6"
["g_val1"]=>
&string(7) "sample1"
["l_val1"]=>
string(7) "sample3"
["l_val2"]=>
string(7) "sample4"
}
g_val1 も表示されましたね?
ここでグローバル変数のデータ型に注目して下さい。アンパサンド(&)が付いてます。
これは、CやC++などをしている人にはピンとくる話ですが、ポインタ変数を表す。
変数はメモリ上のデータ位置を示すアドレスを持ってるんですが、ポインタ変数では、値にそのアドレスを指定します。
(普通の変数は値そのものを直接入れる。)
関数内でg_val1 の値を参照。
↓
ポインタ変数なので、値に入ってるアドレスにある変数を見るように飛ぶ。
↓
飛んだ先の変数値をあたかも自分の値(sample1)かのように振る舞う。
globalキーワードで参照する変数はポインタだったんだと、書いている今気づきました。なかなか奥深いですね?
これは参照以外でも同じ。関数内で値を変更するとアドレスの先にある本物の g_val1 の値を変更するので、関数の外でも g_val1 の値が変わります。
訂正
いわゆるPHP変数のリファレンス(参照)はC系言語のポインタ変数とはまったくの別物。
リファレンスは、ファイルのハードリンクのようなものとイメージしたらいい。
正式な再編集・修正は後日。
最後にglobalキーワードを使わなくてもどこからでも参照できる特殊なグローバル変数 $_GET についても見てみましょう。
<?php
$g_val1 = 'sample1';
$g_val2 = 'sample2';
var_dump($_GET);
function test($param1, $param2){
global $g_val1;
$l_val1 = 'sample3';
$l_val2 = 'sample4';
$_GET = array('a', 'b', 'c');
var_dump(get_defined_vars());
}
test('sample5', 'sample6');
var_dump($_GET);
array(0) {
}
array(5) {
["param1"]=>
string(7) "sample5"
["param2"]=>
string(7) "sample6"
["g_val1"]=>
&string(7) "sample1"
["l_val1"]=>
string(7) "sample3"
["l_val2"]=>
string(7) "sample4"
}
array(3) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
}
$_*** のグローバル変数は、関数内で宣言された変数ではないので、get_defined_vars() では表示されません。でも関数内で変えた値はプログラム全体に反映されてます。
厳密にはプログラミング上、PHPに変数宣言はありません。最初に変数に値を入れると自動的に変数宣言が行われます。
変数宣言とは、シンボルテーブルに変数が登録されることを指します。
$_***はプログラム実行の早い段階で設定されます。一方、globalキーワード演算子は、ローカル(関数)内で、ポインタ変数として宣言されます。
シンボルテーブル上は、$_***はひとつだけですが、g_val1 はグローバル変数用と、ローカルのポインタ変数用の同名の変数名が登録されます。
シンボルテーブルは変数だけに使うもんじゃない?
定数も変数の一種なのでシンボルテーブルで管理されます。
シンボルは厳密には、名前とその中身を関連付けるという意味で、それをテーブルとしてまとめて管理したものがシンボルテーブル。
たとえば、functionやクラスなどもシンボルです。
クラスオブジェクトは変数に代入できるし、PHPではパラメータにfunction()のコードそのものを渡すことができます。
(変数にfunctionを代入するイメージ。)
だからワードがバリアブルテーブルじゃないんでしょうね? 変数だけに使うもんじゃないということが想像できます。
『シンボルテーブル』が出てきたら、前後の文章の内容から何が入っているのか、何を意味しているのか推察する必要があります。