以下简单介绍如何在C语言层面使用PHP类
对象
创建一个PHP对象,对象类似关联数组,对象之上可关联任意多个函数
1 2 3 4 5 6 7
| PHP_FUNCTION(makeObject) { object_init(return_value);
zend_update_property_string(NULL, return_value, "prop1", strlen("prop1"), "val1"); zend_update_property_long(NULL, return_value, "prop2", strlen("prop2"), 123); }
|
调用函数并打印结果var_dump(makeObject());
,输出如下
1 2 3 4 5 6
| object(stdClass)#1 (2) { ["prop1"]=> string(3) "val1" ["prop2"]=> int(123) }
|
类
创建一个类模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| zend_class_entry *test_ce_myclass;
static const zend_function_entry test_methods[] = { PHP_ME(MyClass, hello, NULL, ZEND_ACC_PUBLIC) PHP_FE_END };
static void test_init_myclass() { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "MyClass", test_methods); test_ce_myclass = zend_register_internal_class(&ce); zend_declare_property_bool(test_ce_myclass, "success", sizeof("success")-1, 1, ZEND_ACC_PUBLIC); }
PHP_METHOD(MyClass, hello) { RETURN_STRING("hello"); }
|
其中,函数test_init_myclass
的最后一个参数ZEND_ACC_PUBLIC
为访问控制标记之一公共访问,常用的访问控制标记还有以下几个
1 2 3 4 5 6 7
| ZEND_ACC_STATIC ZEND_ACC_PUBLIC ZEND_ACC_PROTECTED ZEND_ACC_PRIVATE ZEND_ACC_CTOR ZEND_ACC_DTOR ZEND_ACC_DEPRECATED
|
一个class定义注册相关逻辑已经完成,要在PHP中使用类MyClass还需要在模块初始化MINIT
中添加运行test_init_myclass
以加载类MyClass
1 2 3 4 5
| PHP_MINIT_FUNCTION(test) { test_init_myclass(); return SUCCESS; }
|
编译test
模块并开启后,运行var_dump(new MyClass());
,将得到以下类似输出
1 2 3 4
| object(MyClass)#1 (1) { ["success"]=> bool(true) }
|
我们也可以直接在C层面初始化并生成一个实例化类对象,增加一个工厂方法factory
1 2 3 4 5 6 7 8 9 10
| static const zend_function_entry test_methods[] = { PHP_ME(MyClass, hello, NULL, ZEND_ACC_PUBLIC) PHP_ME(MyClass, factory, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_FE_END };
PHP_METHOD(MyClass, factory) { object_init_ex(return_value, test_ce_myclass); }
|
此时,我们可以使用MyClass::factory()
获取一个新的MyClass对象
如果需要对MyClass进行一些操作,像在PHP使用构造方法,在test_methods里添加__construct
,如此,PHP在new MyClass()
将自动调用构造方法__construct
1 2 3 4 5 6 7 8 9 10 11 12
| static const zend_function_entry test_methods[] = { PHP_ME(MyClass, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(MyClass, hello, NULL, ZEND_ACC_PUBLIC) PHP_ME(MyClass, factory, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_FE_END };
PHP_METHOD(MyClass, __construct) {
}
|
调用类方法
上面,虽然对象在new时会自动调用__construct
函数进行初始化,但factory
不会自动调用构造函数,仅返回包含默认值的新对象,为此,我们需要在factory
内调用构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include "zend_interfaces.h"
PHP_METHOD(MyClass, factory) { zval *myzval;
ZEND_PARSE_PARAMETERS_START(1,1) Z_PARAM_ZVAL(myzval) ZEND_PARSE_PARAMETERS_END();
object_init_ex(return_value, test_ce_myclass);
zend_call_method(return_value, test_ce_myclass, NULL, "__construct", sizeof("__construct")-1, NULL, 1, myzval, NULL); }
|
this
先前我们已经了解函数返回值return_value
的使用,现在,我们来看如何在方法内访问$this
,PHP提供getThis()
函数
1 2 3 4 5 6 7 8 9 10 11
| PHP_METHOD(MyClass, __construct) { char *msg; size_t msg_len;
ZEND_PARSE_PARAMETERS_START(1,1) Z_PARAM_STRING(msg, msg_len) ZEND_PARSE_PARAMETERS_END();
zend_update_property_string(test_ce_myclass, getThis(), "msg", sizeof("msg")-1, msg); }
|
关联结构体
我们构建一个结构体,这个结构体在PHP是无法访问的,但可以在扩展内访问对该结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| typedef struct _test_struct { zend_object std; int unknown_id; char *unknown_str; } test_struct;
static zend_object *create_test_struct(zend_class_entry *class_type) { test_struct *intern;
intern = ecalloc(1, sizeof(test_struct) + zend_object_properties_size(class_type));
zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type);
intern->std.handlers = zend_get_std_object_handlers();
return &intern->std; }
static void free_test_struct(void *object) { test_struct *secrets = (test_struct*)object; if (secrets->unknown_str) { efree(secrets->unknown_str); } efree(secrets); }
|
访问结构体test_struct
1 2 3 4 5 6 7 8
| PHP_METHOD(MyClass, attachStruct) { test_struct *secrets;
secrets = (test_struct*)getThis();
RETURN_LONG(secrets->unknown_id); }
|
异常
PHP的异常继承自Exception
,所以下面除了新增一个异常类,还涉及了类的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <zend_exceptions.h>
zend_class_entry *test_ce_exception;
static void test_init_exception() { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "MyException", NULL); test_ce_exception = zend_register_internal_class_ex(&ce, (zend_class_entry*)zend_exception_get_default()); }
PHP_MINIT_FUNCTION(test) { test_init_myclass(); test_init_exception(); return SUCCESS; }
|
在方法/函数中抛出异常
1 2 3
| PHP_METHOD(MyClass, throwExcept) { zend_throw_exception(test_ce_exception, "custom exception throw", 1024); }
|
完整代码
php_ext_tutorial
参考文档
PHP Extensions Made Eldrich: Classes