adwin's blog
从头开始,编译一个PHP扩展
post by:adwin 2017-12-13 9:26

因为最近在研究一些不可描述的事情,所以有想到要编译一个自己的PHP扩展,想到自己入门计算机时的启蒙书籍还是谭浩强的《C程序设计》,才发现其实也还是可以写一点C的,这么多年过去了,恐怕快要忘干净了吧,不过还好最近有接触GO,在风格上还是和C挺相近的。

于是上网查找资料是少不了的,但是经过我随手查找的两篇资料(百度随便搜的,排名相对靠前的两篇文章,一个CSDN一个CNBLOGS)与实际操作对比来看,第一是对过程和原理的解释不够清楚,第二是实际操作存在很多错误的地方,于是也就想着自己记录一下自己的过程,确保在当前条件下的无误。

环境:

阿里云ECS Ubuntu 12.04 64位

PHP 7.1.9 编译安装

 

好了暂时就介绍这么多,首先我的PHP本身就是编译安装的,那么我就按照我的情况来写。首先我们设定一个基本前提,就是已经下载PHP的源码并且编译安装成功,那么我们知道,PHP的扩展实在ext目录下,那么下面我们的编译操作基本上就都是在ext目录下进行的。

cd导PHP源码的ext目录,这里会有两个比较重要的文件,ext_skel和ext_skel_win32.php,顾名思义后者是Windows下的,前者是Linux下的,我们的环境是Ubuntu,所以以Linux环境为例,Windows环境基本相同,但暂未实践,如果有机会后边补上。首先我们创建一个扩展,当前目录下执行./ext_skel --extname=hello,其中hello就是我们想要创建的扩展的名字,创建成功以后会在ext目录下多处一个hello目录,也就是以扩展名字命名的目录,我们cd进去,就可以看到初始的一些文件了。其中hello.c就是我们这个扩展的C源码了,但是我们先不用管他,我们先打开config.m4,去掉下面几行内容的注释(config.m4中 dnl 为注释):

##动态编译选项,通过.so的方式链接,去掉dnl注释: PHP_ARG_WITH(helloworld, for helloworld support, Make sure that the comment is aligned: [ --with-helloworld Include helloworld support]) ##静态编译选项,通过enable来启用,去掉dnl注释: PHP_ARG_ENABLE(helloworld, whether to enable helloworld support, Make sure that the comment is aligned:

[ --enable-helloworld Enable helloworld support])


OK,这个时候我们就可以开始编译了,什么?还没写代码?别急,代码里边有示例,我们先编译成功再说。

当前目录下执行phpize,执行后看到configure以及一干文件(关于phpize属于编译PHP扩展的基本使用,如果遇到问题的话自行搜索即可,这里不做赘述),然后执行

./configure --with-php-config=/alidata/server/php7/bin/php-config

没别的,就是指定php-config文件,这个文件在你编译好的PHP目录下的bin目录里,很好找,然后就是make && make install,之后make test,就算大功告成吧,这样我们的hello.so扩展文件就编译好了,这个so文件的位置呢,取决与你的extensions目录的位置,我这里是/alidata/server/php7/lib/php/extensions/no-debug-non-zts-20160303目录下,就看到了hello.so,那么我们在php.ini里加载这个so,就可以了。

那么我们怎么确定这个扩展现在没有问题了呢?实际上啊,刚刚在ext目录下,你会看到一个hello.php文件,这个文件就是新扩展的测试脚本啦,我们配置好php.ini以后,执行php hello.php,就可以看到类似下面的提示:

Functions available in the test extension: 

confirm_hello_compiled

Congratulations! You have successfully modified ext/hello/config.m4. Module hello is now compiled into PHP. 那也就是说,告诉我们,我们的hello扩展呢,就已经编译好了,现在扩展里面的函数呢,有confirm_hello_compiled,当然只有这一个,至此,算是完成了第一步,创建和编译扩展,那么下边我们来写一个函数,输出一个hello world。 上边我们有提到,扩展的源码文件就是hello.c了,那么我们现在打开hello.c,可以看到一个PHP_FUNCTION(confirm_hello_compiled)这么一个函数,大致内容如下:

PHP_FUNCTION(confirm_hello_compiled)
{
char *arg = NULL;
size_t arg_len, len;
zend_string *strg;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
return;
}

strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "hello", arg);

RETURN_STR(strg);
}

好了,其实这里呢,就是扩展的函数定义的部分了,这里定义了一个confirm_hello_compiled函数,然后返回了一个Congratulations的字符串,那么我们就在这个函数下边,添加一个我们自己的函数,hello_world,如下:

PHP_FUNCTION(hello_world)
{
php_printf("Hello World!\n");
RETURN_TRUE;
}

OK,这里呢我们定义了一个hello_world函数,内容呢就是输出Hello World!这个字符串然后返回一个true,现在函数已经写完了,但是还不够,同样在这个文件,找到PHP_FE(confirm_hello_compiled,NULL),在下边加上一行:PHP_FE(hello_world, NULL),这回我们就可以保存退出了,然后重新编译:./configure && make && make install,这个时候如果你再执行php hello.php的话,就会看到函数列表里多了一个hello_world,但我们还是写一个PHP page来感受一下吧,在PHP文件里调用hello_world()函数试试看,是不是输出了hello world呢?

到这里,我们就尝试编译了一个自己编写的PHP扩展,但其实这还远远不够,万里长征算是卖出了第一步,剩下的我们后边再聊!

评论:
MrDashi
2017-12-21 08:54 回复
大师路过,摩拜神威 ...
adwin
2017-12-25 09:29 回复
@MrDashi:诶嘛,大师好久不见啊
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容