Linux下C++实现PHP扩展中级应用(一)

    此篇文章准备分2个部分来讲述:
    第一部分主要详细讲述一下怎么构建一个完成的C++应用扩展模块;
    第二部分主要讲述在PHP及Zend框架下怎么使用Zend API和C++语言来实现自己所要的功能以及项目的开发;
    此篇文章所运用的环境在Linux 2.4.21-4.ELsmp(Red Hat Linux 3.2.3-20),Apache/2.2.8,gcc version 3.2.3 20030502,PHP 5.2.5 (cli),Zend Engine v2.2.0下进行。

 

一、前言

  1. 以前写过一些使用C语言来扩展PHP的应用[1]。在淘宝使用C++做PHP的扩展做项目的过程中,遇到了一些问题,从Google中查找,使用C++来开发PHP的中文文章少之又少,而且没有一个手册来告诉用户怎么写m4[2]文件,怎么使用zend[3]引擎的一套api函数去写相关PHP的接口,这里就怎么用C++语言来开发PHP的一些心得介绍给大家,希望有心人能够有所收获;

二、为什么要用C++开发PHP

  1. 使用C++比用C语言开发PHP主要有2个好处:
    • 使用C++能够很方便的操作string类型,本身的一些容器和模板[4]、以及面对对象的功能让开发者能够节省大量开发的时间,这是比较重要的一点;
    • C++可以直接使用C的库,只需要extern "C" {}将其C的头文件和库定义包含起来就可以,不需要太多的移植工作,可以重复利用前人的代码或者库进行后续的工作;
  2. 用C++开发PHP是快速、迅捷的,熟悉了相关的定义以及语法,相信开发PHP不是难事。

三、书写config文件

config.m4[5]或config.w32[6]文件是编译基础中最核心的文件,这个文件主要是用autoconf[7]来产生configure[8]配置文件,继而自动生成大家所熟悉的Makefile文件,以Linux系统为例:

你可以自己书写config.m4文件,也可以由Shell脚本 ext_skel[9] 来生成样板:

  [cnangel@localhost ~]$wget http://docs.php.net/get/php-5.2.5.tar.bz2/from/cn.php.net/mirror
[cnangel@localhost ~]$tar -jxf php-5.2.5.tar.bz2
[cnangel@localhost ~]$cd php-5.2.6/ext
[cnangel@localhost ext]./ext_skel --extname=extern_name

接着你会发现在ext目录下多了一个叫extern_name的目录。进入该目录,会发现目录下有几个文件:

  [cnangel@localhost ext_name]$ls -l
总计 32
-rw-r--r-- 1 cnangel cnangel 2103 06-29 19:00 config.m4
-rw-r--r-- 1 cnangel cnangel 310 06-29 19:00 config.w32
-rw-r--r-- 1 cnangel cnangel 8 06-29 19:00 CREDITS
-rw-r--r-- 1 cnangel cnangel 0 06-29 19:00 EXPERIMENTAL
-rw-r--r-- 1 cnangel cnangel 5305 06-29 19:00 ext_name.c
-rw-r--r-- 1 cnangel cnangel 508 06-29 19:00 ext_name.php
-rw-r--r-- 1 cnangel cnangel 2766 06-29 19:00 php_ext_name.h
drwxr-xr-x 2 cnangel cnangel 4096 06-29 19:00 tests

然后可以根据提示来修改config.m4文件,这里有几个重要的宏命令如下:

  • dnl 是注释;
  • PHP_ARG_WITH 或者 PHP_ARG_ENABLE 指定了PHP扩展模块的工作方式,前者意味着不需要第三方库,后者正好相反;
  • PHP_REQUIRE_CXX 用于指定这个扩展用到了C++;
  • PHP_ADD_INCLUDE 指定PHP扩展模块用到的头文件目录;
  • PHP_CHECK_LIBRARY 指定PHP扩展模块PHP_ADD_LIBRARY_WITH_PATH定义以及库连接错误信息等;
  • PHP_ADD_LIBRARY(stdc++,"",EXTERN_NAME_LIBADD)用于将标准C++库链接进入扩展
  • PHP_SUBST(EXTERN_NAME_SHARED_LIBADD) 用于说明这个扩展编译成动态链接库的形式;
  • PHP_NEW_EXTENSION 用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开;

ext_skel默认生成的模块框架是针对C的,我们要使用C++进行PHP扩展, 那除以上的PHP_REQUIRE_CXX, PHP_ADD_LIBRARY两个宏必需外,还要把extern_name.c改名成extern_name.cpp。

需要注意的是,在config.m4里面可以使用类似的Makefile语法,片段如下:

  PHP_REQUIRE_CXX()
INCLUDES="$INCLUDES `mysql_config --cflags`"
PHP_ADD_LIBRARY(stdc++, "", EXTRA_LDFLAGS)
EXTRA_LDFLAGS="$EXTRA_LDFLAGS `mysql_config --libs` -lmemcached"
AC_CHECK_HEADERS([mysql/mysql.h])
CPPFILE="ext_name.cpp antiForbitWord.cpp antiBaseDict.cpp Trie.cpp Logger.cpp antiEncodeConverter.cpp strnormalize.cpp"
PHP_NEW_EXTENSION(ext_name, $CPPFILE, $ext_shared)

四、书写.h文件

这里指修改php_ext_name.h这个头文件。

由于TSRM.h这个文件所包含的函数和类都是用纯C语言写的,故应该使用extern来说明如下:

  extern "C" {
#ifdef ZTS
#include "TSRM.h"
#endif
}

如果该php_ext_name.h头文件或者ext_name.cpp文件用到了C++语言中的一些容器或者一些函数,则需要在头文件中包含相应的c++库的头文件,否则会出现找不到相应的C++函数错误。

五、书写.cpp文件

这里指修改ext_name.cpp这个cpp文件。

由于config.h、php.h、php_ini.h和ext/standard/info.h中包含的函数和类如TSRM.h一样,都是用纯C语言写的,所以也需要用extern说明如下:

  extern "C" {
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
  #include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
}

#include "php_ext_name.h" 这句则已经不需要包含在extern "C"内,另外,ZEND_GET_MODULE这个宏命令也是需要特别申明如下:

  #ifdef COMPILE_DL_EXT_NAME
BEGIN_EXTERN_C()
ZEND_GET_MODULE(ext_name)
END_EXTERN_C()
#endif

总之,把一些C写的库或轰用兼容的方式给解决。

六、初步执行

这里需要用到一个命令:phpize[10],命令如下:

  [cnangel@localhost ext_name]$phpize
[cnangel@localhost ext_name]$./configure
[cnangel@localhost ext_name]$make

注意:可以使用用phpize生成configure执行文件后,可以使用./configure --help查看帮助信息,修改config.m4文件可以修改configure的帮助信息。每次修改了config.m4文件,需要使用清除临时文件 命令phpize --clean来完成消除configure。

七、初步应用

怎么应用到php上,把刚才的扩展模块当作一个普通的php函数调用呢?简单的应用直接使用命令:

  [cnangel@localhost ext_name]$sudo make install

如果有多个php版本,则寻找扩展库目录显得没有那么好找了,比如,你的php执行文件的路径在/usr/local/php/bin/目录下,想知道php扩展模块所在的目录的话,那么执行(PHP5.0以上):

  [cnangel@localhost ext_name]$/usr/local/php/bin/php-config | grep extension-dir | sed 's/.*\[\(.*\)]/\1/'`

PHP5.0以下执行:

  [cnangel@localhost ext_name]$/usr/local/php/bin/php-config --extension-dir

这样你可以发现你的扩展库的路径:

  /usr/local/php/lib/php/extensions/no-debug-non-zts-20060613

当然,你可以修改php.ini,找到php安装的配置文件,修改extension_dir的值为你想要的一个路径另外,需要将你的扩展写入php.ini,像这样:

   extension=ext_name.so

最后,找到扩展库的路径后,将modules下面的extern_name.so文件复制到扩展库的目录下,重新启动一下Apache进程:

  [cnangel@localhost ext_name]$which httpd
/usr/bin/httpd
[cnangel@localhost ext_name]$sudo /usr/bin/httpd -k stop
[cnangel@localhost ext_name]$sudo /usr/bin/httpd -k start

把这个样例ext_name.php复制到web路径上去,看看是否好使啦?下一节我们将详细讲一些Zend API的宏在ext_name.cpp中的一些复杂应用。

Monthly Archives

Pages

Powered by Movable Type 7.7.2

About this Entry

This page contains a single entry by Cnangel published on June 10, 2008 3:44 PM.

memcached应用开发心得 was the previous entry in this blog.

RPM简单模板 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.