将 C 语言数据嵌入 PHP 对象
让我们把普通的 PHP $factor 属性转换为嵌入式 C 数据。为此,必须以特殊方式分配 zend_object。C 数据和 zend_object 必须一起分配: C 数据位于指向地址的上方,zend_object 位于下方。这样,PHP core 就可以把这些自定义对象当作普通对象来使用,我们也可以使用负偏移量来获取 C 数据。无论如何,PHP core 至少应该知道已分配块的真实地址,以便释放它。我们可以通过覆盖 object_handlers 中的相应字段来通知它负偏移量。
因此,我们需要重写对象处理程序表,为此我们使用了一个类型为 "scaler_object_handlers" 的全局变量。(我们声明新的 "scaler_t "类型,将 C 数据和 zend_object 结合起来。(zend_object 必须是结构中的最后一个元素,因为 PHP 引擎可能会在它之后为定义的属性分配内存。(参见 "PHP 类和对象 "一章开头的图片)我们还定义了一个宏 Z_SCALER_P(),用于执行指针运算并从 PHP 对象值中获取结构体的地址,以及一个回调函数 scaler_new(),用于创建 Scale 类型的对象。
static zend_object_handlers scaler_object_handlers;
typedef struct scaler_t {
zend_long factor;
zend_object std;
} scaler_t;
#define Z_SCALER_P(zv) \
((scaler_t*)((char*)(Z_OBJ_P(zv)) - XtOffsetOf(scaler_t, std)))
zend_object *scaler_new(zend_class_entry *ce)
{
scaler_t *scaler = zend_object_alloc(sizeof(scaler_t), ce);
zend_object_std_init(&scaler->std, ce);
scaler->std.handlers = &scaler_object_handlers;
return &scaler->std;
}
该函数分配必要的内存,初始化对象的 PHP 标准部分,覆盖默认的对象处理程序,并返回指向对象标准部分的指针。
现有的方法将转换为使用新的 C 字段。它们给出自定义对象的地址,然后直接使用 C 数据字段(无需哈希查找、类型检查、转换等)。
PHP_METHOD(Scaler, __construct)
{
scaler_t *scaler = Z_SCALER_P(ZEND_THIS);
zend_long factor = DEFAULT_SCALE_FACTOR; // default value
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(factor)
ZEND_PARSE_PARAMETERS_END();
scaler->factor = factor;
}
PHP_METHOD(Scaler, scale)
{
zval *x;
scaler_t *scaler = Z_SCALER_P(ZEND_THIS);
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(x)
ZEND_PARSE_PARAMETERS_END();
do_scale_ref(x, scaler->factor);
}
在 MINIT 中,我们重写了 "create_object" 回调,将默认对象处理程序表复制到我们自己的表中,并重写了 "offset" 字段(用于通知引擎特殊的对象布局)。
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);
scaler_class_entry->create_object = scaler_new;
memcpy(&scaler_object_handlers, &std_object_handlers,
sizeof(zend_object_handlers));
scaler_object_handlers.offset = XtOffsetOf(scaler_t, std);
zend_declare_class_constant_long(scaler_class_entry,
"DEFAULT_FACTOR", sizeof("DEFAULT_FACTOR")-1, DEFAULT_SCALE_FACTOR);
return SUCCESS;
}
测试:
$ php -r '$o = new Scaler(4); $x = 5; $o->scale($x); var_dump($x,$o);'
int(20)
object(Scaler)#1 (0) {
}
一切都很好,只是我们看不到作为 C 变量存储的 "factor" 值,但这很容易解决。请看下一节了解如何解决。