首先,我们要有个认识,那就是在php中的类和函数,以至于变量,在本质上都是C实现的。 你所编写的脚本,最终都会被转换成C代码来执行。 这个和我在以前文章中(深入理解PHP原理之Opcodes)介绍的opcode并不冲突, 因为Zend虚拟机的指令最终还是要翻译成C代码来执行。
开发者也就可以使用C/C++来写一些PHP的函数,类。从而扩展PHP, 那么,为什么要扩展PHP呢? 很多同学只是盲目的去扩展,认为扩展PHP是一个很有“意义”的事(许三多语气),而我认为,扩展PHP只是在特定的情况才有必要,最主要的俩个原因是:
与外部的库做交互, 比如你有一个C/C++的库, 不妨假设,这个库呢就是实现了一个字符串加密和解密,而你并没有这个库的源码,也就是说,你无法把这个库在PHP中实现, 那么你只有编写一个PHP扩展,来做为一个桥梁,连接起你的PHP和这个库。
效率, 不容置疑,PHP编写的脚本,比同等的C/C++编写的脚本,效率自然要低很多。 当你用常见的手段,再也无法提高你的脚本的效率,而效率要求又是那么的紧迫, 那么我只能劝你, 把你的逻辑,用C/C++改写吧。。
其实第二个理由,已经是很充足了,但是用扩展来实现你的逻辑,并不是那么简单的事情:
首先如果你是使用PHP来实现你的逻辑,那么你不需要考虑资源管理,Zend会替你完成。其次你也不需要担心, 代码的一个简单的错误,就会导致PHP core dump。
而如果你用C来编写PHP的扩展,那么你就要自己考虑这些事情,自己管理资源的分配,使用,释放。 你也要学会适应segmentation fault :)。 我要提醒你的是, 因为PHP是一个长时间运行的模块(因为Apache是一个长时间运行的Web服务器), 所以,你千万要防止资源泄露, 我就遇到过一个简单的字符串泄露, 在一段时间以后,Apache占用的内存超过了1个G。
接下来,总结一下用C/C++扩展PHP的优缺点:
优点:
效率,还是效率
减少PHP脚本的复杂度, 极端情况下, 你只需要在PHP脚本中,简单的调用一个扩展实现的函数,然后你所有的功能都就被扩展实现了,呵呵
而缺点也是显而易见的:
开发复杂
可维护性降低
开发周期变长, 最简单的一个例子,当你用PHP脚本的时候, 如果你发现某个判断条件出错,你只要修改了这一行,保存,那么就立刻能见效。 而如果是在C/C++编写的PHP扩展中, 那你可需要,修改源码,重新编译,然后重新load进PHP, 然后重启Apache,才能见效。。
恩,现在你看到了, 用C/C++实现PHP扩展的优点和缺点了,那么你就可以自己权衡你的逻辑,是要用那种方式实现了。呵呵
如果你熟悉C,那么编写一个PHP扩展,并不是什么非常难的事情。 PHP本身就提供了一个框架,来简化你的开发。
最简单的方式来开始一个PHP扩展的开发,是使用PHP提供的扩展框架wizard ext_skel, 它会生成一个PHP扩展所必须的最基本的代码, 要使用它,首先你要下载PHP的源码,或者开发包, 进入PHP源码的ext目录, 就会发现这个工具。比如我下载了PHP源码php5.2-SRC到/home/xinchen/,那么这个工具的路径就在: **/home/xinchen/php5.2-SRC/ext**下。
这个工具的使用方式也很简单, 比如我们要创建一个名叫example的扩展,那么:
$ cd ~/php5.2-SRC/ext
$ ./ext_skel --extname=example
Creating directory example
Creating basic files: config.m4 config.w32 .cvsignore example.c php_example.h CREDITS EXPERIMENTAL tests/001.phpt example.php [done].
To use your new extension, you will have to execute the following steps:
1. $ cd ..
2. $ vi ext/example/config.m4
3. $ ./buildconf
4. $ ./configure --[with|enable]-example
5. $ make
6. $ ./php -f ext/example/example.php
7. $ vi ext/example/example.c
8. $ make
Repeat steps 3-6 until you are satisfied with ext/example/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.这样,就在ext目录下生成了一个名为example的目录,并在这个目录下生成了所有的要完成一个PHP扩展所必须的文件和代码(事实上,这个目录下的文件,已经可以生成一个PHP的标准扩展了,只不过这个扩展不完成任何功能,就好像我们使用Visual Studio的Wizard生成的一个空的MFC框架一样)。接下来,我们要做的就是不停的重复最后俩个步骤,从而实现我们的功能。
在这个目录下,我们要尤其关注的是example.c这个文件,这个文件是我们扩展的主要文件,也就是说,如果我们要充实我们的扩展,那么就需要在这个文件中进行工作。 在后面的内容中,我会消息介绍这个文件中包含的各个字段,结构的含义,现在我们就只是简单的掠过他。
第二个要特别注意的文件就是config.m4,如果读者你有过在Unix/Linux下的编程经历,那么你或许听过autoconf和m4, m4是一个宏解释工具,它会把输入文件中的宏展开到输出文件。所以这个config.m4是PHP扩展框架所必须的,也是关键的一个文件,用来生成我们扩展的makefile。
在config.m4中,有一行:
PHP_ARG_ENABLE(example, whether to enable example support,
[ --enable-example Enable example support])在m4中,dnl表示注释, 这段指令创建了一个configure时的参数“enable-example”, 第二个参数会显示在当configure处理到这个模块的configure文件的时候。第三个参数,会在用户输入./configurehelp的时候,作为一个可选的选项被显示。
另外还有一段:
PHP_ARG_WITH(example, for example support,
[ --with-example Include example support])严格来说,上面的俩段没有太大的区别,只不过with是说明了,要启用这个模块,必须要的先决条件,也就是说这个模块依赖于某些其他模块。
就好像:
./configure --with-apxs=/usr/local/apache/bin/apxs --enable-examplePHP的扩展框架构建系统,支持全套的.m4语法,当然还支持用户自定义的宏, 下面我介绍一些常见的,由PHP扩展框架构建系统定义的宏:
PHP_CHECK_LIBRARY(library, func [, found [, not-found [, extra-libs]]])
在库library中查找func是否存在,如果存在则这个宏会被展开成found,否则not-found;
PHP_DEFINE(what, [value])
这个就是对AC_DEFUN简单包装,最终会被展开成:
#define what value
PHP_REQURE_CXX
如果你的扩展是使用C++编写,那么你就必须使用这个宏,来告诉编译器使用C++编译器。这个宏会被展开成:
AC_PROG_CXX
AC_PROG_CXXCPP更多的由PHP扩展框架构建系统提供的宏,你可以到acinclude.m4中查看。
还有一些其他的有ext_skel创建的文件:
CREDITES 这个文件没什么太大的作用,只是用来在发布你的扩展的时候附加一些其他信息 ,比如作者啊,等等。
EXPERIMENTAL 这个文件只是标志说,这个扩展是实验性的,所以你可以不用管它
example.php 这个文件是用来简单测试你的扩展的
php_example.h 这个是我们扩展的头文件
tests/001.phpt 这个也是个测试文件, 不过使用的是单元测试, 阶段测试, 具体内容,你打开一看便知 ;)
下一次我讲介绍一些要开发扩展,最好要先了解的知识,比如变量的内部表示,copy-on-write , change-on-write机制, 函数的内部表示==。 当然,这其中有些知识是我以前的文章已经涉及过的:
PHP Life Cycle演讲幻灯片
深入浅出PHP
深入理解PHP原理之Opcodes
PHP的函数(Introspecting PHP Function)