用擴展開發一個PHP類


原文: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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM