今天花了幾乎一天的時間研究php的相關c++擴展,第一次接觸的時候很多地方不太熟悉,也碰到了不少坑,這裡把整個過程敘述如下,參考的文章主要是http://devzone.zend.com/1435/wrapping-c-classes-in-a-php-extension/:
現在定義了一個Car類,它有一些成員函數,整個擴展包括的文件如下:
1 PHP_ARG_ENABLE(vehicles, 2 [Whether to enable the "vehicles" extension], 3 [ --enable-vehicles Enable "vehicles" extension support]) 4 5 if test $PHP_VEHICLES != "no"; then 6 PHP_REQUIRE_CXX() 7 PHP_SUBST(VEHICLES_SHARED_LIBADD) 8 PHP_ADD_LIBRARY(stdc++, 1, VEHICLES_SHARED_LIBADD) 9 PHP_NEW_EXTENSION(vehicles, vehicles.cc car.cc, $ext_shared) 10 fi
#ifndef VEHICLES_CAR_H 2 #define VEHICLES_CAR_H 3 4 // A very simple car class 5 class Car { 6 public: 7 Car(int maxGear); 8 void shift(int gear); 9 void accelerate(); 10 void brake(); 11 int getCurrentSpeed(); 12 int getCurrentGear(); 13 private: 14 int maxGear; 15 int currentGear; 16 int speed; 17 }; 18 19 #endif /* VEHICLES_CAR_H */這個跟c++的頭文件聲明是完全一樣的. 類的源文件car.cc 源文件也是,屬於C++的類定義
2 #include "car.h" 3 Car::Car(int maxGear) { 4 this->maxGear = maxGear; 5 this->currentGear = 1; 6 this->speed = 0; 7 } 9 void Car::shift(int gear) { 10 if (gear < 1 || gear > maxGear) { 11 return; 12 } 13 currentGear = gear; 14 } 16 void Car::accelerate() { 17 speed += (5 * this->getCurrentGear()); 18 } 20 void Car::brake() { 21 speed -= (5 * this->getCurrentGear()); 22 } 24 int Car::getCurrentSpeed() { 25 return speed; 26 }
1 #ifndef PHP_VEHICLES_H 2 #define PHP_VEHICLES_H 4 #define PHP_VEHICLES_EXTNAME "vehicles" 5 #define PHP_VEHICLES_EXTVER "0.1" 7 #ifdef HAVE_CONFIG_H 8 #include "config.h" 9 #endif 10 11 extern "C" { 12 #include "php.h" 13 } 14 15 extern zend_module_entry vehicles_module_entry; 16 #define phpext_vehicles_ptr &vehicles_module_entry; 17 18 #endif /* PHP_VEHICLES_H */首先用宏判斷這個頭文件是不是已經包含了.然後在第四行給這個擴展一個別名.第五行給定版本號. 注意在11到13行用extern "C"包含了起來,這是因為php是用c寫的,所以在開發c++擴展的時候一定要聲明一下. 第15行聲明了整個擴展模塊的入口,在這個入口函數中會定義諸如MINIT\RINIT這種startup函數 和 MSHUTDOWN RSHUTDOWN這種shutdown函數. php擴展的源文件vehicles.cc: 這個文件裡面的內容相當多,因為它承載了如何把我們想要的c++的類與php的內核聯系起來的任務.同時在這個文件中還需要把類中的成員函數進行相應的mapping,以方便php可以直接調用.這些功能會在下面的源碼中一一加以說明: 在第一階段的代碼裡,先不涉及類相關的部分,而是循序漸進,這裡的代碼先給出常規php擴展源碼中需要進行的一些操作:
1 #include "php_vehicles.h" 2 PHP_MINIT_FUNCTION(vehicles) 3 { 4 return SUCCESS; 5 } 6 zend_module_entry vehicles_module_entry = { 7 #if ZEND_MODULE_API_NO >= 20010901 8 STANDARD_MODULE_HEADER, 9 #endif 10 PHP_VEHICLES_EXTNAME, 11 NULL, /* Functions */ 12 PHP_MINIT(vehicles), 13 NULL, /* MSHUTDOWN */ 14 NULL, /* RINIT */ 15 NULL, /* RSHUTDOWN */ 16 NULL, /* MINFO */ 17 #if ZEND_MODULE_API_NO >= 20010901 18 PHP_VEHICLES_EXTVER, 19 #endif 20 STANDARD_MODULE_PROPERTIES 21 }; 22 #ifdef COMPILE_DL_VEHICLES 23 extern "C" { 24 ZEND_GET_MODULE(vehicles) 25} 26 #endif
1 #include "php_vehicles.h" 2 zend_class_entry *car_ce; 3 PHP_METHOD(Car, __construct){} 5 PHP_METHOD(Car, shift) {} 7 PHP_METHOD(Car, accelerate) {} 9 PHP_METHOD(Car, brake) {} 11 PHP_METHOD(Car, getCurrentSpeed){} 13 PHP_METHOD(Car, getCurrentGear){} 15 zend_function_entry car_methods[] = { 16 PHP_ME(Car, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 17 PHP_ME(Car, shift, NULL, ZEND_ACC_PUBLIC) 18 PHP_ME(Car, accelerate, NULL, ZEND_ACC_PUBLIC) 19 PHP_ME(Car, brake, NULL, ZEND_ACC_PUBLIC) 20 PHP_ME(Car, getCurrentSpeed, NULL, ZEND_ACC_PUBLIC) 21 PHP_ME(Car, getCurrentGear, NULL, ZEND_ACC_PUBLIC) 22 {NULL, NULL, NULL} 23 }; 24 PHP_MINIT_FUNCTION(vehicles) 25 { 26 zend_class_entry ce; 27 INIT_CLASS_ENTRY(ce, "Car", car_methods); 28 car_ce = zend_register_internal_class(&ce TSRMLS_CC); 29 return SUCCESS; 30 } 31 zend_module_entry vehicles_module_entry = { 32 #if ZEND_MODULE_API_NO >= 20010901 33 STANDARD_MODULE_HEADER, 34 #endif 35 PHP_VEHICLES_EXTNAME, 36 NULL, /* Functions */ 37 PHP_MINIT(vehicles), /* MINIT */ 38 NULL, /* MSHUTDOWN */ 39 NULL, /* RINIT */ 40 NULL, /* RSHUTDOWN */ 41 NULL, /* MINFO */ 42 #if ZEND_MODULE_API_NO >= 20010901 43 PHP_VEHICLES_EXTVER, 44 #endif 45 STANDARD_MODULE_PROPERTIES 46 }; 47 #ifdef COMPILE_DL_VEHICLES 48 extern "C" { 49 ZEND_GET_MODULE(vehicles) 50 } 51 #endif
1 #include "car.h" 2 zend_object_handlers car_object_handlers; 3 struct car_object { 4 zend_object std; 5 Car *car; 6 };
1 void car_free_storage(void *object TSRMLS_DC) 2 { 3 car_object *obj = (car_object *)object; 4 delete obj->car; 5 zend_hash_destroy(obj->std.properties); 6 FREE_HASHTABLE(obj->std.properties); 7 efree(obj); 8 } 9 zend_object_value car_create_handler(zend_class_entry *type TSRMLS_DC) 10 { 11 zval *tmp; 12 zend_object_value retval; 13 car_object *obj = (car_object *)emalloc(sizeof(car_object)); 14 memset(obj, 0, sizeof(car_object)); 15 obj->std.ce = type; 16 ALLOC_HASHTABLE(obj->std.properties); 17 zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); 18 zend_hash_copy(obj->std.properties, &type->default_properties, 19 (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); 20 retval.handle = zend_objects_store_put(obj, NULL, 21 car_free_storage, NULL TSRMLS_CC); 22 retval.handlers = &car_object_handlers; 23 return retval; 24 }
25 PHP_MINIT_FUNCTION(vehicles) 26 { 27 zend_class_entry ce; 28 INIT_CLASS_ENTRY(ce, "Car", car_methods); 29 car_ce = zend_register_internal_class(&ce TSRMLS_CC); 30 car_ce->create_object = car_create_handler; 31 memcpy(&car_object_handlers, 32 zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 33 car_object_handlers.clone_obj = NULL; 34 return SUCCESS; 35}
1 PHP_METHOD(Car, __construct) 2 { 3 long maxGear; 4 Car *car = NULL; 5 zval *object = getThis(); 6 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &maxGear) == FAILURE) { 7 RETURN_NULL(); 8 } 9 car = new Car(maxGear); 10 car_object *obj = (car_object *)zend_object_store_get_object(object TSRMLS_CC); 11 obj->car = car; 12 }
PHP_METHOD(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(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(); }
如果說輸出跟標識的一致的話,那麼整個過程就成功了,恭喜!/ create a 5 gear car $car = new Car(5); print $car->getCurrentSpeed(); // prints '0' $car->accelerate(); print $car->getCurrentSpeed(); // prints '5' If you can run this script, congratulations, you’ve just created a PHP extension that wraps a C++ class.