| ユーザフォーラムで議論/質問 | マニュアル検索 | ハイライト | ハイライトオフ | ポータル | php spot |
拡張モジュールのグローバル変数PHP 拡張モジュールのグローバル変数とはC のような言語における "グローバル" 変数とは、 特別な宣言をしなくてもすべての関数からアクセスできる変数のことです。 この昔ながらのグローバル変数には、いくつかの弱点があります。
PHP 拡張モジュールのグローバル変数は、どちらかというと "extension state (拡張モジュールの状態)" と呼んだほうが適切でしょう。 ほとんどのモジュールは、関数コールの間に自分が何をしているのかを覚えておく必要があるからです。 "counter" 拡張モジュールは、その最たる例でしょう。 基本インターフェイスでは、カウンタの値を永続化させています。 Zend や PHP にあまりなじみのないプログラマのみなさんは、counter.c で値を保存するときにこんな風にしてしまいがちです。 例1 カウンタの基本インターフェイスで値を保存するときの間違った方法 /* ... */ static long basic_counter_value; /* ... */ PHP_FUNCTION(counter_get) { RETURN_LONG(basic_counter_value); } 上っ面だけ見ればこれは正しそうに見えるでしょうし、 実際のところ単純なテストでは正しく動作します。 しかし、複数の PHP が同一スレッドで動作することもよくあります。 そんな場合は counter モジュールの複数のインスタンスが存在することになります。 そして複数のスレッドが同じカウンタの値を共有することになりますが、 これが望ましい結果でないことは明らかです。 さらに別の問題もあります。別の拡張モジュールが ある日たまたま同じ名前のグローバル変数を持つことになったとしましょう。 C のスコープの規則では、この場合コンパイルが失敗してしまう可能性があります。 さらに悪いことに、実行時エラーとなる可能性もあります。 少し頭を使う必要があるでしょう。 そこで Zend では、スレッドセーフなモジュール単位のグローバル変数をサポートしています。 モジュール単位のグローバルの宣言そのモジュールで使うグローバル変数がひとつであろうと大量であろうと、 それを構造体の中で定義したうえで構造体を宣言しなければなりません。 モジュール間での名前の衝突を防いでそれを支援するマクロが ZEND_BEGIN_MODULE_GLOBALS() や ZEND_END_MODULE_GLOBALS() そして ZEND_DECLARE_MODULE_GLOBALS() です。 これらのマクロに渡すパラメータはモジュールの短い名前で、 counter モジュールの場合は "counter" となります。 php_counter.h でのグローバル構造体の宣言の例を示します。 例2 counter モジュールのグローバル変数 ZEND_BEGIN_MODULE_GLOBALS(counter) long basic_counter_value; ZEND_END_MODULE_GLOBALS(counter) そして、これが counter.c での宣言です。 例3 counter モジュールのグローバル構造体宣言 ZEND_DECLARE_MODULE_GLOBALS(counter) モジュールグローバルへのアクセス先ほど説明したように、モジュール単位のグローバルは C の構造体の内部で宣言されており、その名前は Zend マクロで隠蔽されています。 構造体のメンバーにアクセスするための最もよい方法は、 これらのマクロを使用することです。 したがって、ほとんどすべてといっていいほどの拡張モジュールには、 ヘッダファイルのどこかに次のような宣言があります。 例4 モジュール単位のグローバルにアクセスするためのマクロ #ifdef ZTS #define COUNTER_G(v) TSRMG(counter_globals_id, zend_counter_globals *, v) #else #define COUNTER_G(v) (counter_globals.v) #endif
したがって、counter 拡張モジュールのコード内でグローバルにアクセスするには必ず
警告
グローバルにアクセスする関数は、Zend マクロで宣言するか最後の引数を
これらすべてを考慮すると、 counter_get() の新しいバージョンはこのようになります。 例5 正しい方法での基本カウンタインターフェイスの値の保存 /* php_counter.h */ ZEND_BEGIN_MODULE_GLOBALS(counter) long basic_counter_value; ZEND_END_MODULE_GLOBALS(counter) #ifdef ZTS #define COUNTER_G(v) TSRMG(counter_globals_id, zend_counter_globals *, v) #else #define COUNTER_G(v) (counter_globals.v) #endif /* counter.c */ ZEND_DECLARE_MODULE_GLOBALS(counter) /* ... */ PHP_FUNCTION(counter_get) { RETURN_LONG(COUNTER_G(basic_counter_value)); } これは正しい実装です。しかし、完全なものではありません。その理由は 拡張モジュールのライフサイクル で説明します。 |
各種マニュアル:
PHPマニュアル |
PEARマニュアル |
Smarty(英語)マニュアル |
PHP-GTKマニュアル |
「拡張モジュールのグローバル変数」をGoogle検索
|