5

C を使用して PHP 拡張機能を開発しています。これまでのところ、PHP ユーザー空間から拡張機能の関数に渡される引数の適切な検証に取り組んでいます。

このマクロZEND_BEGIN_ARG_INFO_EXを使用して、関数の引数に関する情報を Zend Engine に提供できます。as という名前のマクロの 4 番目のパラメーターを使用するとrequired_num_args、エンジンが引数の数を自動的に制御できるようになり、この手間が省けます。しかし、それを機能させる方法を見つけることができませんでした。エンジンは、PHP スクリプトが十分な引数を渡さなくても、常に警告なしで拡張機能の関数を実行します。

関数の引数の定義は次のとおりです。

ZEND_BEGIN_ARG_INFO_EX(test_func_swt_arginfo, 0, 0, 3)
    ZEND_ARG_INFO(1, firstArg)
    ZEND_ARG_ARRAY_INFO(0, secondArg, true)
    ZEND_ARG_OBJ_INFO(1, thirdArg, SomeClass, false)
ZEND_END_ARG_INFO()

PHP拡張機能によってエクスポートされた関数の私の定義は次のとおりです。

static const zend_function_entry test_func_functions[] = {
    PHP_FE(sample_with_types, test_func_swt_arginfo)
    PHP_FE_END
};

これが私の機能です:

PHP_FUNCTION(sample_with_types)
{
    RETURN_TRUE;
}

私が実行するPHPスクリプトは次のとおりです。

<?php
sample_with_types();

期待される結果: PHP は、 「十分な引数が関数に渡されていません」のようなエラー/警告/例外を示します。関数は実行されません。

実際の結果: 関数が実行され、 が返されますtrue

Zend Engine が引数の数を自動的にチェックするように、関数の引数構造を適切に構成するにはどうすればよいですか? それとも、マクロのrequired_num_args引数の目的を間違えていますか?ZEND_BEGIN_ARG_INFO_EX

4

1 に答える 1

8

私の知る限り、これZEND_BEGIN_ARG_INFO_EXは目的ではありません。

ZEND_BEGIN_ARG_INFO_EXPHP 5 の追加機能であり、よりクリーンなコードを生成するために使用され、型ヒント、参照渡し、およびリフレクションを有効にします。true を返すだけの実際の関数について、次の arginfo 宣言を検討してください。

ZEND_BEGIN_ARG_INFO_EX(arginfo_test, 0, 0, 3)
    ZEND_ARG_INFO(0, firstArg)
    ZEND_ARG_OBJ_INFO(0, objNonNull, stdClass, 0)
    ZEND_ARG_OBJ_INFO(0, obj, stdClass, 1)
    ZEND_ARG_OBJ_INFO(1, objByRef, stdClass, 1)
ZEND_END_ARG_INFO()

次の効果があります。

sample_with_types();                          // ok
sample_with_types(1, null);                   // error: arg #2 should be stdClass
sample_with_types(1, new stdClass, null);     // ok
sample_with_types(1, new stdClass, 1);        // error: arg #3 should be stdClass
sample_with_types(1, new stdClass, null, 2);  // error: arg #4 must be reference

さらに、関数にリフレクション機能を提供します。

$ref = new ReflectionFunction('sample_with_types');
var_dump($ref->getParameters());

...次のような出力が得られます。

array(4) {
  [0]=>
  &object(ReflectionParameter)#2 (1) {
    ["name"]=>
    string(8) "firstArg"
  }
  [1]=>
  &object(ReflectionParameter)#3 (1) {
    ["name"]=>
    string(10) "objNonNull"
  }
  [2]=>
  &object(ReflectionParameter)#4 (1) {
    ["name"]=>
    string(3) "obj"
  }
  [3]=>
  &object(ReflectionParameter)#5 (1) {
    ["name"]=>
    string(8) "objByRef"
  }
}

arginfo を省​​略すると、ReflectionFunction::getParameters()代わりに空の配列が返されます。

マクロ パラメーターは特にrequired_num_argsリフレクションに使用され、関数を反映するときに必須とマークされるパラメーターの数を示します。

リフレクションを使用するときに引数を必須としてマークするだけでなく、必須にする必要がある場合でもzend_parse_parameters、ほとんどの場合、引数の実際の値を取得する必要があるを使用する必要があります。

PHP_FUNCTION(sample_with_types)
{
    long arg1;
    zval *arg2 = NULL, *arg3 = NULL, *arg4 = NULL;
    zend_class_entry ce2, ce3, ce4;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "looo", &arg1, 
                              &arg2, &ce2, &arg3, &ce3, &arg4, &ce4) == FAILURE)
    {
        return;
    }

    RETURN_TRUE;
}

"looo"上記で(汎用オブジェクト型)を使用し、( "lOO!O!"null 指定子を持つ特定のオブジェクト型) を使用しなかったことに注意してください。型ヒントは arginfo で既に指定されているので、2 回行う必要はありません。

したがって、arginfo なし:

  • zend_fetch_classオブジェクトの引数をタイプ ヒントするには、いくつかの呼び出しとクラス エントリを使用する必要があります。
  • それは反射を可能にしません。
  • 参照渡しの引数を宣言することはできません。
  • 明らかにクリーンなコードが生成されません。

明らかな理由から、arginfo 宣言と呼び出しの両方が一致することを確認する必要がありますzend_parse_parameters

于 2013-02-01T16:45:44.423 に答える