在我们的示例扩展中使用 OOP
让我们尝试扩展我们的示例扩展,用 C 语言实现以下 PHP 类。
<?php
class Scaler {
const DEFAULT_FACTOR = 2;
private $factor = DEFAULT_FACTOR;
function __construct($factor = self:: DEFAULT_FACTOR) {
$this->factor = factor;
}
function scale(&$x) {
test_scale($x, $this->factor);
}
}
方法的 C 代码版本的编写方法与 PHP 内部函数类似。只需使用带有类和方法名称的 PHP_METHOD() 宏,而不是 PHP_FUNCTION。在方法中,$this 变量(zval)可作为 ZEND_THIS 宏使用。在方法 __construct() 中,我们使用 zend_update_property_long() 函数为属性 $factor 赋值,并在方法 scale() 中使用 zend_read_property() 读取它。
static zend_class_entry *scaler_class_entry = NULL;
#define DEFAULT_SCALE_FACTOR 2
PHP_METHOD(Scaler, __construct)
{
zend_long factor = DEFAULT_FACTOR; // default value
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(factor)
ZEND_PARSE_PARAMETERS_END();
if (ZEND_NUM_ARGS() > 0) {
zend_update_property_long(Z_OBJCE_P(ZEND_THIS), ZEND_THIS,
"factor", sizeof("factor")-1, factor);
}
}
PHP_METHOD(Scaler, scale)
{
zval *x, *zv, tmp;
zend_long factor;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(x)
ZEND_PARSE_PARAMETERS_END();
zv = zend_read_property(Z_OBJCE_P(ZEND_THIS), ZEND_THIS,
"factor", sizeof("factor")-1, 0, &tmp);
factor = zval_get_long(zv);
do_scale_ref(x, factor);
}
参数信息描述符的创建和使用方法与普通函数完全相同。然后,必须将所有类方法的信息收集到一个数组中。这类似于扩展函数列表,但使用的是 PHP_ME() 宏而不是 ZEND_FE()。PHP_ME() 需要两个附加参数。第一个是类名,第四个是方法标志(例如 ZEND_ACC_PUBLIC、ZEND_ACC_PROTECTED、ZEND_ACC_PRIVATE、ZEND_ACC_STATIC)。
ZEND_BEGIN_ARG_INFO(arginfo_scaler_construct, 0)
ZEND_ARG_INFO(0, factor)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_scaler_scale, 0)
ZEND_ARG_INFO(1, x) // pass by reference
ZEND_END_ARG_INFO()
static const zend_function_entry scaler_functions[] = {
PHP_ME(Scaler, __construct, arginfo_scaler_construct, ZEND_ACC_PUBLIC)
PHP_ME(Scaler, scale, arginfo_scaler_scale, ZEND_ACC_PUBLIC)
PHP_FE_END
};
最后,我们必须在 MINIT 中注册类及其实体。
INIT_CLASS_ENTRY() 会初始化临时类入口结构,设置类名,并添加给定的类方法。 zend_ register_class_entry() 会在全局类表中注册类,并返回生成的类入口。然后为类添加常量和属性。
PHP_MINIT_FUNCTION(test)
{
zend_class_entry ce;
REGISTER_INI_ENTRIES();
INIT_CLASS_ENTRY(ce, "Scaler", scaler_functions);
scaler_class_entry = zend_register_internal_class(&ce);
zend_declare_class_constant_long(scaler_class_entry,
"DEFAULT_FACTOR", sizeof("DEFAULT_FACTOR")-1, DEFAULT_SCALE_FACTOR);
zend_declare_property_long(scaler_class_entry,
"factor", sizeof("factor")-1, DEFAULT_SCALE_FACTOR, ZEND_ACC_PRIVATE);
return SUCCESS;
}
这运行:
$ php -r '$o = new Scaler(5); $x = 5; $o->scale($x); var_dump($x, $o);'
int(25)
object(Scaler)#1 (1) {
["factor":"Scaler":private]=>
int(5)
}
实现的类没有太大意义(教育意义除外),因为它并不比用 PHP 编写的原始类更好用。在实践中,如果用 C 语言重新实现的东西效率更高、占用内存更少,或者无法用 PHP 编写,那么用 C 语言重新实现的东西就有意义了。例如,我们可以在 PHP 对象中嵌入一些 C 语言数据。