5

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

该宏ZEND_BEGIN_ARG_INFO_EX可用于向 Zend Engine 提供有关函数参数的信息。宏的第 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_EX是 PHP 5 的新增功能,用于生成更简洁的代码、启用类型提示、引用传递和反射。考虑以下 arginfo 声明,用于只返回 true 的实际函数:

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!"(具有空说明符的特定对象类型)。类型提示已经用 arginfo 指定了,所以不需要重复两次。

所以,没有 arginfo:

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

出于显而易见的原因,您需要确保 arginfo 声明和zend_parse_parameters调用匹配。

于 2013-02-01T16:45:44.423 回答