信息发布软件,b2b软件,广告发布软件

标题: C++ 和PHP这二个搞夫妻关系终有了这个结果了 [打印本页]

作者: 群发软件    时间: 2017-6-16 20:41
标题: C++ 和PHP这二个搞夫妻关系终有了这个结果了
本帖最后由 群发软件 于 2017-6-16 20:42 编辑

PHP是比较流行的脚本语言,WEB开发使用广泛,如何在C++程序中不依赖任何WEB SERVER调用PHP,并取得执行结果完成交互,这是本文代码所要实现的功能。

  PHP安装好后,在目录下有一个php-cgi.exe,我们只要执行这个CGI程序,并将数据通过命名管道传递给它,然后把执行结果通过命名管道读取出来即可,过程并不复杂!请看如下代码:

  命名管道的创建:

  SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES)};

  sa.bInheritHandle = 1;

  sa.lpSecurityDescriptor = NULL;

  HANDLE hStdoutR, hStdoutW, hStdinR, hStdinW;

  CreatePipe(&hStdoutR, &hStdoutW, &sa, 0);

  SetHandleInformation(hStdoutR,HANDLE_FLAG_INHERIT, 0);

  CreatePipe(&hStdinR, &hStdinW, &sa, 0);

  SetHandleInformation(hStdinW, HANDLE_FLAG_INHERIT, 0);

  启动php-cgi进程: STARTUPINFO si = {sizeof(STARTUPINFO)};

  PROCESS_INFORMATION pi;

  si.dwFlags = STARTF_USESTDHANDLES;

  si.hStdOutput = hStdoutW;

  si.hStdInput = hStdinR;

  char env[255] = "REQUEST_METHOD=POST\0CONTENT_LENGTH=18\0CONTENT_TYPE=

  application/x-www-form-urlencoded\0SCRIPT_FILENAME=D:\\test.php";

  if(!CreateProcess(NULL, "d:\\php5\\php-cgi.exe D:\\test.php",

  NULL, NULL, 1, NORMAL_PRIORITY_CLASS, env, NULL, &si, &pi))

  return 0;

  CloseHandle(hStdoutW);

  CloseHandle(hStdinR);

  传递数据:if(!WriteFile(hStdinW, "var=Hello VCKBASE!", 18, &dwWritten, NULL))

  return 0;

  CloseHandle(hStdinW);

  读取返回数据:char buf[1000] = {0};

  DWORD dwRead = 0;

  while(ReadFile(hStdoutR, buf, sizeof(buf), &dwRead, NULL) && dwRead != 0){

  printf(buf);

  }

  CloseHandle(hStdoutR);

  D盘的test.php

  <?

  echo $_REQUEST["var"];

  ?>

  执行结果:

  X-Powered-By: PHP/5.3.1

  Content-type: text/html

  Hello VCKBASE!

  实际上,C++调用其他CGI程序,例如PERL,方法也大同小异,如果你打算做一个自己的WEB服务器,调用CGI程序是少不了的。


在/var/www中建个测试文件夹 cpp
在此文件夹中新建c++文件sort.cpp,如下
C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件
编译并测试执行通过进行以下步骤。

2.在cpp文件夹下新建文件cpp.html,如下
C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件
3.同样在cpp下建php文件cpp.php,如下
C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件
保存。
4.程序执行如下
C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件
提交后:
C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件




有时候,单纯依靠 PHP “本身”是不行的。尽管普通用户很少遇到这种情况,但一些专业性的应用则经常需要将 php的性能发挥到极致(这里的性能是指速度或功能)。由于受到 PHP 语言本身的限制,同时还可能不得不把庞大的库文件包含到每个脚本当中。因此,某些新功能并不是总能被顺利实现,所以我们必须另外寻找一些方法来克服 PHP 的这些缺点。

了解到了这一点,我们就到了应该接触一下 PHP 的心脏并探究一下它的内核——可以编译成 PHP 并让之工作的 C 代码——的时候了。

概述:

PHP调用动态链接库几个必要步骤为:

1. C/C++编写动态链接库,编译打包成.so文件

2. 初始化一个新的PHP扩展

3. 配置、编写PHP扩展内容,在扩展中使用C/C++调用.so

4. 编译并添加PHP扩展

5. 在PHP应用中直接调用PHP扩展里暴露出来的API

为了从能运行的最简单的例子开始,所以下面的叙述可能不会严格按照上面列的步骤来写,也有可能会重复穿插着写。但总体顺序是一致的。

一. C/C++编写动态链接库,编译打包成.so文件

如果还不会用C++调用自己写的.so库,请参考我的这篇文章:

http://keping.me/cpp_invoke_so/

文中从什么是Name Mangling开始,到如何用调用一个包含简单函数的so库,再到如何从so中加载类,都有详细叙述,并附有可运行示例代码。

二. 初始化PHP扩展

本文中,我们将创建一个叫“vehicle”的PHP扩展,其中包含一个“Car”类。

将要创建的扩展会涉及到以下文件需要修改,这些文件将会出现在vehicle目录下。后面会一一叙述这些文件是怎么来的,现在只是大致了解一下。

1. 需要PHP源码包

如果你不是通过源码包方式安装的PHP,而是通过apt-get install 安装的,那么首先确保你安装了 php-devel 包,然后需要下载php源代码,然后可以跳到第2步。

如果想从源码包编译安装PHP,可以参考我写的另外一篇文章《Linux(Ubuntu12.10)搭建PHP开发环境(源码包方式)》,有详细的每一步的介绍。地址为:

http://keping.me/linux-php-dev-by-source-style/

安装完成以后,跳到第2步。

2. 制作PHP外部扩展

去到PHP源码包目录的ext目录下,我的在

/usr/local/src/php-5.3.22/ext

然后输入命令

sudo ./ext_skel --extname=vehicle

该命令会在ext目录下新建一个vehicle目录,并创建新模块“vehicle”目前所需的所有文件,包括

config.m4, php_vehicle.h, vehicle.php, CREDITS等。

还会列出应该执行哪些后续步骤来使用新的扩展,具体如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

图中我一共使用了三个命令

$ pwd 显示我的php源码包的ext目录所在位置

$ sudo ./ext_skel --extname=vehicle 前面已解释

$ ls 列出了执行上一条命令以后,在vehicle目录下,为我们创建的文件及目录。

如上图所示,在它的指导步骤1~8中,大致了解到我们需要编译config.m4文件,然后需要运行配置,然后需要使用make命令编译等。现在不用知道具体都干些什么,接下来会分别叙述。

三. 配置、搭建最基本的PHP扩展骨架

1. 配置PHP构建系统——编写config.m4

首先我们需要明确的是,为了在PHP扩展中使用C++,那么必须通知PHP构建系统使用C++编译器

通过在config.m4 文件中添加宏PHP_REQUIRE_CXX()来实现。

使用C++的过程中,肯定会用到C++的一些库,至少需要标准库(libstdc++ 大多系统都是这样的)

通过在config.m4 文件中添加宏PHP_ADD_LIBRARY()来实现。

将下面的代码添加到config.m4中

PHP_ARG_ENABLE(vehicle,    [Whether to enable the "vehicle" extension],    [  --enable-vehicle      Enable "vehicle" extension support])if test $PHP_VEHICLE != "no"; then    PHP_REQUIRE_CXX()    PHP_SUBST(VEHICLE_SHARED_LIBADD)    PHP_ADD_LIBRARY(stdc++, 1, VEHICLE_SHARED_LIBADD)    PHP_NEW_EXTENSION(vehicle, vehicle.cc car.cc, $ext_shared)fi

即去掉config.m4文件中某些行前面的注释符号“dnl”,然后添加我们需要的配置。如果还不清楚,可以参考我的配置文件,内容如下图

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

这里的宏PHP_SUBST()是标准autoconf的AC_SUBST()宏的php修改版, 它在将扩展构建为共享模块时需要。

PHP_NEW_EXTENSION宏中,第一个参数代表模块的名称;第二个参数是需要编译的文件,用空格隔开;第三个参数跟宏PHP_SUBST()是一样的。

2. 编写头文件php_vehicle.h

该头文件应该包含以下内容,其中PHP_VEHICLE_EXTNAME “vehicle” 代表该扩展的名称,PHP_VEHICLE_EXTVER则代表版本号,这些都会在php_info()中显示出来。

#ifndef PHP_VEHICLE_H#define PHP_VEHICLE_H#define PHP_VEHICLE_EXTNAME  "vehicle"#define PHP_VEHICLE_EXTVER   "1.0"#ifdef HAVE_CONFIG_H#include "config.h"#endif extern "C" {#include "php.h"}extern zend_module_entry vehicle_module_entry;#define phpext_vehicle_ptr &vehicle_module_entryHP_MINIT_FUNCTION(vehicle)HP_MSUTDOWN_FUNCTION(vehicle)HP_RINIT_FUNCTION(vehicle)HP_RSHUTDOWN_FUNCTION(vehicle)HP_MINFO_FUNCTION(vehicle);#endif /* PHP_VEHICLE_H */

我的php_vehicle.h文件内容如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

要理解PHP_MINIT_FUNCTION()函数,就需要了解PHP的启动步骤。大体就是

1
2
3
4
5
PHP_MINIT_FUNCTION(extension_name) {

/* Initialize functions, classes etc */

}



以上是PHP启动的第一步。为了先跑通整个流程,这里就不在一一叙述每一个方法以及宏的作用了。

3. 编写需要编译的文件(vehicle.cc、car.cc)

使扩展能够运行的最基本的vehicle.cc框架应该包含以下内容

#include "php_vehicle.h"HP_MINIT_FUNCTION(vehicle){    return SUCCESS;}zend_module_entry vehicle_module_entry = {#if ZEND_MODULE_API_NO >= 20010901    STANDARD_MODULE_HEADER,#endif    PHP_VEHICLE_EXTNAME,    NULL,                  /* Functions */    PHP_MINIT(vehicle),    NULL,                  /* MSHUTDOWN */    NULL,                  /* RINIT */    NULL,                  /* RSHUTDOWN */    NULL,                  /* MINFO */#if ZEND_MODULE_API_NO >= 20010901    PHP_VEHICLE_EXTVER,#endif    STANDARD_MODULE_PROPERTIES};#ifdef COMPILE_DL_VEHICLEextern "C" {ZEND_GET_MODULE(vehicle)}#endif

我的vehicle.cc文件如下图所示

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

有了这个文件,我们还不能能创建最基本的PHP扩展,因为前面我们在配置文件config.m4中指定了需要编译的还有car.cc文件,所以必须给出,即使现在里面什么也没有。

所以还需要新建一个car.cc文件,暂时让它空着。

4. 配置、编译、安装

在当前文件目录(即/usr/local/src/php-5.3.22/ext/vehicle)下执行命令

$ sudo /usr/local/php/bin/phpize

$ sudo ./configure --enable-vehicle --with-php-config=/usr/local/php/bin/php-config

如下图所示

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

然后执行make & make install 命令

$ sudo make

$sudo make install

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

安装完成以后,会看到在

/usr/local/php/lib/php/extensions/no-debug-zts-20090626/

目录下有一个新生成的so文件,名为vehicle.so,将库该文件的路径添加到php.ini配置文件中,如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

即添加这句话

“extension="/usr/local/php/lib/php/extensions/no-debug-zts-20090626/vehicle.so"

然后重启你的apache,在phpinfo()中就可以看到已经成功启动扩展vehicle了,如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

至此,最基本的框架已经搭起来了。接下来要做的工作就是如何使用PHP调用C/C++编写的so文件。先从简单的直接调用开始

四. 编写具体的PHP扩展内容

1.  编写car.h头文件

把头文件与源码文件分开是一个不错的习惯,特别是在别人不想知道你的具体实现的时候。这里我们也采取这种方式。

#ifndef VEHICLE_CAR_H#define VEHICLE_CAR_H// A very simple car classclass Car {public:    Car(int maxGear);    void shift(int gear);    void accelerate();    void brake();    int getCurrentSpeed();    int getCurrentGear();private:    int maxGear;    int currentGear;    int speed;};#endif /* VEHICLE_CAR_H */

如上代码所示,我们先定义一个Car类,然后定义几个public的方法以及私有成员变量。然后在car.cc中实现它

#include "car.h"Car::Car(int maxGear) {    this->maxGear = maxGear;    this->currentGear = 1;    this->speed = 0;}void Car::shift(int gear) {    if (gear < 1 || gear > maxGear) {        return;    }    currentGear = gear;}void Car::accelerate() {    speed += (5 * this->getCurrentGear());}void Car::brake() {    speed -= (5 * this->getCurrentGear());}int Car::getCurrentSpeed() {    return speed;}int Car::getCurrentGear() {    return currentGear;}

现在我们已经定义好了Car类,那么如何使其暴露给PHP用户空间,让PHP能够调用这些方法呢。

首先你需要定义一个包含有function_entry 表的PHP类来调用Car,这个function_entry 表里就包含了每一个你想暴露给PHP的C++方法。这里所指的这个PHP类就是前面提到的vehicle.cc。更新一下vehicle.cc的内容,如下所示。

#include "php_vehicle.h"zend_class_entry *car_ceHP_METHOD(Car, __construct){}PHP_METHOD(Car, p_shift){}PHP_METHOD(Car, p_accelerate){}PHP_METHOD(Car, p_brake){}PHP_METHOD(Car, p_getCurrentSpeed){}PHP_METHOD(Car, p_getCurrentGear){}function_entry car_methods[] = {    PHP_ME(Car,  __construct,     NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)    PHP_ME(Car,  p_shift,           NULL, ZEND_ACC_PUBLIC)    PHP_ME(Car,  p_accelerate,      NULL, ZEND_ACC_PUBLIC)    PHP_ME(Car,  p_brake,           NULL, ZEND_ACC_PUBLIC)    PHP_ME(Car,  p_getCurrentSpeed, NULL, ZEND_ACC_PUBLIC)    PHP_ME(Car,  p_getCurrentGear,  NULL, ZEND_ACC_PUBLIC)    {NULL, NULL, NULL}}HP_MINIT_FUNCTION(vehicle){    zend_class_entry ce;    INIT_CLASS_ENTRY(ce, "Car", car_methods);    car_ce = zend_register_internal_class(&ce TSRMLS_CC);    return SUCCESS;}zend_module_entry vehicle_module_entry = {#if ZEND_MODULE_API_NO >= 20010901    STANDARD_MODULE_HEADER,#endif    PHP_VEHICLE_EXTNAME,    NULL,        /* Functions */    PHP_MINIT(vehicle),        /* MINIT */    NULL,        /* MSHUTDOWN */    NULL,        /* RINIT */    NULL,        /* RSHUTDOWN */    NULL,        /* MINFO */#if ZEND_MODULE_API_NO >= 20010901    PHP_VEHICLE_EXTVER,#endif    STANDARD_MODULE_PROPERTIES};#ifdef COMPILE_DL_VEHICLEextern "C" {ZEND_GET_MODULE(vehicle)}#endif

可以看到在PHP_METHOD中的函数名称与Car.cc类里写的函数名称并不需要一样,你可以定义任意自己觉得合适的函数名,为了表示方便,我这里一律添加前缀"p_"。这里每一个函数都还没有具体实现。

前面提到的function_entry表在上诉代码中可以看到由很多PHP_ME组成,每一个都代表了想要暴露给PHP用户空间的方法,最后一定以{NULL,NULL,NULL}表示结束。

现在我们已经有了C++的类,也有了PHP类,那么如何把两者联系起来呢?

首先你需要做的是定义一个zend_object_hander。然后定义一个结构,该结构包含了这个hander和C++的类。在PHP5中一个object其实就是一个hander,可以如下定义。

zend_object_handlers car_object_handlers;struct car_object {    zend_object std;    Car *car;};

该结构就会把C++的对象与zend的对象联系起来,然后你需要把下列代码添加到你的vehicle.cc文件中去,在PHP_METHOD方法之前。

void car_free_storage(void *object TSRMLS_DC){    car_object *obj = (car_object *)object;    delete obj->car;     zend_hash_destroy(obj->std.properties);    FREE_HASHTABLE(obj->std.properties);    efree(obj);}zend_object_value car_create_handler(zend_class_entry *type TSRMLS_DC){    zval *tmp;    zend_object_value retval;    car_object *obj = (car_object *)emalloc(sizeof(car_object));    memset(obj, 0, sizeof(car_object));    obj->std.ce = type;    ALLOC_HASHTABLE(obj->std.properties);    zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);    zend_hash_copy(obj->std.properties, &type->default_properties,        (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *));    retval.handle = zend_objects_store_put(obj, NULL,        car_free_storage, NULL TSRMLS_CC);    retval.handlers = &car_object_handlers;    return retval;}

然后更新一下PHP_MINIT_FUNCTION,这个函数前面提到过,就是PHP的扩展的模块初始化函数,如下所示:

PHP_MINIT_FUNCTION(vehicle){    zend_class_entry ce;    INIT_CLASS_ENTRY(ce, "Car", car_methods);    car_ce = zend_register_internal_class(&ce TSRMLS_CC);    car_ce->create_object = car_create_handler;    memcpy(&car_object_handlers,        zend_get_std_object_handlers(), sizeof(zend_object_handlers));    car_object_handlers.clone_obj = NULL;    return SUCCESS;}

可能这里对“TSRMLS_DC”这个宏比较疑惑,它其实是 “ , void ***tsrm_ls”

然后编写一下构造函数

PHP_METHOD(Car, __construct){    long maxGear;    Car *car = NULL;    zval *object = getThis();    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &maxGear) == FAILURE) {        RETURN_NULL();    }    car = new Car(maxGear);    car_object *obj = (car_object *)zend_object_store_get_object(object TSRMLS_CC);    obj->car = car;}

接着实现一下前面定义好的,但没有写内容的PHP_METHOD函数。为了演示的简洁,我们就实现两个函数。

PHP_METHOD(Car, p_accelerate){    Car *car;    car_object *obj = (car_object *)zend_object_store_get_object(        getThis() TSRMLS_CC);    car = obj->car;    if (car != NULL) {        car->accelerate();    }}PHP_METHOD(Car, p_getCurrentSpeed){    Car *car;    car_object *obj = (car_object *)zend_object_store_get_object(        getThis() TSRMLS_CC);    car = obj->car;    if (car != NULL) {        RETURN_LONG(car->getCurrentSpeed());    }    RETURN_NULL();}

记得添加所需要的头文件car.h。

然后重新 make & make install 吧,做完以后重启Apache服务器。

五. 在PHP脚本中调用扩展暴露出来的方法

编写一个PHP脚本,去测试是否成功。测试代码如下

1
2
3
4
5
6
7
<?php
// create a 5 gear car
$car = new Car(5);
print $car->p_getCurrentSpeed(); // prints '0'
$car->p_accelerate();
print $car->p_getCurrentSpeed(); // prints '5'
?>



运行结果如果是0 和 5就说明成功了。

自此一个简单的PHP调用C++类已经实现了。

六. 在扩展中调用C/C++写的so库

假设现在我们有一个冒泡排序的.so库文件,现在我们想在PHP扩展中调用这个库文件里的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extern "C" void bubble_sort(int *arr, int len)
{
    int tmp;

    for(int i = 0; i < len - 1; i++)
        for(int j = i + 1; j < len; j++)
        {
            if(arr > arr[j])
            {
                tmp = arr;
                arr = arr[j];
                arr[j] = tmp;
            }
        }
}



1. 更新car.h文件以及car.cc文件,添加调用so的代码,如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件

car.cc 中代码如下图所示。

C++ 和PHP这二个搞夫妻关系终有了这个结果了 b2b软件



作者: 发财猪    时间: 2017-6-22 10:00
很好,服务也很好,下次还会来的!
作者: jh134724    时间: 2017-6-22 18:53
了,很适合店铺装修的
作者: xiaoyaoyao001    时间: 2017-6-22 18:53
店主,速度很快,很满意,正好赶上我得活动,谢谢店主,这次活动赚钱了,再来设计,哈哈
作者: yirenzhuang    时间: 2017-6-22 20:09
绍朋友来的,有没有提成啊,哈哈
作者: lnzhanghao    时间: 2017-6-28 04:47
我是个凑数的。。。
作者: 乱世扶稣    时间: 2017-7-2 04:37
很好,值得信赖的店铺
作者: dongwuhua    时间: 2017-7-4 17:28
好非常耐心,技术员静静也很好,长期合作哦~
作者: 694012770    时间: 2017-7-5 23:07
平台做的很好,帮我申请号。。。我不懂都给我一一讲解,让我明白怎么操作,谢谢卖家。。谢谢客服和技术人员。。。
作者: 89260881    时间: 2017-7-8 05:07
卖家服务很好,客服和技术都很有耐心,慢慢摸熟悉了,感觉很不错,很让人放心的平台。
作者: 发财猪    时间: 2017-7-9 22:01
,有问必答,值得购买
作者: ctbvip    时间: 2017-7-15 01:26
推荐的店铺,客服超级nice,远程装修,全5分,棒棒滴!




欢迎光临 信息发布软件,b2b软件,广告发布软件 (http://postbbs.com/) Powered by Discuz! X3.2