ホームページ  >  記事  >  バックエンド開発  >  PHP の拡張機能と埋め込み -- C++ クラスの拡張機能開発_PHP チュートリアル

PHP の拡張機能と埋め込み -- C++ クラスの拡張機能開発_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-13 17:18:45938ブラウズ

今日、私はほぼ 1 日かけて PHP の関連する C++ 拡張機能を勉強しましたが、最初にそれらに触れたとき、多くの点で不慣れで、多くの落とし穴に遭遇しました。ここでは、主に次の記事を参照します。 //devzone .zend.com/1435/wrapping-c-classes-in-a-php-extension/:

これで、いくつかのメンバー関数を含む Car クラスが定義されました。拡張機能全体に含まれるファイルは次のとおりです。

    config.m4 拡張設定ファイル
  • php_vehicles.h 拡張ヘッダファイル
  • vehicles.cc 拡張ソースファイル
  • car.h クラスヘッダファイル
  • car.cc クラスソースファイル 次に、この拡張子の各部分をファイルの順序で個別に説明します。 設定ファイル:config.m4 れーれー
  • 6 行目では C++ コンパイラを使用する必要があります。 7 行目は、拡張機能がダイナミック リンク ライブラリの形式で表示されることを示します。 表の 8 行目では C++ ライブラリが追加されています。これは、string や std などが使用できることを意味します。 9 行目には、すべてのソース ファイルを必ず含めてください。 クラスヘッダーファイル car.h リーリー これは、C++ ヘッダー ファイルの宣言とまったく同じです。 car.ccクラスのソースファイル ソースファイルもC++のクラス定義です。 れーれー
    次に重要なポイントは次のとおりです。 PHP 拡張ヘッダー ファイル php_vehicles.h リーリー まず、マクロを使用してヘッダー ファイルがインクルードされているかどうかを確認し、4 行目に拡張子にエイリアスを付けます。5 行目にはバージョン番号が示されます。 11 行目から 13 行目は extern "C" に含まれていることに注意してください。これは、PHP が C で記述されているため、C++ 拡張機能を開発するときに宣言する必要があります。 15行目では拡張モジュール全体のエントリを宣言しています。このエントリ関数ではMINITRINITなどのスタートアップ関数とMSHUTDOWN RSHUTDOWNなどのシャットダウン関数を定義します。 PHP 拡張子 vehicle.cc のソース ファイル: このファイルには、必要な C++ クラスを PHP カーネルに接続する方法のタスクが含まれるため、非常に多くの内容が含まれています。同時に、クラス内のメンバー関数もこのファイル内で適切にマップする必要があります。便宜上、PHP は直接呼び出すことができます。これらの関数については、以下のソース コードで 1 つずつ説明します。 コードの最初の段階では、最初にクラス関連の部分については説明しませんが、ここでのコードはまず、従来の PHP 拡張機能のソース コードで実行する必要があるいくつかの操作を示します。 れーれー

    最初の行はヘッダー ファイルを示します。行 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 をチェックして、車両拡張機能が既に利用可能かどうかを確認します。
    • 毎回入力するのが面倒な場合は、単純にシェル スクリプトを記述してこれらのタスクを完了することもできます。 基本的な初期化が完了したら、PHP ユーザー空間と定義した C++ クラスの間の接続を検討します。コードのこの部分では、クラス内の関数を PHP ユーザー空間スクリプトに公開します。
      • まず、Car と同じ名前の php クラスを定義する必要があります。
      • 次に、このクラスのどのメソッドを php ユーザー空間に導入するかを説明するために、一連の zend_function_entry テーブルを定義する必要があります
      • 注意してください。 php ユーザーの場合 スペース内のメソッドは C++ クラスのメソッドと同じ名前である必要はありません。必要に応じて C++ クラスのメソッドを追加または削除することもできます。

      • vehicle.h を次のように変更します。 れーれー

        まず、zend_class_entry が 2 行目で定義されます。このエントリは、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) により、クラスを登録し、クラス テーブルにクラスを追加します。 31 行目から 51 行目は、前のモジュール エントリと変わりません。

        • C++ クラスのメンバー関数と同じ名前を持つ一連の php 関数が宣言されたので、次に行う必要があるのは、この 2 つを接続することです。 各 C++ クラス インスタンスは PHP クラス インスタンスに対応する必要があります。これを実現する 1 つの方法は、構造体を使用して既存の 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代码就可以进行测试了:
            <pre class="code">/ 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.


            如果说输出跟标识的一致的话,那么整个过程就成功了,恭喜!

            www.bkjia.comtruehttp://www.bkjia.com/PHPjc/621614.htmlTechArticle今天花了几乎一天的时间研究php的相关c扩展,第一次接触的时候很多地方不太熟悉,也碰到了不少坑,这里把整个过程叙述如下,参考的文...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。