[实践ok]Linux下C++实现PHP扩展中级应用

jackxiang 2010-11-19 23:14 | |
我觉得首先还是看这位兄弟的两篇文章后实践Ok后再接着看吧,那两篇文章写得很好,如下:

http://hi.baidu.com/doyoueat/blog/item/2593153a88604b3371cf6c65.html
http://hi.baidu.com/doyoueat/blog/item/f3b48320f13f2e0f4d088d5a.html

以上两篇文章,博主是实践Ok了的。。。。:)


下载php源码

进入源码路径假如说是 /data/soft/php-5.2.5/

进入ext 目录

./ext_skel --extname=cpptest

进入 cpptest 目录 修改 config.m4

变化主要是这几行
PHP_ARG_ENABLE(cpptest, whether to enable cpptest support,
Make sure that the comment is aligned:
[ --enable-cpptest           Enable cpptest support])

以下几行主要用来指定源代码是 c++的

PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, "", EXTRA_LDFLAGS)
PHP_NEW_EXTENSION(cpptest, cpptest.cpp, $ext_shared)



然后 mv cpptest.c cpptest.cpp

修改 cpptest.cpp

20 extern "C" {
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "php_cpptest.h"
29 }

用 extern c 把原来的c 的 头文件包起来 让其兼容c++

#ifdef COMPILE_DL_CPPTEST
ZEND_GET_MODULE(cpptest)
#endif
修改为如下:
72 BEGIN_EXTERN_C()
73 ZEND_GET_MODULE(cpptest)
74 END_EXTERN_C()

用 BEGIN_EXTERN_C() 还有 END_EXTERN_C() 把 ZEND_GET_MODULE(cpptest) 包起来,最关键是:
#include "cpptest.h"
这个cpp的头文件要放在:
extern "C" { 的花括号里!!!
using std::string;
extern "C" {
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "cpptest.h"
否则会出现编译错误:错误:‘char* PHP_HELLO()’的早先声明有‘C++’链接 错误:与带有‘C’链接的新声明冲突,
再加入:
#ifdef COMPILE_DL_EXT_NAME
  BEGIN_EXTERN_C()
  ZEND_GET_MODULE(ext_name)
  END_EXTERN_C()
#endif
为此,特别注意!!!
------------------------------------------------------------------------------------------------------------
然后可以 随便测试进入几个c++的 头文件

30 #include <iostream>
31 #include <string>
32 #include "cpptest.h"
33 using std::string;

修改一下示例

160 PHP_FUNCTION(confirm_cpptest_compiled)
161 {
174     int len;
175     char *strg;
176     strg=estrdup(PHP_HELLO());
177     std::cout<<strg<<std::endl;
178     len=strlen(strg);
179     RETURN_STRINGL(strg, len, 0);
180    // std::cout<<"hello world"<<std::endl;
181 }
182 char * PHP_HELLO()
183 {
184   string str("aaaaaa");
186   char * cp;
187   cp=estrdup(str.c_str());
188   return cp;
190
191 }

然后添加 cpptest.h

定义 PHP_HELLO()
char * PHP_HELLO();

然后 phpize

./configure --with-php-config=/data/app/php/bin/php-config

make

makeinstall

在 phpini中 引用 so文件

下边就可以测试啦

继续参考下面:

来源:http://hi.baidu.com/lostdays/blog/item/e7efc488b9a5629da5c2722a.html
篇文章准备分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下进行。
一、前言

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

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

使用C++比用C语言开发PHP主要有2个好处:
使用C++能够很方便的操作string类型,本身的一些容器和模板[4]、以及面对对象的功能让开发者能够节省大量开发的时间,这是比较重要的一点;
C++可以直接使用C的库,只需要extern “C” {}将其C的头文件和库定义包含起来就可以,不需要太多的移植工作,可以重复利用前人的代码或者库进行后续的工作;
用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中的一些复杂应用。

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

这里主要讲述在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下进行。

前言

上次我们说到使用c++写一个完整的php扩展,这里以ext_name模块为例复习一下:

首先仍然修改config.m4文件,由于没有引用外面的模块或者相关库,所以不需要使用PHP_ARG_WITH的方式,使用PHP_ARG_ENABLE方式。找到

PHP_NEW_EXTENSION(ext_name, ext_name.c, $ext_shared)
修改成

PHP_REQUIRE_CXX()
  PHP_ADD_LIBRARY(stdc++, "", EXTRA_LDFLAGS)
  PHP_NEW_EXTENSION(ext_name, ext_name.cpp, $ext_shared)
并将ext_name.c重新命名为ext_name.cpp,接着修改其内容,将

#include "php.h"
  #include "php_ini.h"
  #include "ext/standard/info.h"
用extern “C”将其用大括号括起来,修改

ZEND_GET_MODULE(ext_name)


BEGIN_EXTERN_C()
  ZEND_GET_MODULE(ext_name)
  END_EXTERN_C()
到此为止,这就是我们第一章内容,第二章比较庞大,这里还是分节来叙述吧。

概述

概述里面主要简单介绍PHP扩展中的一些大致结构和需要注意的事项,做过C扩展PHP的都会知道 PHP_FE是一个宏把这个宏标识的函数,例如:helloworld,这个函数可以直接作用于PHP解释器,比如

<?php
  helloworld();
  ?>
安装ext_name样板后,系统会自动有一个函数confirm_ext_name_compiled,这个函数是可以自行修改的,当然,PHP_FE可以定义多个函数,这些函数都必须在之前进行申明,一般在php_ext_name.h头文件进行申明。

我们还知道,仅仅有头文件和PHP_FE宏来申明这个函数是不行的,这个函数还没有内容,怎么编写这个函数的内容呢?这个在接下来会讲到。

其实,稍微细心的人看了ext_name.cpp就知道,去掉注释后,还有很多的宏命令,比如zend_module_entry、ZEND_GET_MODULE、PHP_MINIT_FUNCTION等等,读者不要着急,下面会一一道来。

关于ext_name.cpp文件中一些变量的命名,通常是PHP模块名(eg:ext_name)前面或者后面有一串字符,比如 le_ext_name、ext_name_functions、这是一种习惯,最好我们在书写的时候遵循这种习惯,这样写出来的代码不仅仅让你自己明 白,让其他的开发人员也能够很快熟悉你的代码。通常一些定义的常量会大写,比如要定义这个模块的名字和版本,可以在头文件中添加:

#define PHP_EXT_NAME_EXTNAME "ext_name"
  #define PHP_EXT_NAME_VERSION "0.1"
然后修改ext_name_module_entry的内容,将”ext_name”和”0.1″分别用PHP_EXT_NAME_EXTNAME和PHP_EXT_NAME_VERSION来替换,这样具有方便且通用。

如果你可能在代码中可能需要用到stl之类的或者c++的一些库,那么你可以在ext_name.cpp文件中添加

#ifndef __APP_CPP__
  #define __APP_CPP__
  #include <iostream>
  #include <fstream>
  #include <string>
  /*
  #include <sstream>
  #include <list>
  #include <vector>
  #include <map>
  #include <hashmap>
  #include <set>
  #include <bitset>
  */
  #endif
PHP 与 Zend API

引用一句经典的原文来说明PHP和Zend API之间的关系

PHP的核心由两部分组成。最底层是Zend引擎(ZE)。ZE把人类易读的脚本解析成机器可读的符号,
  然后在进程空间内执行这些符号。ZE也处理内存管理、变量作用域及调度程序调用。另一部分是PHP内核,
  它绑定了SAPI层(Server Application Programming Interface,通常涉及主机环境,如Apache,IIS,CLI,CGI等),
  并处理与它的通信。它同时对safe_mode和open_basedir的检测提供一致的控制层,就像流层将fopen()、fread()和
  fwrite()等用户空间的函数与文件和网络I/O联系起来一样。
模块信息

模块信息主要体现在ext_name_module_entry结构上,它包含了

1, 标准模块的头

通常用 “STANDARD_MODULE_HEADER” 来填充,它指定了模块的四个成员:

标识整个模块结构大小的 size
值为 ZEND_MODULE_API_NO 常量的 zend_api
标识是否为调试版本(使用 ZEND_DEBUG 进行编译)的 zend_debug
还有一个用来标识是否启用了 ZTS (Zend 线程安全,使用 ZTS 或USING_ZTS 进行编译)的 zts。
2, 模块名称

模块名称这个名字就是使用 phpinfo() 函数后在“Additional Modules”部分所显示的名称。

3, PHP扩展可用到的函数或类

zend函数块的指针

4, 模块启动函数

5, 模块关闭函数

6, 请求启动函数

7, 请求关闭函数

8, 模块信息函数

9, 模块的版本号

10, 其它结构元素

首先和C初使的操作一样. ./ext_skel –extname=hahacpp 创建一个模块.

进入后修改config.m4文件,由于没有引用外面的模块或者相关库,所以不需要使用PHP_ARG_WITH的方式,使用PHP_ARG_ENABLE方式。

找到

PHP_NEW_EXTENSION(ext_name, ext_name.c, $ext_shared)修改成

PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, “”, EXTRA_LDFLAGS)
PHP_NEW_EXTENSION(ext_name, hahacpp.cpp, $ext_shared)

注意: 在第三句括号内的第二个参数,将hahacpp.c重新命名为hahacpp.cpp,

接着修改其内容,在hahacpp.c中将

#include “php.h”
#include “php_ini.h”
#include “ext/standard/info.h”

用extern “C”将其用大括号括起来,接着修改

ZEND_GET_MODULE(ext_name)为

BEGIN_EXTERN_C()
ZEND_GET_MODULE(ext_name)
END_EXTERN_C()

完成.保存退出.

phpize

./configure

make

make install

完成C++框架的开发. 下一篇继续扩展…

上一篇说到C++做了一个简单的框架.

今天整了一个下午.加入了文件包含及相关的调用.

步骤如下:

首先需要使用这一句:phpize –clean 清除上次编译的配置.

编辑 haha.h

bool iswork();

编辑 haha.cpp

#include “haha.h”
bool iswork(){
return true;
}

编辑hahacpp.cpp ,添加

#incude “haha.h”

增加一个函数:

zend_function_entry mycpp_functions[] = {
PHP_FE(confirm_mycpp_compiled, NULL) /* For testing, remove later.
*/
PHP_FE(helloHAHA, NULL)
{NULL, NULL, NULL} /* Must be the last line in mycpp_functions[] */
};

增加函数实现:

PHP_FUNCTION(helloHAHA){
char *strg;
int len=0;

len = spprintf(&strg, 0, “wahaha!!! cpp!!!”); php_printf(”wahaah,cpp!!!n”);

if(iswork()){
php_printf(”can work!!n”);
}
else{
php_printf(”can’tn”);
}

}

保存退出. 编辑config.m4, 找到文件中的:

PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++,”",EXTRA_LDFLAGS)
PHP_NEW_EXTENSION(hahacpp,hahacpp.cpp,$ext_shared)

将最后一行改为:

PHP_NEW_EXTENSION(hahacpp,hahacpp.cpp haha.cpp,$ext_shared)

保存退出,执行
phpize
./configure
make
make install
完成!!~在PHP中执行 helloHAHA() 试试吧~~哈!
继续参考:
http://www.laruence.com/2009/04/28/719.html

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:http://jackxiang.com/post/3761/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2012-5-12 23:04
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]