如何使ZEND_BEGIN_ARG_INFO_EX控制参数数量,传递给PHP扩展?

And*_*kus 5 php c php-extension php-internals

我正在开发一个PHP扩展,使用C.到目前为止,我正在研究正确的参数验证,从PHP用户空间传递给扩展的函数.

该宏ZEND_BEGIN_ARG_INFO_EX可用于向Zend Engine提供有关函数参数的信息.宏的第四个参数,命名为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()
Run Code Online (Sandbox Code Playgroud)

这是我对函数的定义,由PHP扩展导出:

static const zend_function_entry test_func_functions[] = {
    PHP_FE(sample_with_types, test_func_swt_arginfo)
    PHP_FE_END
};
Run Code Online (Sandbox Code Playgroud)

这是我的功能:

PHP_FUNCTION(sample_with_types)
{
    RETURN_TRUE;
}
Run Code Online (Sandbox Code Playgroud)

这是我运行的PHP脚本:

<?php
sample_with_types();
Run Code Online (Sandbox Code Playgroud)

预期结果:PHP显示错误/警告/异常,例如"没有足够的参数传递给函数" ; 该功能不执行.

实际结果:函数执行并返回true.

如何正确配置函数参数结构,以便Zend Engine自动检查参数个数?或者我required_num_argsZEND_BEGIN_ARG_INFO_EX宏观中误解了论证的目的?

net*_*der 8

据我所知,这不是ZEND_BEGIN_ARG_INFO_EX为了什么.

ZEND_BEGIN_ARG_INFO_EX是一个PHP 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()
Run Code Online (Sandbox Code Playgroud)

它具有以下效果:

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
Run Code Online (Sandbox Code Playgroud)

此外,它还为您的功能提供反射功能:

$ref = new ReflectionFunction('sample_with_types');
var_dump($ref->getParameters());
Run Code Online (Sandbox Code Playgroud)

...给出类似于的输出:

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"
  }
}
Run Code Online (Sandbox Code Playgroud)

如果省略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;
}
Run Code Online (Sandbox Code Playgroud)

注意我如何使用"looo"(通用对象类型)而不是"lOO!O!"(具有空说明符的特定对象类型).类型提示已经使用arginfo指定,因此不需要执行两次.

所以,没有arginfo:

  • 您必须使用少量zend_fetch_class调用和类条目来键入提示您的对象参数.
  • 它无法实现反思.
  • 您将无法声明通过引用传递的参数.
  • 显然会产生较少的干净代码.

出于显而易见的原因,您需要确保您的arginfo声明和您的zend_parse_parameters呼叫都匹配.