原文:http://my.oschina.net/mickelfeng/blog/122519?p=1
假設我們要用PHP擴展實 現一個類Person,它有一個private的成員變量$_name和兩個public的實例方法getName()和setName(),可以用 PHP代碼表示如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<?php
class
Person
{
private
$_name
;
public
function
getName()
{
return
$this
-> _name;
}
public
function
setName(
$name
)
{
$this
-> _name =
$name
;
}
}
|
1. 聲明方法:還使用第一篇文章里面用過的示例,首先在頭文件php_fetion_echo.h里加入方法聲明。
PHP_METHOD(Person, __construct); PHP_METHOD(Person, __destruct); PHP_METHOD(Person, getName); PHP_METHOD(Person, setName);
前面的擴展在聲明函數時使用PHP_FUNCTION宏,而在實現類擴展時我們使用PHP_METHOD宏,第一個參數指定類名,第二個參數指定方法名。
2. 方法實現:在fetion_echo.c文件中實現這幾個方法,構造函數和析構函數中只是輸出一些文本。
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
29
|
PHP_METHOD(Person, __construct) {
php_printf(
"__construct called."
);
}
PHP_METHOD(Person, __destruct) {
php_printf(
"__destruct called.<br/>"
);
}
PHP_METHOD(Person, getName) {
zval *self, *name;
self = getThis();
name = zend_read_property(Z_OBJCE_P(self), self, ZEND_STRL(
"_name"
), 0 TSRMLS_CC);
RETURN_STRING(Z_STRVAL_P(name), 0);
}
PHP_METHOD(Person, setName) {
char
*arg = NULL;
int
arg_len;
zval *value, *self;
if
(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"s"
, &arg, &arg_len) == FAILURE) {
WRONG_PARAM_COUNT;
}
self = getThis();
MAKE_STD_ZVAL(value);
ZVAL_STRINGL(value, arg, arg_len, 0);
SEPARATE_ZVAL_TO_MAKE_IS_REF(&value);
zend_update_property(Z_OBJCE_P(self), self, ZEND_STRL(
"_name"
), value TSRMLS_CC);
RETURN_TRUE;
}
|
對上面的代碼做一些解釋:
A. 獲取方法的參數信息,仍然使用zend_parse_parameters函數,與之前我們介紹過的一樣;
B. 獲取this指針(相對於PHP代碼而言,在PHP擴展中仍然使用zval結構表示)使用getThis()函數;
C. 使用MAKE_STD_ZVAL宏申請並初始化一個zval結構,在PHP擴展中,所有的數據類型其實都是用zval結構來表示的,在本系列文章中我會單獨寫一篇來介紹zval。
D. 獲取屬性值使用zend_read_property()函數,使用zend_update_property()函數更新屬性值。
3. 初始化類:在擴展初始化函數中,注冊並初始化類。
zend_class_entry *person_ce;
PHP_MINIT_FUNCTION(fetion_echo)
{
zend_class_entry person; INIT_CLASS_ENTRY(person, "Person", fetion_echo_functions); person_ce = zend_register_internal_class_ex(&person, NULL, NULL TSRMLS_CC); zend_declare_property_null(person_ce, ZEND_STRL("_name"), ZEND_ACC_PRIVATE TSRMLS_CC); return SUCCESS; }
使用INIT_CLASS_ENTRY宏初始化類,第二個參數指定類名,第三個參數是函數表。
4. 注冊到函數:聲明方法的參數,並注冊到函數表中。
ZEND_BEGIN_ARG_INFO(arg_person_setname, 0)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO() const zend_function_entry fetion_echo_functions[] = {
PHP_ME(Person, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(Person, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
PHP_ME(Person, getName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Person, setName, arg_person_setname, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL} /* Must be the last line in fetion_echo_functions[] */ };
類方法參數的聲明與之前我們函數參數聲明方式一致,在注冊類方法到函數表中時使用PHP_ME宏,而不是之前使用的PHP_FE宏。
ZEND_ACC_PUBLIC:指定方法的訪問修飾符
ZEND_ACC_CTOR:指定該方法為構造函數
ZEND_ACC_DTOR:指定該方法為析構函數
5. 運行測試:編譯安裝擴展后,編寫一段簡單的測試腳本:
<?php $person = new Person(); $person->setName("mickelfeng"); echo $person->getName().'<br/>';
運行后可以看到如下輸出,說明擴展工作正常:
__construct called. mickelfeng __destruct called.