(一):
此篇文章準(zhǔn)備分2個(gè)部分來(lái)講述:
第一部分主要詳細(xì)講述一下怎么構(gòu)建一個(gè)完成的C++應(yīng)用擴(kuò)展模塊;
第二部分主要講述在PHP及Zend框架下怎么使用Zend API和C++語(yǔ)言來(lái)實(shí)現(xiàn)自己所要的功能以及項(xiàng)目的開(kāi)發(fā);
此篇文章所運(yùn)用的環(huán)境在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下進(jìn)行。

一、前言
以前寫(xiě)過(guò)一些使用C語(yǔ)言來(lái)擴(kuò)展PHP的應(yīng)用[1]。在淘寶使用C++做PHP的擴(kuò)展做項(xiàng)目的過(guò)程中,遇到了一些問(wèn)題,從Google中查找,使用C++來(lái)開(kāi)發(fā)PHP的中文文章少之又少,而且沒(méi)有一個(gè)手冊(cè)來(lái)告訴用戶怎么寫(xiě)m4[2]文件,怎么使用zend[3]引擎的一套api函數(shù)去寫(xiě)相關(guān)PHP的接口,這里就怎么用C++語(yǔ)言來(lái)開(kāi)發(fā)PHP的一些心得介紹給大家,希望有心人能夠有所收獲;
二、為什么要用C++開(kāi)發(fā)PHP
使用C++比用C語(yǔ)言開(kāi)發(fā)PHP主要有2個(gè)好處:
使用C++能夠很方便的操作string類型,本身的一些容器和模板[4]、以及面對(duì)對(duì)象的功能讓開(kāi)發(fā)者能夠節(jié)省大量開(kāi)發(fā)的時(shí)間,這是比較重要的一點(diǎn);
C++可以直接使用C的庫(kù),只需要extern “C” {}將其C的頭文件和庫(kù)定義包含起來(lái)就可以,不需要太多的移植工作,可以重復(fù)利用前人的代碼或者庫(kù)進(jìn)行后續(xù)的工作;
用C++開(kāi)發(fā)PHP是快速、迅捷的,熟悉了相關(guān)的定義以及語(yǔ)法,相信開(kāi)發(fā)PHP不是難事。

三、書(shū)寫(xiě)config文件

config.m4[5]或config.w32[6]文件是編譯基礎(chǔ)中最核心的文件,這個(gè)文件主要是用autoconf[7]來(lái)產(chǎn)生configure[8]配置文件,繼而自動(dòng)生成大家所熟悉的Makefile文件,以Linux系統(tǒng)為例:

你可以自己書(shū)寫(xiě)config.m4文件,也可以由Shell腳本 ext_skel[9] 來(lái)生成樣板:
[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

接著你會(huì)發(fā)現(xiàn)在ext目錄下多了一個(gè)叫extern_name的目錄。進(jìn)入該目錄,會(huì)發(fā)現(xiàn)目錄下有幾個(gè)文件:
[cnangel@localhost ext_name]$ls -l
總計(jì) 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

然后可以根據(jù)提示來(lái)修改config.m4文件,這里有幾個(gè)重要的宏命令如下:
dnl 是注釋;
PHP_ARG_WITH 或者 PHP_ARG_ENABLE 指定了PHP擴(kuò)展模塊的工作方式,前者意味著不需要第三方庫(kù),后者正好相反;
PHP_REQUIRE_CXX 用于指定這個(gè)擴(kuò)展用到了C++;
PHP_ADD_INCLUDE 指定PHP擴(kuò)展模塊用到的頭文件目錄;
PHP_CHECK_LIBRARY 指定PHP擴(kuò)展模塊PHP_ADD_LIBRARY_WITH_PATH定義以及庫(kù)連接錯(cuò)誤信息等;
PHP_ADD_LIBRARY(stdc++,”",EXTERN_NAME_LIBADD)用于將標(biāo)準(zhǔn)C++庫(kù)鏈接進(jìn)入擴(kuò)展
PHP_SUBST(EXTERN_NAME_SHARED_LIBADD) 用于說(shuō)明這個(gè)擴(kuò)展編譯成動(dòng)態(tài)鏈接庫(kù)的形式;
PHP_NEW_EXTENSION 用于指定有哪些源文件應(yīng)該被編譯,文件和文件之間用空格隔開(kāi);

ext_skel默認(rèn)生成的模塊框架是針對(duì)C的,我們要使用C++進(jìn)行PHP擴(kuò)展, 那除以上的PHP_REQUIRE_CXX, PHP_ADD_LIBRARY兩個(gè)宏必需外,還要把extern_name.c改名成extern_name.cpp。

需要注意的是,在config.m4里面可以使用類似的Makefile語(yǔ)法,片段如下:
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)
四、書(shū)寫(xiě).h文件

這里指修改php_ext_name.h這個(gè)頭文件。

由于TSRM.h這個(gè)文件所包含的函數(shù)和類都是用純C語(yǔ)言寫(xiě)的,故應(yīng)該使用extern來(lái)說(shuō)明如下:
extern “C” {
#ifdef ZTS
#include “TSRM.h”
#endif
}

如果該php_ext_name.h頭文件或者ext_name.cpp文件用到了C++語(yǔ)言中的一些容器或者一些函數(shù),則需要在頭文件中包含相應(yīng)的c++庫(kù)的頭文件,否則會(huì)出現(xiàn)找不到相應(yīng)的C++函數(shù)錯(cuò)誤。
五、書(shū)寫(xiě).cpp文件

這里指修改ext_name.cpp這個(gè)cpp文件。

由于config.h、php.h、php_ini.h和ext/standard/info.h中包含的函數(shù)和類如TSRM.h一樣,都是用純C語(yǔ)言寫(xiě)的,所以也需要用extern說(shuō)明如下:
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” 這句則已經(jīng)不需要包含在extern “C”內(nèi),另外,ZEND_GET_MODULE這個(gè)宏命令也是需要特別申明如下:
#ifdef COMPILE_DL_EXT_NAME
BEGIN_EXTERN_C()
ZEND_GET_MODULE(ext_name)
END_EXTERN_C()
#endif

總之,把一些C寫(xiě)的庫(kù)或轟用兼容的方式給解決。
六、初步執(zhí)行

這里需要用到一個(gè)命令:phpize[10],命令如下:
[cnangel@localhost ext_name]$phpize
[cnangel@localhost ext_name]$./configure
[cnangel@localhost ext_name]$make

注意:可以使用用phpize生成configure執(zhí)行文件后,可以使用./configure –help查看幫助信息,修改config.m4文件可以修改configure的幫助信息。每次修改了config.m4文件,需要使用清除臨時(shí)文件 命令phpize –clean來(lái)完成消除configure。
七、初步應(yīng)用

怎么應(yīng)用到php上,把剛才的擴(kuò)展模塊當(dāng)作一個(gè)普通的php函數(shù)調(diào)用呢?簡(jiǎn)單的應(yīng)用直接使用命令:
[cnangel@localhost ext_name]$sudo make install

如果有多個(gè)php版本,則尋找擴(kuò)展庫(kù)目錄顯得沒(méi)有那么好找了,比如,你的php執(zhí)行文件的路徑在/usr/local/php/bin/目錄下,想知道php擴(kuò)展模塊所在的目錄的話,那么執(zhí)行(PHP5.0以上):
[cnangel@localhost ext_name]$/usr/local/php/bin/php-config | grep extension-dir | sed ’s/.*\[\(.*\)]/\1/’`

PHP5.0以下執(zhí)行:
[cnangel@localhost ext_name]$/usr/local/php/bin/php-config –extension-dir

這樣你可以發(fā)現(xiàn)你的擴(kuò)展庫(kù)的路徑:
/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613

當(dāng)然,你可以修改php.ini,找到php安裝的配置文件,修改extension_dir的值為你想要的一個(gè)路徑另外,需要將你的擴(kuò)展寫(xiě)入php.ini,像這樣:

extension=ext_name.so

最后,找到擴(kuò)展庫(kù)的路徑后,將modules下面的extern_name.so文件復(fù)制到擴(kuò)展庫(kù)的目錄下,重新啟動(dòng)一下Apache進(jìn)程:
[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

把這個(gè)樣例ext_name.php復(fù)制到web路徑上去,看看是否好使啦?下一節(jié)我們將詳細(xì)講一些Zend API的宏在ext_name.cpp中的一些復(fù)雜應(yīng)用。

——————————————————————————-
(二)

這里主要講述在PHP及Zend框架下怎么使用Zend API和C++語(yǔ)言來(lái)實(shí)現(xiàn)自己所要的功能以及項(xiàng)目的開(kāi)發(fā)。
此篇文章所運(yùn)用的環(huán)境在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下進(jìn)行。
前言

上次我們說(shuō)到使用c++寫(xiě)一個(gè)完整的php擴(kuò)展,這里以ext_name模塊為例復(fù)習(xí)一下:

首先仍然修改config.m4文件,由于沒(méi)有引用外面的模塊或者相關(guān)庫(kù),所以不需要使用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,接著修改其內(nèi)容,將
#include “php.h”
#include “php_ini.h”
#include “ext/standard/info.h”

用extern “C”將其用大括號(hào)括起來(lái),修改
ZEND_GET_MODULE(ext_name)


BEGIN_EXTERN_C()
ZEND_GET_MODULE(ext_name)
END_EXTERN_C()

到此為止,這就是我們第一章內(nèi)容,第二章比較龐大,這里還是分節(jié)來(lái)敘述吧。
概述

概述里面主要簡(jiǎn)單介紹PHP擴(kuò)展中的一些大致結(jié)構(gòu)和需要注意的事項(xiàng),做過(guò)C擴(kuò)展PHP的都會(huì)知道 PHP_FE是一個(gè)宏把這個(gè)宏標(biāo)識(shí)的函數(shù),例如:helloworld,這個(gè)函數(shù)可以直接作用于PHP解釋器,比如
< ?php
helloworld();
?>

安裝ext_name樣板后,系統(tǒng)會(huì)自動(dòng)有一個(gè)函數(shù)confirm_ext_name_compiled,這個(gè)函數(shù)是可以自行修改的,當(dāng)然,PHP_FE可以定義多個(gè)函數(shù),這些函數(shù)都必須在之前進(jìn)行申明,一般在php_ext_name.h頭文件進(jìn)行申明。

我們還知道,僅僅有頭文件和PHP_FE宏來(lái)申明這個(gè)函數(shù)是不行的,這個(gè)函數(shù)還沒(méi)有內(nèi)容,怎么編寫(xiě)這個(gè)函數(shù)的內(nèi)容呢?這個(gè)在接下來(lái)會(huì)講到。

其實(shí),稍微細(xì)心的人看了ext_name.cpp就知道,去掉注釋后,還有很多的宏命令,比如zend_module_entry、ZEND_GET_MODULE、PHP_MINIT_FUNCTION等等,讀者不要著急,下面會(huì)一一道來(lái)。

關(guān)于ext_name.cpp文件中一些變量的命名,通常是PHP模塊名(eg:ext_name)前面或者后面有一串字符,比如 le_ext_name、ext_name_functions、這是一種習(xí)慣,最好我們?cè)跁?shū)寫(xiě)的時(shí)候遵循這種習(xí)慣,這樣寫(xiě)出來(lái)的代碼不僅僅讓你自己明 白,讓其他的開(kāi)發(fā)人員也能夠很快熟悉你的代碼。通常一些定義的常量會(huì)大寫(xiě),比如要定義這個(gè)模塊的名字和版本,可以在頭文件中添加:
#define PHP_EXT_NAME_EXTNAME “ext_name”
#define PHP_EXT_NAME_VERSION “0.1″

然后修改ext_name_module_entry的內(nèi)容,將”ext_name”和”0.1″分別用PHP_EXT_NAME_EXTNAME和PHP_EXT_NAME_VERSION來(lái)替換,這樣具有方便且通用。

如果你可能在代碼中可能需要用到stl之類的或者c++的一些庫(kù),那么你可以在ext_name.cpp文件中添加
#ifndef __APP_CPP__
#define __APP_CPP__
#include
#include
#include
/*
#include
#include #include
#include
#include
#include
#include
*/
#endif
PHP 與 Zend API

引用一句經(jīng)典的原文來(lái)說(shuō)明PHP和Zend API之間的關(guān)系
PHP的核心由兩部分組成。最底層是Zend引擎(ZE)。ZE把人類易讀的腳本解析成機(jī)器可讀的符號(hào),
然后在進(jìn)程空間內(nèi)執(zhí)行這些符號(hào)。ZE也處理內(nèi)存管理、變量作用域及調(diào)度程序調(diào)用。另一部分是PHP內(nèi)核,
它綁定了SAPI層(Server Application Programming Interface,通常涉及主機(jī)環(huán)境,如Apache,IIS,CLI,CGI等),
并處理與它的通信。它同時(shí)對(duì)safe_mode和open_basedir的檢測(cè)提供一致的控制層,就像流層將fopen()、fread()和
fwrite()等用戶空間的函數(shù)與文件和網(wǎng)絡(luò)I/O聯(lián)系起來(lái)一樣。
模塊信息

模塊信息主要體現(xiàn)在ext_name_module_entry結(jié)構(gòu)上,它包含了

1, 標(biāo)準(zhǔn)模塊的頭

通常用 “STANDARD_MODULE_HEADER” 來(lái)填充,它指定了模塊的四個(gè)成員:
標(biāo)識(shí)整個(gè)模塊結(jié)構(gòu)大小的 size
值為 ZEND_MODULE_API_NO 常量的 zend_api
標(biāo)識(shí)是否為調(diào)試版本(使用 ZEND_DEBUG 進(jìn)行編譯)的 zend_debug
還有一個(gè)用來(lái)標(biāo)識(shí)是否啟用了 ZTS (Zend 線程安全,使用 ZTS 或USING_ZTS 進(jìn)行編譯)的 zts。

2, 模塊名稱

模塊名稱這個(gè)名字就是使用 phpinfo() 函數(shù)后在“Additional Modules”部分所顯示的名稱。

3, PHP擴(kuò)展可用到的函數(shù)或類

zend函數(shù)塊的指針

4, 模塊啟動(dòng)函數(shù)

5, 模塊關(guān)閉函數(shù)

6, 請(qǐng)求啟動(dòng)函數(shù)

7, 請(qǐng)求關(guān)閉函數(shù)

8, 模塊信息函數(shù)

9, 模塊的版本號(hào)

10, 其它結(jié)構(gòu)元素

原文:http://my.huhoo.net/archives/2008/02/php_ip.html#1
參考:
快速開(kāi)發(fā)一個(gè)PHP擴(kuò)展:http://blog.csdn.net/heiyeshuwu/archive/2008/12/05/3453854.aspx
如何編寫(xiě)PHP擴(kuò)展:http://blog.csdn.net/taft/archive/2006/02/10/596291.aspx