I am new in this area of writing extension for PHP, however I need to create a wrapper class for C++ to PHP. I am currently using PHP 5.2.13. I read this article http://devzone.zend.com/article/4486-Wrapping-C-Classes-in-a-PHP-Extension, a tutorial on how I could proceed to wrap C++ class to communicate with PHP Zend however it is written to for linux system. Do you guys have any article or advice on how I could proceed to write a wrapper class to communicate with PHP?
问题:
回答1:
This is exactly what I've been doing lately. The tutorial you referenced is a good one (it was my starting point as well). Here's the basic process I've followed for wrapping my classes. Say you're wrapping your C++ class named Myclass
:
Create php_myclass.h:
#ifndef PHP_MYCLASS_H #define PHP_MYCLASS_H extern "C" { #include "php.h" } // Include your C++ class definition #include "Myclass.h" // Here is the struct which will represent the PHP version of your Myclass. // It simply includes a pointer to a Myclass and a zend_object for PHP to struct myclass_object { zend_object std; Myclass *myclass; }; // Here is whatever your PHP class is going to be called in the userspace (the PHP code) #define PHP_MYCLASS_CLASSNAME "Myclass" extern zend_class_entry *myclass_ce; extern zend_object_handlers myclass_object_handlers; zend_object_value myclass_create_handler(zend_class_entry *type TSRMLS_DC); // Later, this will be the array full of your Myclass's method declarations extern function_entry php_myclass_functions[]; #endif /* PHP_MYCLASS_H */
Then define your php class in php_myclass.cpp:
#include "php_myclass.h" zend_class_entry *myclass_ce; zend_object_handlers myclass_object_handlers; // I'm still a newb, but I think this is the function that handles memory management when // the PHP class is deleted (goes out of scope, script ends, whatever) void myclass_free_storage(void *object TSRMLS_DC) { myclass_object *obj = (myclass_object*)object; delete obj->myclass; zend_hash_destroy(obj->std.properties); FREE_HASHTABLE(obj->std.properties); efree(obj); } // And likewise I believe this handles, as the name implies, mem management // when your Myclass is instantiated. zend_object_value myclass_create_handler(zend_class_entry *type TSRMLS_DC) { zval *tmp; zend_object_value retval; // make room in memory for a new PHP Myclass object: myclass_object *obj = (myclass_object*)emalloc(sizeof(myclass_object)); // fill that memory with 0s memset(obj, 0, sizeof(myclass_object)); obj->std.ce = type; // some magic stuff (no idea) 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*)); // make it so you can get an instance of this object in later code retval.handle = zend_objects_store_put(obj, NULL, myclass_free_storage, NULL TSRMLS_CC); retval.handlers = &myclass_object_handlers; return retval; } // First, we define some argument info for methods that take arguments (if we have any) // This one means, obviously, one argument: ZEND_BEGIN_ARG_INFO_EX(php_myclass_one_arg, 0, 0, 1) ZEND_END_ARG_INFO() // This one two args, etc. ZEND_BEGIN_ARG_INFO_EX(php_myclass_two_args, 0, 0, 2) ZEND_END_ARG_INFO() // Here's where you tell PHP what methods your Myclass PHP class has. function_entry php_myclass_functions[] = { // A special property at the end of this line for the constructor: PHP_ME(Myclass,__construct,NULL,ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) // Normal methods look like this: PHP_ME(Myclass,methodNameNoArgs,NULL,ZEND_ACC_PUBLIC) PHP_ME(Myclass,methodName1Arg,php_myclass_one_arg,ZEND_ACC_PUBLIC) PHP_ME(Myclass,methodName2Args,php_myclass_two_args,ZEND_ACC_PUBLIC) // Three magic NULL values, no idea why they have to go here. { NULL, NULL, NULL } }; // And now, define each of those Myclass methods you just instructed PHP // to expose to the userspace: PHP_METHOD(Myclass, __construct) { Myclass *myclass = NULL; zval *object = getThis(); // Create an instance of the class you're wrapping myclass = new Myclass(); // Make object (which points to $this for your PHP object instance) // an instance of the struct that represents your php class myclass_object *obj = (myclass_object*)zend_object_store_get_object(object TSRMLS_CC); // Set the internal Myclass of this to the instance of Myclass you just made obj->myclass = myclass; // Done. } PHP_METHOD(Myclass, methodNameNoArgs) { // Get the current instance of your PHP Myclass into myclass: Myclass *myclass; myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\ myclass = mo->myclass; if (obj == NULL) { // error checking RETURN_NULL(); } // Return the value of your myclass method using one of the RETURN_* macros // Here we'll pretend this one returns boolean: RETURN_BOOL(myclass->methodNameNoArgs()); } PHP_METHOD(Myclass, methodName1Arg) { // Now, let's pretend your Myclass::methodName1Arg(int) takes an int // and returns a std::vector (which you want to be an array) long param; // Get the current instance of your PHP Myclass into myclass: Myclass *myclass; myclass_object *mo = (myclass_object*)zend_object_store_get_object(getThis() TSRMLS_CC);\ myclass = mo->myclass; if (obj == NULL) { // error checking RETURN_NULL(); } // Here's how you parse parameters of your PHP method call. // The second parameter is "l" for long int. Read the tutorials online for more // on how to use this function. if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", ¶m) == FAILURE) { RETURN_NULL(); } // Get the real return value you want to translate for PHP std::vector retval = myclass->methodName1Arg(param); // Use the magic "return_value" (which is in every method behind-the-scenes) // and initialize it to be a PHP array: array_init(return_value); // Loop through the vector and build the array: for (std::vector::iterator i = retval.begin(); i != retval.end(); ++i) { add_next_index_long(return_value, *i); } // done. return_value is always returned for you. } PHP_METHOD(Myclass, methodName2Args) { // "Left as an exercise for the reader" is coder slang for // "I *really* don't feel like typing anymore." :) }
I hope this example code compiles, or at least helps. :) It was sort of hastily put together from real working code I have here, and if the find/replaces broke something, at least you might get an idea of what to do. There's plenty that's left out of this, read Sara Golemon's three-part extensions tutorial on http://devzone.zend.com/article/1021 for more. Good luck.
回答2:
The best "documentation" you'll find is the source code of PHP and is extensions (sorry). You'll find you have to dig into the sources (especially the headers of the Zend engine) as soon as you're doing something non-trivial.
Having said this, there are a few resources you might find useful to get you started. See these articles and Extending and Embedding PHP by Sara Golemon. See also pecl.php.net/support.php