分享
 
 
 

Using C++ With PHP

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Before We Begin

Before we begin, I should note that the focus of this article will be on the UNIX side of the PHP world. I'll mention a few pointers for Windows development, but since I do most of my coding on UNIX systems, I'll be sticking to what I know for the most part.

I should also mention that these instructions should work with PHP 4.2.x and 4.3.x.

Now, here's a few conventions I'll use throughout this article...

$PHP_HOME will refer to the path to your PHP source code, i.e. where you unpacked your PHP source. On my system, it's /home/jay/setup/php/php-x.x.x.

The example module we'll be creating will be called cppext.

The Setup

Before you get to coding your extension for PHP in C++, you'll need to get yourself a basic extension skeleton. In the UNIX world, you'll want to run a shell script found in $PHP_HOME/ext called ext_skel. Switch over to the $PHP_HOME/ext directory and fire up the shell script. Use the --extname argument to give your extension a name.

jay@monty ~ $ cd setup/php/php-4.3.1/ext

jay@month ext $ ./ext_skel --extname cppext

On Windows systems, there's PHP script in $PHP_HOME/ext called ext_skel_win32.php in the current PHP CVS HEAD. It'll probably become a part of PHP 5 and may be included in future versions of the PHP 4.x branch. I have no idea if it works, but the daring may try it out...

You should now have a basic PHP extension ready to go in $PHP_HOME/ext/cppext. The only problem is, it's all done in C and not in C++.

Setting Up The config.m4 File

Now would be a good time to open up your extension's config.m4 file and get it ready for some C++ action. This section is geared towards PHP 4.2.x, but it should still work in PHP 4.3.x.

There isn't much you have to change here. Basically, you want to tell PHP's build system that it should be prepared to use C++ and link with the standard C++ library. Here's a sample file for our little cppext extension, minus all of the auto-generated comments:

PHP_ARG_ENABLE(cppext, for cppext support,

[ --enable-cppext Enable cppext support]

)

if test "$PHP_CPPEXT" != "no" ; then

PHP_ADD_LIBRARY(stdc++, 1, CPPEXT_SHARED_LIBADD)

PHP_EXTENSION(cppext, $ext_shared)

PHP_REQUIRE_CXX()

fi

In PHP 4.3.x, the config.m4 file can be set up a little differently, but the above should still work fine.

Setting Up The Makefile.in File

Now you'll need to set up the Makefile.in file. In PHP 4.3.x, this file won't exist, so you'll have to create it. The build system in PHP 4.3.0 got rid of this file, but it will still work.

In this file, you want to tell the build system which files it should compile using your C++ compiler. There's not much difference as compared to the stock Makefile.in that ext_skel creates in PHP 4.2.x...

LTLIBRARY_NAME = libcppext.la

LTLIBRARY_SOURCES_CPP = cppext.cpp

LTLIBRARY_SHARED_NAME = cppext.la

LTLIBRARY_OBJECTS_X = cppext.lo

include $(top_srcdir)/build/dynlib.mk

Setting Up The Source

With config.m4 and Makefile.in in place, you can start setting up your code. Rename cppext.c to a more Cish name. Following our example Makefile.in, rename it to cppext.cpp.

Now you can start editing the file. If you were to try and build the extension in it's current state, odds are it would compile without any errors as a shared object, but it probably would not work with PHP. If you compiled it statically into PHP itself, you'd probably get some linker errors. This is due to various differences in C (which is what PHP is written in, for the most part) and C (which is what your extension will be written in). Think name mangling and that sort of thing.

To remedy this, you'll need to tell your extension to treat a few PHP API functions as if they were written in C (which they are) rather than in C (which they aren't).

You'll need to wrap a few lines with BEGIN/END_EXTERN_C(). Specifically, you'll want your cppext.cpp to look like this:

extern "C" {

#include "php.h"

#include "php_ini.h"

#include "ext/standard/info.h"

}

.

.

.

#ifdef COMPILE_DL_CPPEXT

BEGIN_EXTERN_C()

ZEND_GET_MODULE(cppext)

END_EXTERN_C()

#endif

Normally, we'd want to wrap the header files above in BEGIN/END_EXTERN_C() as we did for ZEND_GET_MODULE(cppext), but since BEGIN/END_EXTERN_C() are defined in zend.h, extern "C" will have to do.

In Windows, you may find that Visual C complains about linking your extension. You may need to put something like this near the top of your extension:

#ifdef PHP_WIN32

#include <iostream>

#include <math.h>

#endif

That seems to help. You should obviously put that in there anyway to try and keep things cross-platform.

Compiling the Extension

Now you're ready to compile your extension. If you want to compile the extension statically (compile into PHP as a part of PHP itself), go down to the PHP root directory at $PHP_HOME, move the configure file out of the way and run buildconf.

Next, run configure with your normal set of configuration options and add --enable-cppext. Run make clean, make, make install and all that other jazz, like recompiling Apache and all that.

If you want to compile the extension as a loadable object, head to your extension's directory and run the phpize command. (This assumes that you have PEAR installed on your system.) phpize will create a configure script just for your little extension. Run configure, make and make install. If you want the extension to load automatically, you'll have to modify your php.ini file to load the proper file. Adding a line like extension=cppext.so should do the trick.

Your extension should now be compiled and ready to use by PHP. Try running the auto-generated cppext.php file found in your extension's source directory to see if everything is kosher.

Mapping C++ Classes To PHP

There are a ton of ways you can go about doing this, but here's a few tips to get you started.

Start of by making a global zend_class_entry for each class. You also need to have a list entry to handle the destructors for your actual C++ objects, which we'll actually be treating as PHP resources. (See Note 1 in the exmaple code.)

Put all of your methods into your function_entry array. For each class, make another function_entry for the methods you want to map. (See Note 2 in example code.)

Your MINIT function should initialize the class entries, list entries, etc. (See Note 3.)

In your PHP class constructor, you need to register a resource within the PHP class. This resource (handle in the example code) is used to reference the actual C++ object in method calls. (See Note 4.)

Map your PHP methods onto the C++ class methods. (See Note 5.)

That's pretty much it. Of course, there are a ton of ways to get the same effect, but this one's pretty simple and should be a decent guide to get you going.

Example Code

Here's some example code to get you going. This is pretty simple stuff -- we have a single C++ class that we map to PHP. The C++ class is simple and doesn't do much, but you should be able to figure out the harder stuff if you can understand this...

config.m4

dnl config.m4 for extension cppext

PHP_ARG_ENABLE(cppext, whether to enable cppext support,

[ --enable-cppext Enable cppext support])

if test "$PHP_CPPEXT" != "no"; then

PHP_ADD_LIBRARY(stdc++, 1, CPPEXT_SHARED_LIBADD)

PHP_NEW_EXTENSION(cppext, cppext.cpp class.cpp, $ext_shared)

PHP_REQUIRE_CXX()

fi

Makefile.in

LTLIBRARY_NAME = libcppext.la

LTLIBRARY_SOURCES_CPP = cppext.cpp

LTLIBRARY_SHARED_NAME = cppext.la

LTLIBRARY_SHARED_LIBADD = $(CPPEXT_SHARED_LIBADD)

LTLIBRARY_OBJECTS_X = cppext.lo class.lo

include $(top_srcdir)/build/dynlib.mk

class.h -- this is for our actual C++ class...

#ifndef __CLASS_H__

#define __CLASS_H__

#include <string>

using namespace std;

class MyPHPClass

{

private:

string itsString;

public:

MyPHPClass(string s = "default");

void setString(const string s);

string getString() const;

};

#endif

class.cpp -- the MyPHPClass implementation...

#include "class.h"

MyPHPClass::MyPHPClass(string s)

{

itsString = s;

}

void MyPHPClass::setString(const string s)

{

itsString = s;

}

string MyPHPClass::getString() const

{

return itsString;

}

php_cppext.h -- our PHP wrapper header. Be careful with putting actual C++ code in here, as the C compiler used to build PHP may choke on it during static builds.

#ifndef PHP_CPPEXT_H

#define PHP_CPPEXT_H

extern zend_module_entry cppext_module_entry;

#define phpext_cppext_ptr &cppext_module_entry

#ifdef PHP_WIN32

#define PHP_CPPEXT_API __declspec(dllexport)

#else

#define PHP_CPPEXT_API

#endif

#ifdef ZTS

#include "TSRM.h"

#endif

PHP_MINIT_FUNCTION(cppext);

PHP_FUNCTION(myphpclass);

PHP_FUNCTION(myphpclass_getstring);

PHP_FUNCTION(myphpclass_setstring);

static void destroy_myphpclass(zend_rsrc_list_entry *rsrc TSRMLS_DC);

#ifdef ZTS

#define CPPEXT_G(v) TSRMG(cppext_globals_id, zend_cppext_globals *, v)

#else

#define CPPEXT_G(v) (cppext_globals.v)

#endif

#endif/* PHP_CPPEXT_H */

cppext.cpp

#ifdef HAVE_CONFIG_H

#include "config.h"

#endif

#ifdef PHP_WIN32

#include <iostream>

#include <math.h>

#endif

extern "C" {

#include "php.h"

#include "php_ini.h"

#include "ext/standard/info.h"

}

#include "php_cppext.h"

#include "class.h"

/* Note 1 -- here's the zend_class_entry. You'll also

need a list entry for the resources used to store the

actual C++ objects... */

static zend_class_entry myphpclass_entry;

static int le_myphpclass;

/* Note 2 -- all of the methods go into this

function_entry. Below are the actual methods that

map onto MyPHPClass. */

function_entry cppext_functions[] = {

PHP_FE(myphpclass,NULL)

PHP_FE(myphpclass_getstring,NULL)

PHP_FE(myphpclass_setstring,NULL)

{NULL, NULL, NULL}

};

static function_entry myphpclass_methods[] = {

{"getstring",PHP_FN(myphpclass_getstring),NULL},

{"setstring",PHP_FN(myphpclass_setstring),NULL},

{NULL, NULL, NULL}

};

/* {{{ cppext_module_entry

*/

zend_module_entry cppext_module_entry = {

#if ZEND_MODULE_API_NO >= 20010901

STANDARD_MODULE_HEADER,

#endif

"cppext",

cppext_functions,

PHP_MINIT(cppext),

NULL,

NULL,

NULL,

NULL,

#if ZEND_MODULE_API_NO >= 20010901

"0.1",

#endif

STANDARD_MODULE_PROPERTIES

};

/* }}} */

#ifdef COMPILE_DL_CPPEXT

BEGIN_EXTERN_C()

ZEND_GET_MODULE(cppext)

END_EXTERN_C()

#endif

/* {{{ PHP_MINIT_FUNCTION

*/

PHP_MINIT_FUNCTION(cppext)

{

/* Note 3 -- here we register the destructor for the myphpclass PHP objects,

which deletes the actual C++ objects. Below that, we initialize the

class entry for myphpclass. */

le_myphpclass = zend_register_list_destructors_ex(destroy_myphpclass, NULL, "myphpclass-obj", module_number);

INIT_CLASS_ENTRY(myphpclass_entry, "myphpclass", myphpclass_methods);

zend_register_internal_class(&myphpclass_entry TSRMLS_CC);

return SUCCESS;

}

/* }}} */

/* {{{ proto myphpclass object myphpclass()

Returns a new myphpclass object. This is basically the constructor. */

PHP_FUNCTION(myphpclass)

{

/* Note 4 -- here's the constructor. First, initalize return_value as an

object, then make a handle to the C++ object. Register the handle as a resource

and store the resource within the PHP object so we can get to it in the method

calls. */

MyPHPClass *obj;

zval *handle;

if (ZEND_NUM_ARGS() != 0) {

WRONG_PARAM_COUNT;

}

obj = new MyPHPClass;

object_init_ex(return_value, &myphpclass_entry);

MAKE_STD_ZVAL(handle);

ZEND_REGISTER_RESOURCE(handle, obj, le_myphpclass);

zend_hash_update(Z_OBJPROP_P(return_value), "handle", sizeof("handle"), &handle, sizeof(zval *), NULL);

}

/* }}} */

/* Note 5 -- this macro gets the resource handle for the C++ object so we can

use the C++ object in our methods mappings below. */

#define CPP_GET_THIS() id = getThis(); if (!id) { php_error(E_ERROR, "this isn't an object?! %s()", get_active_function_name(TSRMLS_C)); } if (zend_hash_find(Z_OBJPROP_P(id), "handle", sizeof("handle"), (void **) &handle) == FAILURE) { php_error(E_ERROR, "underlying object missing, can't find 'this' in %s()", get_active_function_name(TSRMLS_C)); } obj = (MyPHPClass *) zend_list_find(Z_LVAL_PP(handle), &type); if (!obj || type != le_myphpclass) { php_error(E_ERROR, "underlying object is of wrong type in %s()", get_active_function_name(TSRMLS_C)); }

/* {{{ proto string myphpclass->getstring()

Passthru to MyPHPClass::getString(). */

PHP_FUNCTION(myphpclass_getstring)

{

MyPHPClass *obj;

zval *id, **handle;

int type;

CPP_GET_THIS();

RETURN_STRINGL((char*) obj->getString().c_str(), obj->getString().length(), 1);

}

/* }}} */

/* {{{ proto bool myphpclass->setstring(string)

Passthru to MyPHPClass::setString(string). */

PHP_FUNCTION(myphpclass_setstring)

{

MyPHPClass *obj;

zval *id, **handle;

int type, len;

char *str;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {

RETURN_FALSE;

}

else {

CPP_GET_THIS();

obj->setString(str);

RETURN_TRUE;

}

}

/* }}} */

/* {{{ destructor for myphpclass objects */

static void destroy_myphpclass(zend_rsrc_list_entry *rsrc TSRMLS_DC)

{

delete (MyPHPClass*) rsrc->ptr;

}

/* }}} */

Where To Go From Here

Now that you have a working PHP extension written in C++, you can start using all of C++'s features with PHP. Start making some classes, or wrapping PHP extensions around C++ libraries. The possibilities are finite!

For a more advanced example and various C++ tricks like classes, pure abstract virtual stuff and all of that nonsense, you can review the source for a PHP extension I wrote in C++ called cryptopp-php, which can be found at http://www.tutorbuddy.com/software/. There's plenty in there to see and perhaps get some ideas from.

Have fun with your C++ and PHP, you OOP nut.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有