为PHP开发C++扩展

转载:https://blog.csdn.net/baozoumingren/article/details/75735780

说明:windows上的头文件和cpp直接那过来 直接

phpize

./configure --with-php-config=/usr/bin/php-config
make(如果用到c++11(如auto),请在上步生成的makefile中g++后面追加-std=c++11)
make install 

如果扩展库依赖与别的库和头文件,需要在config.m4中添加

//这里是增加的
  PHP_ADD_INCLUDE(./include) //额外头文件路径
  PHP_ADD_LIBRARY(stdc++, 1, BAOZOU_SHARED_LIBADD)
  PHP_ADD_LIBRARY_WITH_PATH(hello, ./lib, BAOZOU_SHARED_LIBADD) //额外库路径
  PHP_REQUIRE_CXX()
  PHP_SUBST(BAOZOU_SHARED_LIBADD)

在生成的makefile中可以增加宏定义 和c++11的一些东西

CPPFLAGS = -DHAVE_CONFIG_H -DBOOST_NO_EXCEPTIONS
CXX = g++ -std=c++11

时隔一段时间,PHP版本变更,导致原先生成的静态库不兼容(不同linux环境下编译的c++静态/动态库也可能出现不兼容现象,建议换PHP版本的同时重新编一下c++静态/动态库),经过仔细琢磨发现PHP版本高于5.5,也就是5.6版本,生成扩展骨架有些许出入,总体来说还是一样的!(本文不涉及PHP7版本)

在linux下调用C++源码写PHP扩展:

1:准备

1.1:下载相应版本的PHP源码包,可以上PHP官网或者网上搜索。本文采用的是php-5.5.38,下载地址:http://pan.baidu.com/s/1bo626mj (炸了没更新的话请自行去网上下载)

1.2:linux操作系统,本文采用的是centoOS 64位。

1.3:LAMP/LNMP环境。

2:编写C++静态库

2.1:编写头文件:hello.h

#include <string>
std::string hello_joint(std::string a, std::string b);

    1
    2

2.2:实现头文件定义的函数:hello.cpp

#include "hello.h"
std::string hello_joint(std::string a, std::string b)
{
    std::string str = a+b;
    return str;
}

    1
    2
    3
    4
    5
    6

2.3:生成hello.o文件

g++ -c hello.cpp

    1

2.4:生成静态库libhello.a文件

ar -r libhello.a hello.o

    1

2.5:写个简单的test.cpp测试下:

#include <iostream>
#include "hello.h"
int main()
{
    std::string a = std::string("Hello ");
    std::string b = std::string("World!");
    std::cout<<hello_joint(a, b)<<std::endl;
    return 0;
}

    1
    2
    3
    4
    5
    6
    7
    8
    9

2.6:编译

g++ test.cpp libhello.a

    1

2.7:运行

./a.out

    1

如果终端输出“Hello World!”,说明你的C++静态库制作成功!

3:生成PHP扩展骨架

3.1:cd到源码压缩包文件夹,分别执行以下命令:

tar  -zxvf   php-5.5.38.tar.gz
cd php-5.5.38/ext/

    1
    2

3.2:使用ext下的ext_skel文件生成PHP扩展骨架

./ext_skel --extname=baozou

    1

命名为baozou没有啥特别意义,这里命名会影响到生成的.so扩展文件,你也可以命名为test,那么生成的扩展文件名就是test.so

4:修改扩展骨架内容,生成PHP扩展

4.1:cd到baozou目录下编辑config.m4,需要改的地方大概归结为三处:

①找到如下三行,并把前面的注释dnl去掉(:set nu后大约在16~18行)。

PHP_ARG_ENABLE(baozou, whether to enable baozou support,
Make sure that the comment is aligned:
[  --enable-baozou           Enable baozou support])

    1
    2
    3

②找到如下的代码,并在它的下面加上相应代码,以支持c++调用(:set nu后大约在20~21行)。

if test "$PHP_BAOZOU" != "no"; then
  dnl Write more examples of tests here...

    1
    2

要加上的相应代码,注意其中的参数名。
说明:
BAOZOU_SHARED_LIBADD,不知道啥意思,反正照着改前缀就行了。

PHP_ADD_INCLUDE(./include),头文件路径,”./”啥意思应该不用说明了。。

PHP_ADD_LIBRARY_WITH_PATH(hello, ./lib, BAOZOU_SHARED_LIBADD),这里的第一个参数是静态库/动态库名字,由于make时使用的是-l参数,所以命名应该为:”lib命名.a”或者”lib命名.so”,如上面的”libhello.a”,这里则填写”hello”即可。第二个参数是路径

if test "$PHP_BAOZOU" != "no"; then
  dnl Write more examples of tests here...

  //这里是增加的
  PHP_ADD_INCLUDE(./include)
  PHP_ADD_LIBRARY(stdc++, 1, BAOZOU_SHARED_LIBADD)
  PHP_ADD_LIBRARY_WITH_PATH(hello, ./lib, BAOZOU_SHARED_LIBADD)
  PHP_REQUIRE_CXX()
  PHP_SUBST(BAOZOU_SHARED_LIBADD)

    1
    2
    3
    4
    5
    6
    7
    8
    9

③在该函数的最后一行,把baozou.c改为baozou.cpp
PHP_NEW_EXTENSION(baozou, baozou.cpp, $ext_shared)

4.2:修改baozou.cpp文件

①将baozou.c改名为baozou.cpp

mv baozou.c baozou.cpp

    1

②在两处地方加上EXTERN “C”标识,这点不要忘了:
第一处加extern “C”{},并在下面加上需要的c++头文件(:set nu后,约在20行后):

extern "C" {  --这里是添加的
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_baozou.h"
}           --这里也是添加的
//下面也是添加的,拷贝后最好删除这些中文注释
#include <string>
#include "hello.h"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

第二处加BEGIN_EXTERN_C()和END_EXTERN_C() (:set nu后,约在70多行左右):

#ifdef COMPILE_DL_BAOZOU
BEGIN_EXTERN_C()       ==>添加的
ZEND_GET_MODULE(baozou)
END_EXTERN_C()         ==>添加的
#endif

    1
    2
    3
    4
    5

③在如下的地方,加入函数名称,可以理解为php要调用的函数的声明(:set nu后,约在48行左右;如果是PHP5.6则位置可能不同):

PHP_FE(myfunc, NULL)

    1

说明:这里定义的myfunc是能被PHP直接调用的函数名,如大家熟悉的fopen(),fclose();生成扩展后,我们可以直接调用名为myfunc()的函数。

④注意到baozou.cpp中,它编写了一个函数例子confirm_baozou_compiled,现在,我们也需要完成myfunc的函数实现,它接收来自php的参数,并在函数中调用c++函数。

PHP_FUNCTION(myfunc)
{
    char *arg1 = NULL, *arg2 = NULL;
    int arg1_len, arg2_len;
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"ss",&arg1,&arg1_len,&arg2,&arg2_len) == FAILURE)
        return;
    std::string a = std::string(arg1);
    std::string b = std::string(arg2);
    std::string res = hello_joint(a,b);
    RETURN_STRING(res.c_str(),res.length(),0);
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

简单的说明下个人理解(小白求不喷):

if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ss”, &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE)

在这个判断前,定义的参数是传入zend_parse_parameters()函数里的,从PHP捕获过来的值,比如我PHP那边写了个myfunc(‘hello’,’world’),则在这里定义2个char *类型参数,这里是C++语法,不是很懂。

在这个判断后,经过zend_parse_parameters()函数处理的值可以用C++的语法获取到,然后传给C++定义的函数”hello_joint()”。

最后RETURN_STRING(res.c_str(), res.length(),0); 指的是你想返回给PHP的数据类型,返回类型可参考:
http://blog.sina.com.cn/s/blog_4ee5c07b0101e1a1.html

关于zend_parse_parameters()函数:
第一二个参数个人暂时无法解释,第三个参数”ss”,指的是2个string类型的参数,如果多个则多个s,第三个参数以后,则是在这函数上面定义的一些参数,如果是”s”类型的参数,还要定义长度(char*, int 前者接收指针,后者接收长度),如果不是,则不需要定义了。参考:http://laravelacademy.org/post/7233.html

4.3:编写php_baozou.h文件,在PHP_FUNCTION(confirm_baozou_compiled)下增加如下代码(:set nu后大约在47行;在PHP5.6中可能没有PHP_FUNCTION(confirm_baozou_compiled),那么直接把下面代码扔在最后一行就行了。):

PHP_FUNCTION(myfunc);

    1

该文件主要是声明PHP函数名,如果没有声明则会报错(被坑过)

:4.4:生成PHP扩展

①在当前目录下新建lib文件夹,将之前制作的libhello.a拷贝到lib里;
②在当前目录下新建include文件夹,将hello.h头文件拷贝到这里。

③利用php工具,按顺序运行如下四个命令(注意php-config路径):

phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install

    1
    2
    3
    4

备注1:如果在make过程中提示下面错误,请往下拉找到第⑥步解决。

/usr/bin/ld: /home/lnmp1.4-full/src/php-5.5.38/ext/baozou/lib/libhello.a(hello.o): relocation R_X86_64_32 against `__gxx_personality_v0' can not be used when making a shared object; recompile with -fPIC
/home/lnmp1.4-full/src/php-5.5.38/ext/baozou/lib/libhello.a: could not read symbols: Bad value
collect2: ld returned 1 exit status
make: *** [baozou.la] Error 1

    1
    2
    3
    4

备注2:如果中间修改了程序代码什么的,记得要从第一个phpize重新执行来过。
如果生成成功,则会提示你成功生成在XXX目录,复制目录路径cd过去看一下有没有生成baozou.so文件

④修改php.ini配置文件
如果不知道php.ini文件在哪的话,你可以在php程序中输出(echo phpinfo())。
找到如下内容:

Loaded Configuration File => /usr/local/etc/php/5.6/php.ini

    1

打开php.ini,找到enable_dl,改为On;

enable_dl = On         --在php.ini 中开启php动态加载dl

    1

再找到有很多extension=xxxxxx.so的地方,添加下面句子,加载动态库,当然也可以在程序中手动加载。

extension=baozou.so

    1

⑤在php程序中测试:
记得重启下php:

/etc/init.d/php-fpm stop
/etc/init.d/php-fpm start

    1
    2

在php文件中编辑以下代码:

<?php
echo myfunc("hello ", "world!");

    1
    2

网页输出:

测试成功

⑥如果在make的时候出现如下错误(倒数后四行):

/usr/bin/ld: /home/lnmp1.4-full/src/php-5.5.38/ext/baozou/lib/libhello.a(hello.o): relocation R_X86_64_32 against `__gxx_personality_v0' can not be used when making a shared object; recompile with -fPIC
/home/lnmp1.4-full/src/php-5.5.38/ext/baozou/lib/libhello.a: could not read symbols: Bad value
collect2: ld returned 1 exit status
make: *** [baozou.la] Error 1

    1
    2
    3
    4

重点是看第一行错误:

can not be used when making a shared object; recompile with -fPIC

    1

虽然该错误的解决方法,百度给的是重新编译一个新的libhello.a静态库文件可以解决,但是在我用的php-5.5.38版本中,静态库是无法编译通过的,就算怎么改也是一样(测试了好久只会报这个错误)

个人的解决方法是:使用动态库。也就是说之前生成的libhello.a静态库文件,要替换成动态库文件libhello.so,生成动态库的方法为:

cd到之前的生成hello.o的文件夹下,执行分别如下命令即可生成libhello.so动态库文件:

g++ -c -fPIC hello.cpp
g++ -shared -fPIC -o libhello.so hello.o

    1
    2

再cd到之前编译PHP扩展的文件夹,将.so文件拷贝到lib文件夹下,并且删除lib文件夹下之前编译过的.a文件,然后重新执行如下编译代码(注意执行路径要在编译扩展的文件夹下,不是lib文件夹下)

phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install

    1
    2
    3
    4

执行make install之后出现以下提示:

[root@localhost baozou]# make install
Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-non-zts-20121212/

    1
    2

cd到上面路径,执行“ll”查询即可看到生成了扩展文件.so,然后进行复制到extension文件夹下:

cp baozou.so ../../extension/

    1

回到上面的④步骤,直到⑤步骤页面输出“hello world”,说明PHP开发扩展成功

小结:
总算写完了,差不多花了一天,并且自己亲手测试整篇文章的可行性,终于可以生成可用的简单的PHP扩展了
这玩意坑比较多,比如网上(以及本文里)生成静态库文件,然后编译PHP扩展时,一直报错(我觉得理论上来说静态库也是可以生成扩展的,如果有人能按照上面方法用静态库编译出PHP扩展的请告知下,评估下错误原因,谢谢了)。自己也不会C++,所以在编译C++的时候撞了很多坑,最后得感谢C++的新入职同事帮我编译过静态库文件,测试发现不行,最后用动态库文件瞎猫碰上死耗子,通过了,由衷感谢C++的同事的帮忙。
---------------------
作者:baozoumingren
来源:CSDN
原文:https://blog.csdn.net/baozoumingren/article/details/75735780
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值