PHP マニュアル
前のページ | 第 46章Zend API: PHP のコアをハックする | 次のページ

# 変数の作成

あなたの作成する拡張モジュールが PHP スクリプトとの間でデータ交換を行うにあたって、 もっとも重要な問題は変数の作成です。この節では、 PHP がサポートする変数の型を扱う方法を示します。

## 概要

実行中のスクリプトによって「外部から」見える変数を新しく作成するには、 まず新しい `zval` コンテナを確保してそこに値を格納し、 それを Zend の内部シンボルテーブルに登録しなければなりません。 これは、変数を作成する際のお決まりの手順です。

    zval *new_variable;

    /* 新しいコンテナを確保して初期化します */
    MAKE_STD_ZVAL(new_variable);

    /* 型や値をここで設定します。これ以降の節を参照ください */

    /* この変数を "new_variable_name" という名前でシンボルテーブルに登録します */
    ZEND_SET_SYMBOL(EG(active_symbol_table), "new_variable_name", new_variable);

    /* これで、$new_variable_name という名前でスクリプトからアクセスできるようになります */

マクロ `MAKE_STD_ZVAL` は、`ALLOC_ZVAL` を使用して新しい `zval` コンテナを確保し、 `INIT_ZVAL` でそれを初期化します。 これを書いている時点の Zend の実装では、初期化 というのは参照カウンタを `1` にして `is_ref` フラグをクリアすることを指します。 しかし、これは今後拡張される可能性があります。そのため、 `ALLOC_ZVAL` を直接使用するのではなく `MAKE_STD_ZVAL` を用いるようにしておくことが大切です。 実行速度を最適化したい場合 (そして、ここで明示的に `zval` コンテナを初期化する必要がない場合) は `ALLOC_ZVAL` を使用することも可能です。 しかし、データの整合性を保証できなくなるため、 この方法は推奨されません。

`ZEND_SET_SYMBOL` の役割は、新しい変数を Zend のシンボルテーブルに登録することです。このマクロは、 その値が既にシンボルテーブルに存在するかどうかを調べ、 もし存在すれば新しいシンボルを参照に変換します (古い `zval` コンテナが使用していたメモリは、 自動的に開放されます)。速度を気にする必要がない場合には この方法がお勧めです。メモリの使用量を抑えることができます。

`ZEND_SET_SYMBOL` は、マクロ `EG` 経由で Zend executor のグローバル変数を使用することに注意しましょう。 `EG(active_symbol_table)` を指定することで、 現在アクティブなシンボルテーブルを、アクティブなローカルスコープで扱えるようになります。 ローカルスコープは、その関数が関数内でコールされたかどうかによって異なります。

とにかく速度を最適化したい、メモリの消費量はあまり気にしない という場合には、同じ値が既存の変数に登録されているかどうかの チェックを省略することができます。その代わりに、 zend_hash_update() を使用して 強制的にシンボルテーブルに挿入します。

    zval *new_variable;

    /* 新しいコンテナを確保して初期化します */
    MAKE_STD_ZVAL(new_variable);

    /* 型や値をここで設定します。これ以降の節を参照ください */

    /* この変数を "new_variable_name" という名前でシンボルテーブルに登録します */
    zend_hash_update(
        EG(active_symbol_table),
        "new_variable_name",
        strlen("new_variable_name") + 1,
        &new_variable,
        sizeof(zval *),
        NULL
    );

これは、ほとんどのモジュールで使用されている標準的な方法です。

上の例で作成された変数は、常にローカルスコープにあります。 つまり、その関数がコールされたコンテキスト内に存在するということです。 新しい変数をグローバルスコープに作成するにも同じメソッドを使用しますが、 別のシンボルテーブルを参照します。

    zval *new_variable;

    // 新しいコンテナを確保して初期化します
    MAKE_STD_ZVAL(new_variable);

    //
    // 型や値をここで設定します
    //

    // この変数を "new_variable_name" という名前でグローバルシンボルテーブルに登録します
    ZEND_SET_SYMBOL(&EG(symbol_table), "new_variable_name", new_variable);

今回は、`EG(symbol_table)` によって参照される本体のグローバルシンボルテーブルを使用して `ZEND_SET_SYMBOL` マクロがコールされています。

注意: 変数 `active_symbol_table` はポインタですが、`symbol_table` はそうではありません。 これは、 `EG(active_symbol_table)` および `&EG(symbol_table)` を `ZEND_SET_SYMBOL` へのパラメータとして使用する必要があるからです - ここにはポインタが必要です。

同様に、より効率的なバージョンとして、 シンボルテーブルの更新をハードコーディングすることもできます。

    zval *new_variable;

    // 新しいコンテナを確保して初期化します
    MAKE_STD_ZVAL(new_variable);

    //
    // 型や値をここで設定します
    //

    // この変数を "new_variable_name" という名前でグローバルシンボルテーブルに登録します
    zend_hash_update(
        &EG(symbol_table),
        "new_variable_name",
        strlen("new_variable_name") + 1,
        &new_variable,
        sizeof(zval *),
        NULL
    );

例46-9 は、ふたつの変数を作成する例です。 `local_variable` はローカルスコープ、そして `global_variable` はグローバルスコープとなります (図 9.7 を参照ください)。完全な例は CD-ROM にあります。

注意: グローバル変数は、実際には関数内からアクセスできないことにお気づきでしょう。 これは、PHP ソース内で `global $global_variable;` のようにローカルスコープにインポートしていないからです。

例 46-9. スコープが異なる変数の作成

    ZEND_FUNCTION(variable_creation)
    {
        zval *new_var1, *new_var2;

        MAKE_STD_ZVAL(new_var1);
        MAKE_STD_ZVAL(new_var2);

        ZVAL_LONG(new_var1, 10);
        ZVAL_LONG(new_var2, 5);

        ZEND_SET_SYMBOL(EG(active_symbol_table), "local_variable", new_var1);
        ZEND_SET_SYMBOL(&EG(symbol_table), "global_variable", new_var2);

        RETURN_NULL();

    }

## Long (整数型)

さあ、それではデータを変数に代入していきましょう。まずは long 型からです。long は PHP の整数型で、非常にシンプルな形式で保存されます。 この章の最初のほうで説明した `zval.value` コンテナの 構造を見てみましょう。long 型のデータは、共用体の `lval` フィールドに直接格納されることがおわかりでしょう。 long 型に対応する `type` の値は `IS_LONG` です (例46-10 を参照ください)。

例 46-10. long の作成

    zval *new_long;

    MAKE_STD_ZVAL(new_long);

    new_long->type = IS_LONG;
    new_long->value.lval = 10;

あるいは、マクロ `ZVAL_LONG` を使用することもできます。

    zval *new_long;

    MAKE_STD_ZVAL(new_long);
    ZVAL_LONG(new_long, 10);

## Double (浮動小数点型)

double 型は PHP の浮動小数点型で、long 型と同様に簡単に代入できます。 この値もまた共用体に直接格納されるからです。 `zval.value` コンテナで対応するメンバは `dval` です。また、対応する type は `IS_DOUBLE` となります。

    zval *new_double;

    MAKE_STD_ZVAL(new_double);

    new_double->type = IS_DOUBLE;
    new_double->value.dval = 3.45;

あるいは、マクロ `ZVAL_DOUBLE` を使用することもできます。

    zval *new_double;

    MAKE_STD_ZVAL(new_double);
    ZVAL_DOUBLE(new_double, 3.45);

## 文字列

文字列についてはもう少し手間を掛けなければなりません。 先に説明したように、Zend の内部データ構造に関連付けられる すべての文字列は、Zend 自身のメモリ管理関数を使用して管理しなければなりません。 静的な文字列を参照したり、標準関数を使用して割り当てた文字列を使用することはできません。 文字列を代入するには、`zval.value` コンテナの構造体 `str` にアクセスしなければなりません。 対応する type は `IS_STRING` です。

    zval *new_string;
    char *string_contents = "This is a new string variable";

    MAKE_STD_ZVAL(new_string);

    new_string->type = IS_STRING;
    new_string->value.str.len = strlen(string_contents);
    new_string->value.str.val = estrdup(string_contents);

ここでの、Zend の estrdup() の使用法に注意しましょう。 もちろん、定義済みのマクロ `ZVAL_STRING` を使用することも可能です。

    zval *new_string;
    char *string_contents = "This is a new string variable";

    MAKE_STD_ZVAL(new_string);
    ZVAL_STRING(new_string, string_contents, 1);

`ZVAL_STRING` は、3 番目のパラメータをとることができます。 これは、指定した文字列が (estrdup() を使用して) 複製されるべきかどうかを指定します。このパラメータに `1` を指定すると、文字列が複製されます。 `0` の場合は、渡されたポインタを そのまま変数の内容として使用します。これは、すでに Zend の内部メモリに割り当てられている文字列を参照する変数を 作成する場合に便利です。

ある場所で文字列を切り捨てたい場合、 あるいは文字列の長さが事前にわかっている場合は、`ZVAL_STRINGL(zval, string, length, duplicate)` を使用して 新しい文字列の長さを明示的に指定することができます。 このマクロは `ZVAL_STRING` より高速に動作し、 バイナリセーフです。

空の文字列を作成するには、文字列の長さを `0` にしたうえで、中身に `empty_string` を使用します。

    new_string->type = IS_STRING;
    new_string->value.str.len = 0;
    new_string->value.str.val = empty_string;

もちろん、これを行うためのマクロもあります (`ZVAL_EMPTY_STRING`)。

    MAKE_STD_ZVAL(new_string);
    ZVAL_EMPTY_STRING(new_string);

## 論理型

論理型の作り方は long 型と同じです。ただ、type は `IS_BOOL` となります。 `lval` に指定できる値は `0` および `1` です。

    zval *new_bool;

    MAKE_STD_ZVAL(new_bool);

    new_bool->type = IS_BOOL;
    new_bool->value.lval = 1;

この型に対応するマクロは `ZVAL_BOOL` (値を指定できます)、そして `ZVAL_TRUE` および `ZVAL_FALSE` (それぞれ、値を明示的に `TRUE` および `FALSE` に設定します) です。

## 配列

配列は、Zend の内部ハッシュテーブルに格納されます。 このハッシュテーブルにアクセスするには zend_hash_*() API を使用します。配列を作成するたびに、新しいハッシュテーブルの ハンドルが必要となります。これは、`zval.value` コンテナのメンバ `ht` に格納されます。

単に配列を作成するためだけの API があります。これは非常に便利です。 新しい配列を開始するには、array_init() をコールします。

    zval *new_array;

    MAKE_STD_ZVAL(new_array);

    array_init(new_array);

array_init() は、常に `SUCCESS` を返します。

配列に新しい要素を追加するには、 やりたいことに応じてさまざまな関数を使用できます。 表46-8、 表46-9 および 表46-10 で、これらの関数について説明しています。これらの関数はすべて、 失敗した場合に `FAILURE`、 成功した場合に `SUCCESS` を返します。

表 46-8. 連想配列用の Zend の API

関数 | 説明
add_assoc_long(zval *array, char *key, long n);() | `long` 型の要素を追加します。
add_assoc_unset(zval *array, char *key);() | 未設定の要素を追加します。
add_assoc_bool(zval *array, char *key, int b);() | Boolean 要素を追加します。
add_assoc_resource(zval *array, char *key, int r);() | リソースを配列に追加します。
add_assoc_double(zval *array, char *key, double d);() | 浮動小数点値を追加します。
add_assoc_string(zval *array, char *key, char *str, int duplicate);() | 文字列を配列に追加します。フラグ `duplicate` は、 文字列の内容を Zend の内部メモリにコピーする必要があるかどうかを指定します。
add_assoc_stringl(zval *array, char *key, char *str, uint length, int duplicate); () | 長さ `length` の文字列を配列に追加します。 それ以外は add_assoc_string() と同じです。
add_assoc_zval(zval *array, char *key, zval *value);() | zval を配列に追加します。別の配列やオブジェクト、ストリームなどを追加する際に便利です。

表 46-9. 数値添字配列用の Zend の API その 1

関数 | 説明
add_index_long(zval *array, uint idx, long n);() | `long` 型の要素を追加します。
add_index_unset(zval *array, uint idx);() | 未設定の要素を追加します。
add_index_bool(zval *array, uint idx, int b);() | Boolean 要素を追加します。
add_index_resource(zval *array, uint idx, int r);() | リソースを配列に追加します。
add_index_double(zval *array, uint idx, double d);() | 浮動小数点値を追加します。
add_index_string(zval *array, uint idx, char *str, int duplicate);() | 文字列を配列に追加します。フラグ `duplicate` は、 文字列の内容を Zend の内部メモリにコピーする必要があるかどうかを指定します。
add_index_stringl(zval *array, uint idx, char *str, uint length, int duplicate);() | 長さ `length` の文字列を配列に追加します。 この関数は高速でバイナリセーフです。 それ以外は add_index_string() と同じです。
add_index_zval(zval *array, uint idx, zval *value);() | zval を配列に追加します。別の配列やオブジェクト、ストリームなどを追加する際に便利です。

表 46-10. 数値添字配列用の Zend の API その 2

関数 | 説明
add_next_index_long(zval *array, long n);() | `long` 型の要素を追加します。
add_next_index_unset(zval *array);() | 未設定の要素を追加します。
add_next_index_bool(zval *array, int b);() | Boolean 要素を追加します。
add_next_index_resource(zval *array, int r);() | リソースを配列に追加します。
add_next_index_double(zval *array, double d);() | 浮動小数点値を追加します。
add_next_index_string(zval *array, char *str, int duplicate);() | 文字列を配列に追加します。フラグ `duplicate` は、 文字列の内容を Zend の内部メモリにコピーする必要があるかどうかを指定します。
add_next_index_stringl(zval *array, char *str, uint length, int duplicate);() | 長さ `length` の文字列を配列に追加します。 この関数は高速でバイナリセーフです。 それ以外は add_index_string() と同じです。
add_next_index_zval(zval *array, zval *value);() | zval を配列に追加します。別の配列やオブジェクト、ストリームなどを追加する際に便利です。

これらの関数はすべて、Zend 内部のハッシュ API を抽象化して使用しやすくしたものです。 もちろん、ハッシュ関数を直接使用することも可能です。例えば、すでに割り当て済みの `zval` コンテナを配列に挿入する場合などが考えられます。 これを行うには、連想配列の場合は zend_hash_update() (例46-11 を参照ください)、 数値添字配列の場合は zend_hash_index_update() (例46-12 を参照ください) を使用します。

例 46-11. 連想配列への要素の追加

    zval *new_array, *new_element;
    char *key = "element_key";

    MAKE_STD_ZVAL(new_array);
    MAKE_STD_ZVAL(new_element);

    array_init(new_array);

    ZVAL_LONG(new_element, 10);

    if(zend_hash_update(new_array->value.ht, key, strlen(key) + 1, (void *)&new_element, sizeof(zval *), NULL) == FAILURE)
    {
        // エラー処理をここで行います
    }

例 46-12. 数値添字配列への要素の追加

    zval *new_array, *new_element;
    int key = 2;

    MAKE_STD_ZVAL(new_array);
    MAKE_STD_ZVAL(new_element);

    array_init(new_array);

    ZVAL_LONG(new_element, 10);

    if(zend_hash_index_update(new_array->value.ht, key, (void *)&new_element, sizeof(zval *), NULL) == FAILURE)
    {
        // エラー処理をここで行います
    }

add_next_index_*() の機能をエミュレートするには、 これを使用します。

    zend_hash_next_index_insert(ht, zval **new_element, sizeof(zval *), NULL)

注意: 関数から配列を返すには、まず array_init() を使用し、それ以降の操作は定義済み変数 `return_value` で行います (エクスポートする関数への引数として渡します。 呼び出しインターフェイスについての先ほどの議論を参照ください)。 ここで `MAKE_STD_ZVAL` を使用してはいけません。

Tip: 毎回 `new_array->value.ht` を書く手間を省くためには `HASH_OF(new_array)` を使用します。 これは、互換性やコーディングスタイルの観点からもお勧めです。

## オブジェクト

オブジェクトは配列に変換できる (その逆も可能) ので、 PHP の配列と多くの共通点があるであろうことはお気づきでしょう。 オブジェクトを処理するには、同じようなハッシュ関数を使用しますが、 オブジェクトを作成する際には異なる API を使用します。

オブジェクトを初期化するには、関数 object_init() を使用します。

    zval *new_object;

    MAKE_STD_ZVAL(new_object);

    if(object_init(new_object) != SUCCESS)
    {
        // エラー処理をここで行います
    }

表46-11 で説明している関数を使用すると、 オブジェクトにメンバを追加することができます。

表 46-11. オブジェクトを作成するための Zend の API

関数 | 説明
add_property_long(zval *object, char *key, long l);() | long 型をオブジェクトに追加します。
add_property_unset(zval *object, char *key);() | 未設定のプロパティをオブジェクトに追加します。
add_property_bool(zval *object, char *key, int b);() | Boolean をオブジェクトに追加します。
add_property_resource(zval *object, char *key, long r);() | リソースをオブジェクトに追加します。
add_property_double(zval *object, char *key, double d);() | double 型をオブジェクトに追加します。
add_property_string(zval *object, char *key, char *str, int duplicate);() | 文字列をオブジェクトに追加します。
add_property_stringl(zval *object, char *key, char *str, uint length, int duplicate);() | 指定した長さの文字列をオブジェクトに追加します。この関数は add_property_string() より高速で、バイナリセーフです。
add_property_zval(zval *obect, char *key, zval *container):() | `zval` コンテナをオブジェクトに追加します。 これは、整数値や文字列のような単純なものではなく 配列やその他のオブジェクトなどをプロパティとして追加する際に有用です。

## リソース

リソースは、PHP における特殊なデータ型です。 リソース (resources) は何らかの特定の型を表すわけではなく、 さまざまな情報を扱うための抽象化した方法を表しています。 リソースは、Zend の内部では特別なリストの中に保持されます。 リストの各エントリは、そのリソースが指すデータを表す型定義を持っています。 Zend は、内部でリソースを扱う際には、常にこれを使用します。 リソースに直接アクセスすることはできず、提供されている API を通じてアクセスしなければなりません。特定のリソースに対する 参照がすべて失われると、対応するシャットダウン関数がすぐにコールされます。

例えば、リソースは、 データベースへのリンクやファイル記述子を格納するために使用されます。 事実上の標準実装となっているのは MySQL モジュールです。しかし、例えば Oracle モジュールのような その他のモジュールでもリソースを使用しています。

> 注意: 実際のところ、あなたが関数内で処理したい任意のデータへのポインタ (例: 構造体へのポインタ) を、リソースとして使用することができます。 ユーザがこのデータにアクセスするには、ひとつのリソース変数を 関数に渡すだけでいいようになります。

新しいリソースを作成するには、そのリソースを開放するためのハンドラを 登録しなければなりません。リソースにはあらゆる種類のデータを保存できるので、 Zend は、それが不要になった際にどのように開放するのかを知っておく必要があるのです。 これを実現するために、自分が作成したリソース開放ハンドラを Zend に登録します。これは、(手動・自動にかかわらず) リソースを開放することになった際に、Zend によってコールされます。 リソースハンドラを Zend に登録すると、そのリソースについての リソース型ハンドルが Zend から返されます。 このハンドルは、後でこの型のリソースにアクセスする際には常に必要となり、 たいていは拡張モジュール内のグローバルな静的変数として保存されます。 スレッドセーフであるかどうかについて心配する必要はありません。 なぜなら、リソースハンドラの登録は モジュールの初期化時に一度行うだけだからです。

リソースハンドラを登録するための Zend の関数は、次のように宣言されています。

    ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number);

この関数には、二種類の異なるリソース破壊ハンドラを渡すことが可能です。 それぞれ、通常のリソース用のものと持続的なリソース用のものになります。 持続的なリソースとは、例えばデータベース接続などに使用されるものです。 リソースを登録する際には、これらのどちらかのハンドラを必ず指定しなければなりません。 もう一方のほうには、単に `NULL` を渡すようにします。

zend_register_list_destructors_ex() は、次のパラメータを受け取ります。

`ld` | 通常のリソース用のリソース破壊ハンドラコールバック。
`pld` | 持続的なリソース用のリソース破壊ハンドラコールバック。
`type_name` | そのリソースの名前を表す文字列。 PHP 内で一意になるような名前をつけるよう心がけましょう。 ユーザが例えば `var_dump($resource);` をコールした際に、この名前が表示されます。
`module_number` | `module_number` は、 `PHP_MINIT_FUNCTION` 関数の中で自動的に使用可能となります。 そのため、単純にそれを渡すだけです。

返り値は、作成した リソース型 の一意な ID となる整数値です。

リソース破壊ハンドラ (通常版および持続的リソース版のどちらも) のプロトタイプは次のようになります。

    void resource_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC);

`rsrc` に渡されるのは、次のような構造体へのポインタです。

    typedef struct _zend_rsrc_list_entry {

        void *ptr;
        int type;
        int refcount;

    } zend_rsrc_list_entry;

メンバ `void *ptr` が、 リソースへの実際のポインタとなります。

これでやるべきことがわかりました。Zend に登録したいリソースを、 実際に定義してみましょう。 ここでは、ふたつの整数型メンバからなる単純な構造体を考えます。 members:

    typedef struct {

        int resource_link;
        int resource_type;

    } my_resource;

このリソースを破壊するハンドラは、おそらく次のようなものとなるでしょう。

    void my_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC) {

        // たいていは void ポインタを構造体の型にキャストすることになるでしょう。

        my_resource *my_rsrc = (my_resource *) rsrc->ptr;

        // ここで、そのリソースに対して必要な作業を行います。たとえば
        // ファイルやソケットを閉じたり、内部で新たに確保したメモリを開放したりなどです。
        // もちろん、このリソース自身が使っているメモリを開放することも忘れないようにしましょう。

        do_whatever_needs_to_be_done_with_the_resource(my_rsrc);
    }

> 注意: 注意すべき点: もしそのリソースが複雑な構造体であり、 その内部で実行時に確保したメモリへのポインタを含んでいる場合は、 リソース自身のメモリを開放する 前に 実行時に確保したメモリを開放しなければなりません。

ここまでで、

1. どんなリソースなのか

2. そのリソースを破壊する際のハンドラ

の定義が終了しました。それでは残りの作業を進めましょう。

1. 拡張モジュール内のグローバル変数を作成し、 リソース ID を保持させる。必要に応じて、 これは全ての関数からアクセス可能となる

2. リソース名を定義する

3. リソース破壊ハンドラを記述する

4. そしてそのハンドラを登録する

    // 拡張モジュール内のどこかで、登録したリソース用の変数を定義します。
        // 'le' って何のことだって? 単に 'list entry' を略しただけです。
        static int le_myresource;

        // リソース名もどこかで定義しておくとよいでしょう。
        #define le_myresource_name  "My type of resource"

        [...]

        // 実際にリソース破壊ハンドラを登録します。
        void my_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC) {

            my_resource *my_rsrc = (my_resource *) rsrc->ptr;
            do_whatever_needs_to_be_done_with_the_resource(my_rsrc);
        }

        [...]

        PHP_MINIT_FUNCTION(my_extension) {

            // 'module_number' は、PHP_MINIT_FUNCTION() 関数の
            // 定義で既に提供されていることに注意しましょう。

            le_myresource = zend_register_list_destructors_ex(my_destruction_handler, NULL, le_myresource_name, module_number);

            // 別のリソースを登録したり、グローバル変数や定数を
            // 初期化したりします。
        }

実際に新しいリソースを登録するには、 zend_register_resource() 関数あるいは ZEND_REGISTER_RESOURE() マクロのいずれかを使用します。 これらはどちらも zend_list.h で定義されています。 それぞれの引数は一対一対応しているので、 将来の互換性を考えると常にマクロを使用するようにすることをお勧めします。

    int ZEND_REGISTER_RESOURCE(zval *rsrc_result, void *rsrc_pointer, int rsrc_type);

`rsrc_result` | これは、事前に初期化済みの `zval *` コンテナです。
`rsrc_pointer` | 保存したいリソースへのポインタ。
`rsrc_type` | リソース破壊ハンドラを登録した際に受け取る型。 命名規約に従うなら、これは `le_myresource` となります。

返り値は、そのリソースに対応する一意な整数値になります。

新しいリソースを登録する際に実際には何が行われているのかというと、 まずそれが Zend の内部リストに挿入されます。そして、 結果は単に与えられた `zval *` コンテナに保存されます。

    rsrc_id = zend_list_insert(rsrc_pointer, rsrc_type);

        if (rsrc_result) {
            rsrc_result->value.lval = rsrc_id;
            rsrc_result->type = IS_RESOURCE;
        }

        return rsrc_id;

返される `rsrc_id` は、 新しく登録されたリソースを表す一意な識別子となります。 マクロ `RETURN_RESOURE` を使用して、 これを利用者に返すことができます。

    RETURN_RESOURCE(rsrc_id)

> 注意: そのリソースをすぐに利用者に返したいのなら、 `return_value` を `zval *` コンテナに設定するのが一般的な方法です。

Zend はこれ以降、このリソースに対するすべての参照を管理できるようになります。 リソースへの参照がすべて失われると、リソースに対して事前に定義した デストラクタがすぐにコールされます。この設定の利点は、 あなたのモジュール内で発生するメモリリークを気にしなくてもすむということです。 呼び出し元スクリプトで割り当てているすべてのメモリはリソースを参照しています。 メモリが必要なくなったとスクリプトが判断した時点で、 Zend はすぐにそれを検出して通知してくれます。

さて、利用者がリソースを取得したあとで、 それをまたあなたの作成した関数に渡してきたとしましょう。 `zval *` コンテナ内の `value.lval` にはあなたのリソースのキーが含まれているので、 それを使用して次のマクロでリソースを取得することができます。 `ZEND_FETCH_RESOURCE`:

    ZEND_FETCH_RESOURCE(rsrc, rsrc_type, rsrc_id, default_rsrc_id, resource_type_name, resource_type)

`rsrc` | これは、あなたが事前に登録したリソースを指すポインタです。
`rsrc_type` | これは、ポインタに対する型キャスト引数、例えば `myresource *` などです。
`rsrc_id` | これは、利用者があなたの関数に渡した `zval *` コンテナのアドレスです。例えば `zval *z_resource` が指定された場合には `&z_resource` となります。
`default_rsrc_id` | これは、リソースが取得できなかった場合は -1 が返された場合などのための デフォルトのリソース `ID` です。
`resource_type_name` | これは、要求されたリソースの名前です。 リソースが見つからなかった場合や無効なリソースだった場合に、 意味のあるエラーメッセージを作成するためにこの文字列を使用します。
`resource_type` | リソース破壊ハンドラを登録した際に返すリソース型です。 今回の例では、これは `le_myresource` でした。

このマクロは何も値を返しません。これは開発者の利便性のためで、 TSRMLS 引数のことを考慮したものです。リソースを読み込む段階でチェックが行われます。 リソースの取得時にエラーが発生した場合は、このマクロは 警告メッセージをスローし、PHP 関数は `NULL` を返します。

リソースをリストから強制的に削除するには、関数 zend_list_delete() を使用します。 また事前に割り当てた値への新たな参照を作成した場合 (例えば、デフォルトのデータベースリンクを再利用する場合) などは、強制的に参照カウンタを増加させることができます。 このような場合は、関数 zend_list_addref() を使用します。事前に割り当てたリソースエントリを探すには zend_list_find() を使用します。 これらの完全な API は、`zend_list.h` で確認できます。

## 自動グローバル変数の作成のためのマクロ

先ほど説明したマクロに加え、 単純なグローバル変数を簡単に作成するためのマクロがあります。 これらは、例えばグローバルなフラグなどを作成する際に知っておくと便利です。 あまり行儀のよいやり方ではありませんが、 表46-12 で説明するマクロは まさにこの作業を行うためのものです。 これらは `zval` を確保する必要がありません。 単純に、変数名と値を渡すだけでいいのです。

表 46-12. グローバル変数の作成のためのマクロ

マクロ | 説明
`SET_VAR_STRING(name, value)` | 新しい文字列を作成します。
`SET_VAR_STRINGL(name, value, length)` | 新しい文字列を、指定した長さで作成します。このマクロは `SET_VAR_STRING` より高速で、バイナリセーフです。
`SET_VAR_LONG(name, value)` | 新しい long を作成します。
`SET_VAR_DOUBLE(name, value)` | 新しい double を作成します。

## 定数の作成

Zend は (通常の変数ではなく) 真の定数の作成もサポートしています。 定数へのアクセスにはドル記号 (`$`) の接頭辞が不要で、すべてのスコープで使用可能です。 例としては `TRUE` や `FALSE` などがあります。

独自の定数を作成するには、 表46-13 のマクロを使用します。 これらのマクロは、指定した名前と値の定数を作成します。

各定数に対して、フラグを指定することもできます。

- `CONST_CS` - この定数名は、大文字小文字を区別して扱われます。

- `CONST_PERSISTENT` - この定数は持続的に扱われ、この定数を保持しているプロセスが 終了した後も「忘れられる」ことはありません。

これらのフラグは、OR で組み合わせて使用します。

    // "long" 型の新しい定数を登録します。
         REGISTER_LONG_CONSTANT("NEW_MEANINGFUL_CONSTANT", 324, CONST_CS |
         CONST_PERSISTENT);

マクロには、ふたつの形式があります。 `REGISTER_*_CONSTANT` および `REGISTER_MAIN_*_CONSTANT` です。 最初の形式は、定数を現在のモジュールにバインドします。 これらの定数は、そのモジュールがメモリから開放されるとすぐに、 シンボルテーブルから削除されます。二番目の形式が作成する定数は、 モジュールとは独立してシンボルテーブルに残り続けます。

表 46-13. 定数を作成するためのマクロ

マクロ | 説明
`REGISTER_LONG_CONSTANT(name, value, flags)` `REGISTER_MAIN_LONG_CONSTANT(name, value, flags)` | long 型の新しい定数を登録します。
`REGISTER_DOUBLE_CONSTANT(name, value, flags)` `REGISTER_MAIN_DOUBLE_CONSTANT(name, value, flags)` | double 型の新しい定数を登録します。
`REGISTER_STRING_CONSTANT(name, value, flags)` `REGISTER_MAIN_STRING_CONSTANT(name, value, flags)` | 文字列型の新しい定数を登録します。指定した文字列は、Zend の内部メモリ上に存在しなければなりません。
`REGISTER_STRINGL_CONSTANT(name, value, length, flags)` `REGISTER_MAIN_STRINGL_CONSTANT(name, value, length, flags)` | 文字列型の新しい定数を登録します。文字列の長さを、 明示的に `length` と指定します。 指定した文字列は、Zend の内部メモリ上に存在しなければなりません。

前のページ | ホーム | 次のページ
引数の扱い | 上に戻る | 変数の内容の複製: コピーコンストラクタ