by the dreamland

夢の国の近くで暮らすエンジニア(おもにインフラ系)のブログ

グローバル変数「$GLOBAL」について

http://white-mode.seesaa.net/article/305405576.html

上記のサイトよりコピペ

 

【PHP】グローバル浸食 $GLOBALSにおけるシステムが吹っ飛ぶレベルの危険な仕様

寝ようとしている時にふとPHPの仕様レベルの設計で引っかかって半日くらい原因解明にとられた事を思い出しました。

で、この仕様ってPHP4で出たのは覚えているのですが、
PHP5になっても同じ仕様だったような気がしていたすごく気になったので、
日付が変わった時間にパソコンを付けてチェックコードを出して見た訳なんですが、

PHPの恐るべき恐怖がここに詰まってる

すごいなぁ。
おそろしいなぁ。
PHP5でも動くってことは、これは完全にPHPの仕様としてのシステムなんだよなぁ。



問1.下記のコードにて出力されるメッセージを答えなさい
$dir = 'directory path';
echo $dir;
まぁ、これはそのまま
directory path
と出力されます。



問2.
$GLOBALS['dir'] = 'BaseDir/';
$dir = 'directory path';
echo $GLOBALS['dir'] . $dir;
このソースコードの結果は
directory pathdirectory path
となります。
BaseDir/directory path
とはなりません。



初心者どころか、PHPでメシ食ってるような人でも発生させてしまう危険性のある
グローバル浸食という現象です。
※それっぽいワードが見つからなかったので自分で名付けてみましたw

ちなみに$GLOBALSで始まる配列はグローバルスコープと呼ばれる物でして、
「関数とかクラスの外で定義した変数を関数とかクラスで使いたいです」
「でもglobalでいちいち変数を呼び出すとか、変数を侮辱してるみたいで他の言語でも嫌われてアンチが沸くような記述だからやりたくない」
「defineは変更出来ないから、変更出来なきゃダメダメです」
「だったらこの$GLOBALS変数で一気に解決よ」

というような使い方をされる事が多いです。


多いんです。


小さいシステムの上ではdefineだったりするのに、
大きいシステムでは、固定値の一括管理のつもりなのか、
$GLOBALSにデータベースのパスワードとか、ディレクトリのなんたらの情報とか、ライブラリで使用する設定用配列とか、
グローバルという名前が付いているからという理由で
重要度があって尚かつ関数やクラス内などあらゆる場所で使われる可能性があるデータなんだし使っちゃおう
といった感じに使われる事がちょこちょこあります。



で、$GLOBALSの動きをちゃんと説明しますと、
$GLOBALSはPHPソースコード内で定義されるすべての変数を保持する変数と言う意味合いを持っています。
ですのでvar_dump($GLOBALS)でいろいろと中を覗いてみてください。
$_GETや$_POST、$_SERVERのような定数みたいなもんだと思っていた変数が
['_GET'] ['_POST']のように連想配列で格納されています。
もちろん['GLOBALS']と自分自身も格納しているわけです。

var_dumpの後ろで一つ変数を作ってみてください。
そうすると$GLOBALS['今作った変数']というような項目が増えます。



大体この動きで理解出来たと思いますが、
PHPで使用されるすべての変数は$GLOBALSから使用も変更も出来るし、
$GLOBALSの中にある変数も$・・・という物からアクセスする事が出来ます。
※関数やクラスの中で作られる変数は若干特殊な為、グローバルスコープからは見えません

故にグローバルだからという理由だけでグローバル変数に大切なデータをつっこんでおくと、
うっかり普通に定義した変数の名前を被らせて、そのまま後の処理でめちゃくちゃなバグが起こる危険性があります。



グローバルに使いたい定数はちゃんとdefineで定義しておきましょう。






あっ、ちなみに私はグローバルスコープとかに普通に定数とかつっこんじゃってますw

PHPの変数名には使用出来ない文字ってのがあるので
$GLOBALS['@dir']のように変数名に使えない@から始まる名前を与える事で、
グローバル浸食の発生を押さえ込む事が出来ます。

defineは確かに強固だけど、データによっては配列として保持しておきたいじゃないですか。
とあるライブラリを狙い通りに起動させる為には配列の定数を入れることできっちり動くってパターンもあるんですよ。

そういう理由もあって
固定値でしか使用しないものはdefine
配列として置くと後で見やすい物はGLOBALS
と使い分けていたりします。


どうしても$GLOBALSにデータを突っ込んでおきたい場合は
 $GLOBALS['@~']な名前で定義すればグローバル侵食は起こらない!