PHP: 編寫第一個PHP擴展

今天看了一些PHP擴展的入門文章,很好的文章,但看的還是很吃力。主要是因為不了解文章里說的生命周期,內(nèi)存分配,SAPI這些概念。然后代碼里全是宏,如果不查源碼,和看天書沒什么區(qū)別。所以想記錄一下從源碼的角度分析 hello.c 這個文件的過程,其它文章里有的我就不重復了,我先放代碼,這里有一個部分和原文章不一樣。

隨便新建一個文件夾,里面包含下面3個文件:

config.m4

PHP_ARG_ENABLE(hello, whether to enable Hello
World support,
[ --enable-hello   Enable Hello World support])
if test "$PHP_HELLO" = "yes"; then
  AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
  PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi

php_hello.h

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"

PHP_FUNCTION(hello_world);

extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry

#endif

hello.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_hello.h"

// 模塊所包含的函數(shù)列表信息
static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    {NULL, NULL, NULL}
};

// 模塊自身的相關(guān)信息
// 如模塊名,模塊包含的函數(shù),生命周期,版本號等
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_HELLO_WORLD_EXTNAME,
    hello_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_HELLO_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

// 與動態(tài)加載有關(guān) Dynamic Loading,后面解釋
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

// 你的擴展函數(shù)
PHP_FUNCTION(hello_world)
{
    RETURN_STRING("Hello World", 1);
}

運行下面的命令可以驗證擴展。

phpize
./configure
make
php -dextension=modules/hello.so -r "echo hello_world();"

和原文不同的是這里使用 zend_function_entry 而不是 function_entry。這個和 PHP 的版本有關(guān),不然編譯會出錯。

我們先看這一段代碼

static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    {NULL, NULL, NULL}
};

如果你查看 PHP 官方文檔,也有可能會這樣寫。

static zend_function_entry hello_functions[] = {
    PHP_FE(hello_world, NULL)
    ZEND_FE_END
};

其實說到底,把這些你看不懂的宏的定義找出來,就好了,我們就先找 zend_function_entry,因為我的源碼在下面這個路徑下,所以我先 cd 過去。

/usr/local/Cellar/php56/5.6.32_8/include/php

然后用下面的命令找 zend_function_entry 的定義。

grep -rnw . -e 'zend_function_entry'

發(fā)現(xiàn)在 zend_API.h 這個文件。

./Zend/zend_API.h:41:} zend_function_entry;

源碼是一個結(jié)構(gòu)體,用到存儲函數(shù)的信息。

typedef struct _zend_function_entry {
        const char *fname;
        void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
        const struct _zend_arg_info *arg_info;
        zend_uint num_args;
        zend_uint flags;
} zend_function_entry;

接著再看 PHP_FE,同理使用下面命令。

grep -rnw . -e 'PHP_FE'

得到下面的結(jié)果。

./main/php.h:352:#define PHP_FE         ZEND_FE

再找 ZEND_FE,下面就不寫了,最后查到是這個定義。

#define ZEND_FENTRY(zend_name, name, arg_info, flags)   { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeo
f(struct _zend_arg_info)-1), flags }, 
#define ZEND_FE(name, arg_info)                                         ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)

其實就是一個關(guān)于函數(shù)結(jié)構(gòu)體的信息,和之前的 zend_function_entry 對應。

再看 zend_module_entry 這一段代碼,其實做法也是參照上面的,#if ZEND_MODULE_API_NO >= 20010901 是用來判斷 api 版本是否大于等于 20010901 (這個是年月日),如果大于則在編譯期包含 STANDARD_MODULE_HEADER 這個宏,這些信息都可以在下面這個源文件找到,ZEND_MODULE_API_NO 這個宏也是定義這個文件中的。

zend_modules.h

最后說說這個

// 與動態(tài)加載有關(guān) Dynamic Loading,后面解釋
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

其實我也不是很懂,查了 ZEND_GET_MODULE 這個宏,他是一個函數(shù),用于在運行時供 Zend 引擎獲取模塊的名字。今天大概就到這里,下次再寫函數(shù)帶數(shù)的情況。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容