PHP: 编写第一个PHP扩展

今天看了一些PHP扩展的入门文章,很好的文章,但看的还是很吃力。主要是因为不了解文章里说的生命周期,内存分配,SAPI这些概念。然后代码里全是宏,如果不查源码,和看天书没什么区别。所以想记录一下从源码的角度分析 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"

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

// 模块自身的相关信息
// 如模块名,模块包含的函数,生命周期,版本号等
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
};

// 与动态加载有关 Dynamic Loading,后面解释
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

// 你的扩展函数
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 的版本有关,不然编译会出错。

我们先看这一段代码

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'

发现在 zend_API.h 这个文件。

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

源码是一个结构体,用到存储函数的信息。

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'

得到下面的结果。

./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)

其实就是一个关于函数结构体的信息,和之前的 zend_function_entry 对应。

再看 zend_module_entry 这一段代码,其实做法也是参照上面的,#if ZEND_MODULE_API_NO >= 20010901 是用来判断 api 版本是否大于等于 20010901 (这个是年月日),如果大于则在编译期包含 STANDARD_MODULE_HEADER 这个宏,这些信息都可以在下面这个源文件找到,ZEND_MODULE_API_NO 这个宏也是定义这个文件中的。

zend_modules.h

最后说说这个

// 与动态加载有关 Dynamic Loading,后面解释
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif

其实我也不是很懂,查了 ZEND_GET_MODULE 这个宏,他是一个函数,用于在运行时供 Zend 引擎获取模块的名字。今天大概就到这里,下次再写函数带数的情况。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342