PHP7 扩展开发(六) 函数

编辑于 2016-10-04

* 移动设备下, 可左滑手指以查看较宽代码

前面的章节说了不少原理. 这一章直接记录用法.

函数返回值

PHP 内核为每个函数声明里加了一个 zval* 类型的形参, 名为 return_value, 专门来解决返回值这个问题:

ZEND_FUNCTION(sample_long)
{
    ZVAL_LONG(return_value, 42);
    return;
}

//下面的宏亦可
ZEND_FUNCTION(sample_long)
{
    RETVAL_LONG(42);
    return;
}

//或者直接这样
ZEND_FUNCTION(sample_long)
{
    RETURN_LONG(42);
}

这些宏都定义在 Zend/zend_API.h 文件里, 通过阅读前面几章的内容, 容易猜测它们的意思:

#define RETVAL_RESOURCE(l)              ZVAL_RESOURCE(return_value, l)
#define RETVAL_BOOL(b)                  ZVAL_BOOL(return_value, b)
#define RETVAL_NULL()                   ZVAL_NULL(return_value)
#define RETVAL_LONG(l)                  ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d)                ZVAL_DOUBLE(return_value, d)
#define RETVAL_STRING(s, duplicate)         ZVAL_STRING(return_value, s, duplicate)
#define RETVAL_STRINGL(s, l, duplicate)     ZVAL_STRINGL(return_value, s, l, duplicate)
#define RETVAL_EMPTY_STRING()           ZVAL_EMPTY_STRING(return_value)
#define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE                    ZVAL_BOOL(return_value, 0)
#define RETVAL_TRUE                     ZVAL_BOOL(return_value, 1)
 
#define RETURN_RESOURCE(l)              { RETVAL_RESOURCE(l); return; }
#define RETURN_BOOL(b)                  { RETVAL_BOOL(b); return; }
#define RETURN_NULL()                   { RETVAL_NULL(); return;}
#define RETURN_LONG(l)                  { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d)                { RETVAL_DOUBLE(d); return; }
#define RETURN_STRING(s, duplicate)     { RETVAL_STRING(s, duplicate); return; }
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETURN_EMPTY_STRING()           { RETVAL_EMPTY_STRING(); return; }
#define RETURN_ZVAL(zv, copy, dtor)     { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE                    { RETVAL_FALSE; return; }
#define RETURN_TRUE                     { RETVAL_TRUE; return; }

数组和对象将在后续章节中叙述.

函数的参数

<?php
function sample_hello_world($name, $greeting)
{
    echo "Hello $greeting $name!\n";
}
sample_hello_world('John Smith', 'Mr.');

在扩展中这样实现:

ZEND_FUNCTION(sample_hello_world)
{
    char *name;
    int name_len;
    char *greeting;
    int greeting_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
        &name, &name_len, &greeting, &greeting_len) == FAILURE) {
        RETURN_NULL();
    }
    php_printf("Hello ");
    PHPWRITE(greeting, greeting_len);
    php_printf(" ");
    PHPWRITE(name, name_len);
    php_printf("!\n");
}

因为 PHP 是弱类型语言, "ss" 代表传入的参数转为两个 string 类型, 此外还有:

另外 PHP7 增加了新的字符串宏,增进了性能和便捷性:

zend_string *in;
// 注意这里「S」为大写
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &in) == FAILURE) {
    RETURN_FALSE;
}
// 获取指针
ZSTR_VAL(in);
// 获取长度
ZSTR_LEN(in);
// 创建 zend_string
zend_string_init(str, len, 0);
参数 类型 对应C语言类型
b 布尔型(Boolean) zend_bool
i 整型(Integer) long
d 浮点型(Double) double
s 字符串(String) char*, int (前者指针, 后者长度)
r 资源(Resource) zval*
a 数组(Array) zval*
o 对象(Object) zval*
O 特定类型的对象 zval*, zend_class_entry*
z 任意类型 zval zval*
Z zval** 类型 zval**

在 PHP7 中, 提供另一种获取参数的方式 FAST_ZPP, 是为了提高参数解析的性能:

#ifdef FAST_ZPP
ZEND_PARSE_PARAMETERS_START(1, 2)
    Z_PARAM_STR(type)
    Z_PARAM_OPTIONAL
    Z_PARAM_ZVAL_EX(value, 0, 1)
ZEND_PARSE_PARAMETERS_END();
#endif

函数默认值

假如扩展中的 greeting 是第二个 string 参数的默认值, 只需要这样:

char *name;
int name_len;
char *greeting = "Mr./Mrs.";
int greeting_len = sizeof("Mr./Mrs.") - 1;
 
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
  &name, &name_len, &greeting, &greeting_len) == FAILURE) {
    RETURN_NULL();
}