日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php定义一个名为Vehicles,php的扩展和嵌入--c++类的扩展开发

發布時間:2024/9/19 php 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php定义一个名为Vehicles,php的扩展和嵌入--c++类的扩展开发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

配置文件:config.m4

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

第六行是要求使用c++的編譯器

第七行表示擴展會以動態連接庫的形式出現

第八行表是增加了c++的庫,也就是類似與string和std這種都可以用了.

第九行里面注意要把所有的源文件都包括進來.

類的頭文件car.h

#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 }

接下來才是重點:

php擴展的頭文件php_vehicles.h

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

第一行引入頭文件.2~5行定義了模塊的入口函數,這里先不進行任何操作.這里一般可以初始化模塊的全局變量.

第6~21行通過zend_module_entry給出了擴展和Zend引擎之間的聯系. 在這里因為只有MINT函數被定義了,所以其他位置都是NULL.

第22~24行則是常規項目,在擴展中都要加上,注意這里為了C++做了extern "C"的特殊處理.

在完成了這一步之后,已經可以進行一次擴展編譯了,可以驗證一下自己之前的程序有沒有錯誤.過程如下,之后就不重復了.

phpize

./configure --enable-vehicles

make

sudo make install

在php.ini中加入extension=vehicles.so(只需要一次)

重啟apache,如果是服務的話 sudo /etc/init.d/httpd restart

然后在info.php中查看是否已經有了vehicles這一項擴展.

如果覺得每次打都很麻煩,也可以簡單的寫一個shell腳本來完成這些工作.

在完成了基本的初始化之后,就要開始考慮php用戶空間與我們定義的C++類之間的聯系了.這部分代碼是為了把類中的函數都暴露給php的用戶空間腳本,

首先需要定義一個名字同樣是Car的php類,

然后還要定義一組zend_function_entry表,用來說明這個類中有哪些方法想要引入到php用戶空間中.

需要注意的是,在php用戶空間中的方法不一定要跟c++類中的方法同名,你同時還可以根據自己的需要增加或刪減c++類中的方法.這點非常的自由.

按照如下的代碼更改vehicles.cc

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

首先是在第二行定義了一個zend_class_entry,這個入口會在MINIT的時候進行相應的初始化.

3~13行給出了C++類的成員函數所轉換成的php方法的版本,之后會添加上相應的實現.

15~23行定義了函數入口zend_function_entry,php方法定義的地方.這里也可以聲明一組自己定義的別名.(如何定義,怎么體現?)

24~30給出的是新的模塊初始化MINIT函數:

INIT_CLASS_ENTRY函數把類的入口和之前在zend_function_entry中類的方法聯系了起來,屬于類的初始化

而car_ce = zend_register_internal_class(&ce TSRMLS_CC) ,注冊類,把類加入到Class Table中,

31~51行跟之前的模塊入口沒什么差別。

現在已經聲明了一組跟C++類成員函數同名的php函數,再接下來需要做的就是把兩者聯系起來:

每個C++的類實例都必須對應一個php的類實例,一種實現的方法是使用一個結構來追蹤現有的C++和php的類實例。而為了做到這一點,就需要寫出自己的對象處理器,在php5中,一個對象就是由一個句柄(使得Zend引擎能夠定位你的類的id)、一個函數表、和一組處理器組成的。在對象的聲明周期的不同階段都可以重寫處理器以實現不同的功能。所以在之前的代碼基礎上,先增加一個對象處理器:

1 #include "car.h"

2 zend_object_handlers car_object_handlers;

3 struct car_object {

4 zend_object std;

5 Car *car;

6 };

這個car_object結構會被用來追蹤C++的實例,然后與zend_object聯系起來。在PHP_METHOD的聲明之前,需要加上下面兩個方法:

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 }

而后需要對模塊初始化函數MINIT進行如下修改:

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}

這里我們看到第30行,首先調用car_create_handler創建一個create_object處理器。在調用car_create_handler的時候注意一個大坑那就是第18行這里$type->default_properties,php的新版本中這里是properties_info,這個編譯錯誤在對比源碼了之后才知道怎么改。

13~15行給一個car_object申請了空間,并完成了初始化。同時把obj對應的zend_class_object和輸入的type()進行了連接,也就是把MINIT中初始化的zend對象綁定在了car_object這個結構中。

在完成了綁定之后,16~19繼續進行拷貝過程。

20~21行把obj加入到了zend的對象中,并用函數指針的方式定義了銷毀時候的函數car_free_storage,同時產生了一個對象obj的句柄

第31行則給處理器handlers了相應的zend_object_handlers的值(這里還很不明晰)

現在在php的類構造函數中,就要讀取用戶的參數,并且把它們傳遞給C++的構造函數。一旦C++的類實例被創建了,那就可以從zend對象store中抓取car_object指針,然后設定結構體中的car值。這樣的話,就把zend對象實例和C++的Car實例綁定了起來。

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 }

通過調用zend_object_store_get_object函數就能夠獲得C++類的一個實例。而下面的兩個函數也是同樣的道理:

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();

}

好了,到現在為止基本上整個框架就搭好了。

不要忘了重新配置編譯一下,然后用如下的php代碼就可以進行測試了:

/ 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.

如果說輸出跟標識的一致的話,那么整個過程就成功了,恭喜!

原文鏈接:原文

總結

以上是生活随笔為你收集整理的php定义一个名为Vehicles,php的扩展和嵌入--c++类的扩展开发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。