ユーザ関数のコール

モジュール内からユーザ関数をコールすることができます。 これは、コールバックを実装する場合などにとても便利です。 例えば配列の順次処理や検索、 あるいはイベントベースのプログラムに使用できます。

ユーザ関数をコールするには、call_user_function_ex() を使用します。この関数に渡す内容は、アクセスしたい関数テーブルについてのハッシュ、 オブジェクトへのポインタ (メソッドをコールする場合)、関数名、 返り値、引数の数、引数の配列、そして zval を分割するかどうかを示すフラグです。

ZEND_API int call_user_function_ex(HashTable *function_table, zval *object,
zval *function_name, zval **retval_ptr_ptr,
int param_count, zval **params[],
int no_separation);

function_table および object は、両方とも指定する必要はなく、どちらか一方だけでよいことに注意しましょう。 メソッドをコールしたい場合は、そのメソッドを含むオブジェクトを 指定しなければなりません。このときに、 call_user_function() は自動的にこのオブジェクトの関数テーブルを設定します。 メソッドをコールするのではない場合は、 function_table だけを設定して objectNULL とします。

通常は、デフォルトの関数テーブルは "root" であり、 このテーブルにすべての関数のエントリが含まれます。 この関数テーブルはコンパイラ全体から CG マクロを使用してアクセス可能です。 コンパイラグローバルの内容を関数内で使用するには、 TSRMLS_FETCH マクロを一度コールします。

関数名は zval コンテナで指定します。 最初は少し戸惑うかもしれませんが、これはきわめて論理的なことです。 なぜなら、スクリプト内からコールされる関数でパラメータとして受け取った 関数名は、たいていの場合は再び zval コンテナに保存されることになるからです。 そのため、この関数に引数を渡すだけでよくなります。ここで使用する zvalIS_STRING 型でなければなりません。

その次の引数には、返り値へのポインタを含めます。 このコンテナ用のメモリを確保する必要はありません。 関数が自分自身でメモリを確保します。しかし、 その後には (zval_dtor() を使用して) コンテナを破棄しなければなりません!

その次はパラメータの数を表す整数値、 それからすべての必要なパラメータを含む配列となります。 最後の引数では、この関数が zval の分割を行うかどうかを指定します。 この引数は常に 0 にしておくべきです。 1 にすると関数のメモリ消費量を抑えられますが、 分割が必要なパラメータがひとつでもあった場合に関数が実行できなくなります。

例46-15 は、ユーザ関数をコールする簡単な例です。 このコードは、引数として渡された関数をコールし、 コールした関数の返り値をそのまま自分自身の返り値として使用します。 最後のほうのコンストラクタおよびデストラクタの使用に注目しましょう。 ここではこれらの使用は必須ではありませんが (値を分割しており、代入は安全に行われる)、念のために記述しています。

例 46-15. ユーザ関数のコール

zval **function_name;
zval *retval;

if((ZEND_NUM_ARGS() != 1) || (zend_get_parameters_ex(1, &function_name) != SUCCESS))
{
    WRONG_PARAM_COUNT;
}

if((*function_name)->type != IS_STRING)
{
    zend_error(E_ERROR, "Function requires string argument");
}

TSRMSLS_FETCH();

if(call_user_function_ex(CG(function_table), NULL, *function_name, &retval, 0, NULL, 0) != SUCCESS)
{
    zend_error(E_ERROR, "Function call failed");
}

zend_printf("We have %i as type\n", retval->type);

*return_value = *retval;
zval_copy_ctor(return_value);
zval_ptr_dtor(&retval);

<?php

dl("call_userland.so");

function test_function()
{
    echo "We are in the test function!\n";
    return 'hello';
}

$return_value = call_userland("test_function");

echo "Return value: '$return_value'";
?>

上の例の出力は以下となります。

We are in the test function! We have 3 as type Return value: 'hello'