Maison php教程 php手册 php的扩展和嵌入--c++类的扩展开发

php的扩展和嵌入--c++类的扩展开发

Jun 13, 2016 am 10:19 AM
Intégrer 扩展

今天花了几乎一天的时间研究php的相关c++扩展,第一次接触的时候很多地方不太熟悉,也碰到了不少坑,这里把整个过程叙述如下,参考的文章主要是http://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
     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
    Copier après la connexion

    第六行是要求使用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 */ 
    Copier après la connexion
    这个跟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 }
    Copier après la connexion

    接下来才是重点: 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 */
    Copier après la connexion
    首先用宏判断这个头文件是不是已经包含了.然后在第四行给这个扩展一个别名.第五行给定版本号. 注意在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
    Copier après la connexion


    第一行引入头文件.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.h
        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
        Copier après la connexion


        首先是在第二行定义了一个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  };
          Copier après la connexion



          这个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 }
          Copier après la connexion


          而后需要对模块初始化函数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}
           
          Copier après la connexion


          这里我们看到第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  }
            Copier après la connexion


            通过调用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();
            }
            Copier après la connexion



            好了,到现在为止基本上整个框架就搭好了。 不要忘了重新配置编译一下,然后用如下的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.
            Copier après la connexion


            如果说输出跟标识的一致的话,那么整个过程就成功了,恭喜!
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment utiliser l'extension SNMP de PHP ? Comment utiliser l'extension SNMP de PHP ? Jun 02, 2023 am 10:22 AM

L'extension SNMP pour PHP est une extension qui permet à PHP de communiquer avec les périphériques réseau via le protocole SNMP. En utilisant cette extension, vous pouvez facilement obtenir et modifier les informations de configuration des périphériques réseau, telles que le processeur, la mémoire, l'interface réseau et d'autres informations sur les routeurs, les commutateurs, etc. Vous pouvez également effectuer des opérations de contrôle telles que la commutation des ports des périphériques. Cet article présentera les connaissances de base du protocole SNMP, comment installer l'extension SNMP de PHP et comment utiliser l'extension SNMP en PHP pour surveiller et contrôler les périphériques réseau. 1. SN

Du début à la fin : Comment utiliser l'extension php cURL pour effectuer des requêtes HTTP Du début à la fin : Comment utiliser l'extension php cURL pour effectuer des requêtes HTTP Jul 29, 2023 pm 05:07 PM

Du début à la fin : Comment utiliser l'extension php cURL pour les requêtes HTTP Introduction : En développement web, il est souvent nécessaire de communiquer avec des API tierces ou d'autres serveurs distants. Utiliser cURL pour effectuer des requêtes HTTP est un moyen courant et puissant. Cet article expliquera comment utiliser PHP pour étendre cURL afin d'effectuer des requêtes HTTP et fournira quelques exemples de code pratiques. 1. Préparation Tout d'abord, assurez-vous que l'extension cURL est installée sur php. Vous pouvez exécuter php-m|grepcurl sur la ligne de commande pour vérifier

Combiner PHP avec HTML : trois techniques pour intégrer du code Combiner PHP avec HTML : trois techniques pour intégrer du code Mar 06, 2024 am 08:09 AM

La combinaison de PHP et HTML est une technologie courante dans le développement Web. PHP peut intégrer du contenu dynamique dans des fichiers HTML et implémenter des fonctions auxiliaires, ce qui améliore considérablement l'interactivité et la personnalisation du site Web. Cet article présentera trois techniques d'intégration de code et fournira des exemples de code spécifiques à titre de référence. 1. Utilisez des balises PHP pour intégrer du code La méthode la plus courante consiste à utiliser des balises PHP () pour intégrer du code PHP dans des fichiers HTML afin d'afficher du contenu dynamique. Par exemple, vous pouvez utiliser PHP

Extensions et modules tiers pour les fonctions PHP Extensions et modules tiers pour les fonctions PHP Apr 13, 2024 pm 02:12 PM

Pour étendre les fonctionnalités des fonctions PHP, vous pouvez utiliser des extensions et des modules tiers. Les extensions fournissent des fonctions et des classes supplémentaires qui peuvent être installées et activées via le gestionnaire de packages pecl. Les modules tiers fournissent des fonctionnalités spécifiques et peuvent être installés via le gestionnaire de packages Composer. Des exemples pratiques incluent l'utilisation d'extensions pour analyser des données JSON complexes et l'utilisation de modules pour valider les données.

Comment installer l'extension mbstring sous CENTOS7 ? Comment installer l'extension mbstring sous CENTOS7 ? Jan 06, 2024 pm 09:59 PM

1.UncaughtError:Calltoundefinedfunctionmb_strlen(); Lorsque l'erreur ci-dessus se produit, cela signifie que nous n'avons pas installé l'extension mbstring ; 2. Entrez dans le répertoire d'installation de PHP cd/temp001/php-7.1.0/ext/mbstring ; /usr/local/bin /phpize ou /usr/local/php7-abel001/bin/phpize) pour installer l'extension php 4../configure--with-php-config=/usr/local/php7-abel

Comment utiliser l'extension Aurora Push pour implémenter la fonction push de messages par lots dans les applications PHP Comment utiliser l'extension Aurora Push pour implémenter la fonction push de messages par lots dans les applications PHP Jul 25, 2023 pm 08:07 PM

Comment utiliser l'extension Aurora Push pour implémenter la fonction push de messages par lots dans les applications PHP. Dans le développement d'applications mobiles, le push de messages est une fonction très importante. Jiguang Push est un service push de messages couramment utilisé qui fournit des fonctions et des interfaces riches. Cet article explique comment utiliser l'extension Aurora Push pour implémenter la fonctionnalité push de messages par lots dans les applications PHP. Étape 1 : Enregistrez un compte Jiguang Push et obtenez une clé API. Tout d'abord, nous devons nous inscrire sur le site officiel de Jiguang Push (https://www.jiguang.cn/push).

Comment utiliser l'extension ZipArchive de PHP ? Comment utiliser l'extension ZipArchive de PHP ? Jun 02, 2023 am 08:13 AM

PHP est un langage côté serveur populaire qui peut être utilisé pour développer des applications Web et traiter des fichiers. L'extension ZipArchive pour PHP est un outil puissant pour manipuler les fichiers zip en PHP. Dans cet article, nous verrons comment utiliser l'extension ZipArchive de PHP pour créer, lire et modifier des fichiers zip. 1. Installez l'extension ZipArchive Avant d'utiliser l'extension ZipArchive, vous devez vous assurer que l'extension a été installée. La méthode d'installation est la suivante : 1. Installer

Comment utiliser l'extension POSIX de PHP ? Comment utiliser l'extension POSIX de PHP ? Jun 03, 2023 am 08:01 AM

Les extensions POSIX pour PHP sont un ensemble de fonctions et de constantes qui permettent à PHP d'interagir avec les systèmes d'exploitation compatibles POSIX. POSIX (PortableOperatingSystemInterface) est un ensemble de normes d'interface de système d'exploitation conçues pour permettre aux développeurs de logiciels d'écrire des applications pouvant s'exécuter sur divers systèmes d'exploitation UNIX ou de type UNIX. Cet article explique comment utiliser les extensions POSIX pour PHP, y compris l'installation et l'utilisation. 1. Installez l'extension POSIX de PHP dans

See all articles