php扩展实践之implements内置接口
这次用 php 扩展的方式来实现一下 Laravel5.1 Support 中的 Fluent,实现一下 php 内置接口 ArrayAccess 的方法
method参数的定义
#include "Zend/zend_interfaces.h"
zend_class_entry *fluent_ce;
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent___construct, 0, 0, 1)
ZEND_ARG_ARRAY_INFO(0, attributes, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent_offsetexists, 0, 0, 1)
ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent_offsetget, 0, 0, 1)
ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent_offsetset, 0, 0, 2)
ZEND_ARG_INFO(0, offset)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent_offsetunset, 0, 0, 1)
ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent___get, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent___set, 0, 0, 2)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent___isset, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fluent___unset, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
定义类方法
ZEND_METHOD(fluent, __construct) {
zval *attributes, *instance;
MAKE_STD_ZVAL(attributes);
array_init(attributes);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &attributes) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
zend_update_property(fluent_ce, instance, ZEND_STRL("attributes"), attributes TSRMLS_DC);
}
ZEND_METHOD(fluent, offsetExists) {
zval *value, *instance, *member;
char *offset;
int offsetLen;
int exists;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset, &offsetLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
MAKE_STD_ZVAL(member);
ZVAL_STRINGL(member, offset, offsetLen, 1);
exists = std_object_handlers.has_property(instance, member, 0, 0 TSRMLS_DC);
zval_ptr_dtor(&member);
RETURN_BOOL(exists);
}
ZEND_METHOD(fluent, offsetGet) {
zval *value, *instance;
char *offset;
int offsetLen;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset, &offsetLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
php_printf("%s\n", offset);
value = zend_read_property(fluent_ce, instance, offset, offsetLen, 1 TSRMLS_CC);
RETURN_ZVAL(value, 1, 0);
}
ZEND_METHOD(fluent, offsetSet) {
zval *value, *instance;
char *offset;
int offsetLen;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset, &offsetLen, &value) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
zend_update_property(fluent_ce, instance, offset, offsetLen, value TSRMLS_DC);
}
ZEND_METHOD(fluent, offsetUnset) {
zval *instance, *member;
char *offset;
int offsetLen;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset, &offsetLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
MAKE_STD_ZVAL(member);
ZVAL_STRINGL(member, offset, offsetLen, 1);
std_object_handlers.unset_property(instance, member, 0 TSRMLS_DC);
zval_ptr_dtor(&member);
}
ZEND_METHOD(fluent, __get) {
char *name;
int nameLen;
zval *attributes, *instance;
zval **value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &nameLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
attributes = zend_read_property(fluent_ce, instance, ZEND_STRL("attributes"), 0 TSRMLS_CC);
if (zend_hash_find(Z_ARRVAL_P(attributes), name, nameLen + 1, (void **)&value) == SUCCESS) {
RETURN_ZVAL(*value, 1, 0);
} else {
RETURN_NULL();
}
}
ZEND_METHOD(fluent, __set) {
char *name;
int nameLen;
zval *value, *attributes, *instance;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &nameLen, &value) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
attributes = zend_read_property(fluent_ce, instance, ZEND_STRL("attributes"), 0 TSRMLS_CC);
add_assoc_zval(attributes, name, value);
zval_add_ref(&value);
}
ZEND_METHOD(fluent, __isset) {
char *name;
int nameLen;
zval *attributes, *instance;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &nameLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
attributes = zend_read_property(fluent_ce, instance, ZEND_STRL("attributes"), 0 TSRMLS_CC);
if (zend_hash_exists(Z_ARRVAL_P(attributes), name, nameLen + 1) == SUCCESS) {
RETURN_BOOL(1);
} else {
RETURN_BOOL(0);
}
}
ZEND_METHOD(fluent, __unset) {
char *name;
int nameLen;
zval *attributes, *instance;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &nameLen) == FAILURE) {
RETURN_NULL();
}
instance = getThis();
attributes = zend_read_property(fluent_ce, instance, ZEND_STRL("attributes"), 0 TSRMLS_CC);
zend_hash_del(Z_ARRVAL_P(attributes), name, nameLen + 1);
}
static zend_function_entry fluent_methods[] = {
ZEND_ME(fluent, __construct, arginfo_fluent___construct, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
ZEND_ME(fluent, offsetExists, arginfo_fluent_offsetexists, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, offsetGet, arginfo_fluent_offsetget, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, offsetSet, arginfo_fluent_offsetset, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, offsetUnset, arginfo_fluent_offsetunset, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, __get, arginfo_fluent___get, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, __set, arginfo_fluent___set, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, __isset, arginfo_fluent___isset, ZEND_ACC_PUBLIC)
ZEND_ME(fluent, __unset, arginfo_fluent___unset, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
模块初始化的时候注册类
PHP_MINIT_FUNCTION(learn)
{
zval *attributes;
MAKE_STD_ZVAL(attributes);
array_init(attributes);
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Fluent", fluent_methods);
fluent_ce = zend_register_internal_class(&ce TSRMLS_CC);
// implements ArrayAccess
zend_class_implements(fluent_ce TSRMLS_DC, 1, zend_ce_arrayaccess);
zend_declare_property_null(fluent_ce, ZEND_STRL("attributes"), ZEND_ACC_PROTECTED TSRMLS_CC);
return SUCCESS;
}