The example module we'll be creating will be calledCppext.
The setupBefore 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/EXTCalledExt_skel. Switch over to$ Php_home/EXTDirectory and fire up the shell script. Use-- ExtnameArgument 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/EXTCalledExt_skel_win32.phpIn the current php cvs head. it'll probably become a part of PHP 5 and may be written in future versions of the PHP 4.x branch. I have no idea if it works, but the daring may try it out...
You shoshould 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 FileNow wocould be a good time to open up your extension'sConfig. M4File and get it ready for some C ++ action. This section is geared towards PHP 4.2.x, But it shoshould 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 shoshould be prepared to use C ++ and link with the standard C ++ library. here's a sample file for our littleCppextExtension, 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,Config. M4File can be set up a little differently, but the above shoshould still work fine.
Setting up the makefile. In FileNow you'll need to set upMakefile. InFile. 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 shoshould compile using your c ++ compiler. There's not much difference as compared to the StockMakefile. InThatExt_skelCreates 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 sourceWithConfig. M4AndMakefile. InIn place, you can start setting up your code. RenameCppext. cTo a more CISH name. Following our exampleMakefile. In, Rename itCppext. 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 wocould compile without any errors as a shared object, but it probably wocould not work with PHP. if you compiled it statically into PHP itself, you 'd probably get some linker errors. this is due to varous 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 linesBegin/end_extern_c (). Specifically, you'll want yourCppext. cppTo 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 inBegin/end_extern_c ()As we didZend_get_module (cppext), But sinceBegin/end_extern_c ()Are defined inZend. 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 shocould obviusly put that in there anyway to try and keep things cross-platform.
Compiling the extensionNow 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$ Php_home, MoveConfigureFile out of the way and runBuildconf.
Next, runConfigureWith your normal set of configuration options and add-- Enable-cppext. RunMake clean,Make,Make installAnd 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 runPhpizeCommand. (This assumes that you have pear installed on your system .)PhpizeWill createConfigureScript just for your little extension. RunConfigure,MakeAndMake install. If you want the extension to load automatically, you'll have to modify yourPHP. iniFile to load the proper file. Adding a line likeExtension = cppext. SoShocould do the trick.
Your extension shocould now be compiled and ready to use by PHP. Try running the auto-generatedCppext. phpFile found in your extension's source directory to see if everything is kosher.
Mapping C ++ classes to PhPThere 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 globalZend_class_entryFor 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 yourFunction_entryArray. For each class, make anotherFunction_entryFor the methods you want to map. (See Note 2 in example code .)
YourMinitFunction shocould 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 (HandleIn the example code) is used to reference the actual C ++ object in method CILS. (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 shocould be a decent guide to get you going.
Example codeHere'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; } /* }}} */ |