提要
因为网络上没有一篇完整的详细的php c++扩展教程。自己曾经写过php c++扩展,但是时间一久就忘了,然后上网找教程一点点地记忆起来,有点浪费时间浪费生命的感觉,所以现在就写下这一遍文章,详细地记录下来(纯手写)。
环境的准备
本教程的环境是centos6.5、centos7,先安装依赖:
#添加组
groupadd www
#添加php-fpm用户
useradd -c php-fpm-user -g www -M php-fpm
# c和c++编译器
yum install -y gcc gcc-c++
# PHP扩展依赖
yum install -y libxml2-devel openssl-devel libcurl-devel libjpeg-devel libpng-devel libicu-devel openldap-devel
然后下载php的源码,并解压
wget php-5.6.23.tar.gz
tar -xzvf php-5.6.23.tar.gz
先编译安装PHP
./configure --prefix=/usr/local/php\
--with-config-file-path=/usr/local/php/etc\
--with-libdir=lib64\
--enable-fpm\
--with-fpm-user=php-fpm\
--with-fpm-group=www\
--enable-mysqlnd\
--with-mysql=mysqlnd\
--with-mysqli=mysqlnd\
--with-pdo-mysql=mysqlnd\
--enable-opcache\
--enable-pcntl\
--enable-mbstring\
--enable-soap\
--enable-zip\
--enable-calendar\
--enable-bcmath\
--enable-exif\
--enable-ftp\
--enable-intl\
--with-openssl\
--with-zlib\
--with-curl\
--with-gd\
--with-zlib-dir=/usr/lib\
--with-png-dir=/usr/lib\
--with-jpeg-dir=/usr/lib\
--with-gettext\
--with-mhash\
--with-ldap
make
make install
回到上级目录
cd ../
创建 helloworld.proto 文件,该文件包含了要实现的函数列表,内容如下
string helloworld(string arg1, int arg2)
生成骨架
cd php-5.6.23/ext/
./ext_skel --extname=helloworld --proto=../../helloworld.proto
修改config.m4
cd helloworld/
vim config.m4
config.m4 修改前是这样子的:
dnl $Id$
dnl config.m4 for extension helloworld
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(helloworld, for helloworld support,
dnl Make sure that the comment is aligned:
dnl [ --with-helloworld Include helloworld support])
dnl Otherwise use enable:
dnl PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,
dnl Make sure that the comment is aligned:
dnl [ --enable-helloworld Enable helloworld support])
if test "$PHP_HELLOWORLD" != "no"; then
dnl Write more examples of tests here...
dnl # --with-helloworld -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/helloworld.h" # you most likely want to change this
dnl if test -r $PHP_HELLOWORLD/$SEARCH_FOR; then # path given as parameter
dnl HELLOWORLD_DIR=$PHP_HELLOWORLD
dnl else # search default path list
dnl AC_MSG_CHECKING([for helloworld files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl HELLOWORLD_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$HELLOWORLD_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the helloworld distribution])
dnl fi
dnl # --with-helloworld -> add include path
dnl PHP_ADD_INCLUDE($HELLOWORLD_DIR/include)
dnl # --with-helloworld -> check for lib and symbol presence
dnl LIBNAME=helloworld # you may want to change this
dnl LIBSYMBOL=helloworld # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $HELLOWORLD_DIR/$PHP_LIBDIR, HELLOWORLD_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_HELLOWORLDLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong helloworld lib version or lib not found])
dnl ],[
dnl -L$HELLOWORLD_DIR/$PHP_LIBDIR -lm
dnl ])
dnl
dnl PHP_SUBST(HELLOWORLD_SHARED_LIBADD)
PHP_NEW_EXTENSION(helloworld, helloworld.c, $ext_shared)
fi
config.m4 修改后(修改前后对比一下就知道我改了哪里,怎样对比?用 Beyond compare这个软件, 或命令行工具 diff)
dnl $Id$
dnl config.m4 for extension helloworld
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(helloworld, for helloworld support,
dnl Make sure that the comment is aligned:
dnl [ --with-helloworld Include helloworld support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,
Make sure that the comment is aligned:
[ --enable-helloworld Enable helloworld support])
if test "$PHP_HELLOWORLD" != "no"; then
dnl Write more examples of tests here...
dnl # --with-helloworld -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/helloworld.h" # you most likely want to change this
dnl if test -r $PHP_HELLOWORLD/$SEARCH_FOR; then # path given as parameter
dnl HELLOWORLD_DIR=$PHP_HELLOWORLD
dnl else # search default path list
dnl AC_MSG_CHECKING([for helloworld files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl HELLOWORLD_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$HELLOWORLD_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the helloworld distribution])
dnl fi
dnl # --with-helloworld -> add include path
dnl PHP_ADD_INCLUDE($HELLOWORLD_DIR/include)
dnl # --with-helloworld -> check for lib and symbol presence
dnl LIBNAME=helloworld # you may want to change this
dnl LIBSYMBOL=helloworld # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $HELLOWORLD_DIR/$PHP_LIBDIR, HELLOWORLD_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_HELLOWORLDLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong helloworld lib version or lib not found])
dnl ],[
dnl -L$HELLOWORLD_DIR/$PHP_LIBDIR -lm
dnl ])
dnl
dnl PHP_SUBST(HELLOWORLD_SHARED_LIBADD)
PHP_REQUIRE_CXX() dnl 通知Make使用g++
PHP_ADD_LIBRARY(stdc++, 1, EXTRA_LDFLAGS) dnl 加入C++标准库
PHP_NEW_EXTENSION(helloworld, helloworld.cc, $ext_shared)
fi
helloworld.c 文件改名为 helloworld.cc
mv helloworld.c helloworld.cc
修改 helloworld.cc, (只需修改一下内容,其他内容不用修改)修改前的内容是:
PHP_FUNCTION(helloworld)
{
char *arg1 = NULL;
int argc = ZEND_NUM_ARGS();
int arg1_len;
long arg2;
if (zend_parse_parameters(argc TSRMLS_CC, "sl", &arg1, &arg1_len, &arg2) == FAILURE)
return;
php_error(E_WARNING, "helloworld: not yet implemented");
}
然后修改,简单地修改,完全就是用c/c++了
PHP_FUNCTION(helloworld)
{
char *arg1 = NULL;
int argc = ZEND_NUM_ARGS();
int arg1_len;
long arg2;
if (zend_parse_parameters(argc TSRMLS_CC, "sl", &arg1, &arg1_len, &arg2) == FAILURE)
return;
//实现在这里开始写
std::string str = "Hello world!";
str += arg1 ;
// 参数2我就不用了
RETURN_STRINGL(str.c_str(), str.length(), 1); // 返回值是怎么样子的?zend有规定,后面说
//php_error(E_WARNING, "helloworld: not yet implemented");
}
表3.14 从函数直接返回值的宏(zend规定的返回这样子的)
宏 | 说明 |
---|---|
RETURN_RESOURCE(resource) | 返回一个资源。 |
RETURN_BOOL(bool) | 返回一个布尔值。 |
RETURN_NULL() | 返回一个空值。 |
RETURN_LONG(long) | 返回一个长整数。 |
RETURN_DOUBLE(double) | 返回一个双精度浮点数。 |
RETURN_STRING(string, duplicate) | 返回一个字符串。duplicate 表示这个字符是否使用 estrdup() 进行复制。 |
RETURN_STRINGL(string, length, duplicate) | 返回一个定长的字符串。其余跟 RETURN_STRING 相同。这个宏速度更快而且是二进制安全的。 |
RETURN_EMPTY_STRING() | 返回一个空字符串。 |
RETURN_FALSE | 返回一个布尔值假。 |
RETURN_TRUE | 返回一个布尔值真。 |
表3.15 设置函数返回值的宏
宏 | 说明 |
---|---|
RETVAL_RESOURCE(resource) | 设定返回值为指定的一个资源。 |
RETVAL_BOOL(bool) | 设定返回值为指定的一个布尔值。 |
RETVAL_NULL | 设定返回值为空值 |
RETVAL_LONG(long) | 设定返回值为指定的一个长整数。 |
RETVAL_DOUBLE(double) | 设定返回值为指定的一个双精度浮点数。 |
RETVAL_STRING(string, duplicate) | 设定返回值为指定的一个字符串,duplicate 含义同 RETURN_STRING。 |
RETVAL_STRINGL(string, length, duplicate) | 设定返回值为指定的一个定长的字符串。其余跟 RETVAL_STRING 相同。这个宏速度更快而且是二进制安全的。 |
RETVAL_EMPTY_STRING | 设定返回值为空字符串。 |
RETVAL_FALSE | 设定返回值为布尔值假。 |
RETVAL_TRUE | 设定返回值为布尔值真。 |
如果是想直接编到php里面
如果是想以动态扩展库的方式。