Press "Enter" to skip to content

扩展PHP[Extending PHP](一)

这篇文章将会系统的介绍如何开发自己的PHP扩展, 也许你会说,网上这样的文章已经很多了,为什么还要写? 呵呵,我当然不会只是简单的重复。 这篇文章将会涉及到很多的高级技术,比如在自己的扩展中使用资源,开发一个类给脚本使用,在PHP中使用C++的对象等等,另外这篇文章还会穿插很多Zend引擎和PHP内核的知识,比如变量的实现,资源的实现,PHP4和PHP5对类的不同实现等等

首先,我们要有个认识,那就是在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-example
    

    PHP的扩展框架构建系统,支持全套的.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)

    24 Comments

    1. Hallie
      Hallie April 29, 2016

      Hi Jed! Yo87#u21&;re not wrong, the web copy sucks in the extreme. I’ve just rewritten it for the Nth time, and hope it’s something of an improvement Thanks for the link, am reading now

    2. james
      james July 3, 2015

      写的很好,但是还是看不太懂,我所实现的功能在网上找了许久都未曾有结果,我想实现一个通过PHPweb开发的项目类型GM的后台系统,想让PHP和C++进行交互,服务端的代码不是太了解,这是不能直接操作数据库,必须要通过PHP和c++交互让c++编译好的exe去执行,但是现在思路一点儿都没有

    3. friparia
      friparia December 30, 2014

      鸟哥,您好,请问怎么调试扩展,比如Segment Fault这种错误,用ltrace的话输出大部分都是框架的信息。。

    4. rocky
      rocky July 23, 2013

      真是让人受益非浅的文章呀,虽然看的不大懂,但是我个人感觉,你那写出来的字母是多么的爽,不带$的,鸟哥,顶!

    5. 郭新华1989
      郭新华1989 July 19, 2013

      我想知道鸟哥是怎么调试扩展的,上面说“不停的重复最后俩个步骤”是否是调试扩展之意?可是最后2步只有make没有make install修改的扩展不起作用啊~

    6. zhaolei254
      zhaolei254 January 17, 2013

      如果 这个扩展 要增加编译的 *.c .h 文件要怎么改config.m4?

    7. tfengjun
      tfengjun May 13, 2011

      @雪候鸟
      粗略看了一下,基本上是把printf的解析的活全部都自己干了。。。周末搞搞试下。3x

    8. 雪候鸟
      雪候鸟 May 13, 2011

      @tfengjun 请参考PHP标准扩展中对printf的实现.

    9. tfengjun
      tfengjun May 13, 2011

      博主对php颇有研究啊,最近正在写一个php扩展,遇到一个问题,想请教一下:
      有一个底层C实现的函数:
      void *foo(int n, char *fmt, …);
      还有一个支持va_arg的版本:
      void *foo(int n, char *fmt, va_list vl);
      现在需要写一个php模块来调用这个函数,从而实现以下功能:
      call_foo(1, “test %s”, $str);
      $a->call_foo(1, “test %b”, $str, $str_len);
      $a->call_foo(1, “test %d”, 15);
      等等类似printf语法的东西。
      现在遇到的问题是在php模块中,得到的参数是zval *,并且参数个数是可变的,怎样去调用foo()呢?
      谢谢!

    10. netbus
      netbus December 4, 2010

      请问下如果是nginx ./configure那要怎么写啊。。其他地方还有什么不同嘛。。先谢了:)

    11. 雪候鸟
      雪候鸟 December 28, 2009

      @lixiphp 握手~, 😉

    12. 雪候鸟
      雪候鸟 December 28, 2009

      @lixiphp 握手~, 😉

    13. lixiphp
      lixiphp December 25, 2009

      强烈支持博主,你在linux下的c-php很有技术含量。希望能与博主结识为朋友!

    14. onesec
      onesec December 25, 2008

      希望楼主继续写关于扩展方面的文章.加油!

    15. Andy
      Andy December 12, 2008

      强烈支持楼主,非常好的文章!

    16. 雪候鸟
      雪候鸟 August 25, 2008

      恩,有些内容我不会完全归类到这个系列下面,但是确实和这个系列相关的。最后我会把相关的东西整理起来,所以你可以看看我其他的文章,不一定等我归类到这个系列后再看,;)

    17. Eyelid
      Eyelid August 25, 2008

      网上有很多类似文章,但介绍的要么过于简单,要么过于艰深,希望博主再接在励。

    18. 雪候鸟
      雪候鸟 August 24, 2008

      恩,网上有不少介绍扩展PHP的文章,;) “也许你会说,网上这样的文章已经很多了,为什么还要写?”

    19. Eyelid
      Eyelid August 24, 2008

      部分内容似乎在哪见过,不过写的不错,支持博主。

    20. ylcz
      ylcz August 21, 2008

      很难啃,舔一舔也好

    21. clear
      clear August 16, 2008

      顶,学习了

    22. 雪候鸟
      雪候鸟 August 16, 2008

      恩,这个文章要持续开发。

    Comments are closed.