Maison  >  Article  >  développement back-end  >  Génération automatique du framework d'extension pour l'extension php

Génération automatique du framework d'extension pour l'extension php

黄舟
黄舟original
2017-08-14 09:32:122002parcourir

Avant-propos

Article précédent : Un novice apprend l'extension PHP - bonjour tout le monde, quelle que soit la raison, il dit de force bonjour à l'extension PHP. Le framework généré automatiquement par ext_skel sera expliqué en détail dans cet article sous forme de mémo.

Texte

Utilisation de ext_skel

./ext_skel --extname=module [--proto=file] [--stubs=file] [--xml[=file]]
           [--skel=dir] [--full-xml] [--no-help]

  --extname=module   module is the name of your extension(模块名,会在当前目录创建一个该名称子目录)
  --proto=file       file contains prototypes of functions to create(函数原型定义文件)
  --stubs=file       generate only function stubs in file
  --xml              generate xml documentation to be added to phpdoc-cvs
  --skel=dir         path to the skeleton directory(设置骨架生成的目录,不设置该项则默认在ext/extname下)
  --full-xml         generate xml documentation for a self-contained extension
                     (not yet implemented)
  --no-help          don't try to be nice and create comments in the code                     
  and helper functions to test if the module compiled (生成的代码中不显示各种帮助注释)

PHP et processus liés aux extensions

1 Le démarrage et l'arrêt des programmes PHP sont conceptuellement différents là-bas. sont deux.

La première est que lorsque le module php est chargé, la fonction de démarrage du module est appelée par le moteur (PHP_MINIT_FUNCTION). Cela permet au moteur d'effectuer certaines initialisations telles que les types de ressources, l'enregistrement des variables INI, etc., et ces données résident en mémoire, correspondant à une terminaison (PHP_MSHUTDOWN_FUNCTION)

L'autre est le démarrage de la requête PHP, la requête Ne pas appeler la fonction de démarrage précédente (PHP_RINIT_FUNCTION), qui correspond à la fin une fois la requête terminée (PHP_RSHUTDOWN_FUNCTION)

2 Au démarrage de PHP, il commencera à traiter la méthode MINIT de toutes ses. extensions chargées. (Le nom complet est Module Initialization, qui est une fonction définie par chaque module.) (PHP_MINIT_FUNCTION), exécutez-le une fois. Pendant ce temps, l'extension peut définir certaines de ses propres constantes, classes, ressources, etc., tout cela sera utilisé par le script PHP côté utilisateur. Les éléments définis ici résideront en mémoire et pourront être utilisés par toutes les requêtes jusqu'à ce que le module PHP soit désactivé.

3. Lorsqu'une requête arrive, PHP ouvrira rapidement un nouvel environnement, réanalysera ses propres extensions, et parcourra et exécutera leurs méthodes RINIT respectives (nom complet Request Initialization) (PHP_RINIT_FUNCTION À ce moment, un). L'extension peut initialiser les variables qui seront utilisées dans cette requête, etc., et peut également initialiser les variables dans le client (c'est-à-dire le script PHP) ultérieurement, etc.

4. Lorsque la requête traverse le code métier et est exécutée jusqu'à la fin, PHP démarrera le programme de recyclage et exécutera la méthode RSHUTDOWN (nom complet Request Shutdown) (PHP_RSHUTDOWN_FUNCTION) de toutes les extensions chargées, en utilisant des variables. dans le noyau Faites quelque chose comme une table, et une fois l'exécution terminée, tout ce qui est utilisé dans cette requête sera libéré, y compris toutes les variables de la table des variables, toute la mémoire demandée dans cette requête, etc.

5 Après le traitement des requêtes est terminé, celles qui doivent être fermées sont également fermées et PHP entrera dans l'étape MSHUTDOWN (nom complet Module Shutdown) (PHP_MSHUTDOWN_FUNCTION). À ce moment, PHP lancera un ultimatum à toutes les extensions. a des souhaits non exaucés, laissez-le partir. Dans sa propre méthode MSHUTDOWN, c'est la dernière chance une fois que PHP aura fini d'exécuter le MSHUTDOWN étendu, il entrera dans le programme d'autodestruction. (Dernière chance d'effacer la mémoire non autorisée, sinon la mémoire fuira)

Résumé, le processus que je comprends :
PHP_MINIT_FUNCTION (exécuté une fois par processus)
|
Exécutez plusieurs PHP_RINIT_FUNCTION
|
Exécutez plusieurs PHP_RSHUTDOWN_FUNCTION
|
PHP_MSHUTDOWN_FUNCTION (exécuté une fois par processus)

Ci-joint les schémas de multi-threading et multi-processus
Génération automatique du framework dextension pour lextension php

Génération automatique du framework dextension pour lextension php

config.m4

dnl signifie commenter cette ligne, la même chose que // en php. Je n’étudierai pas pourquoi c’est DNL, ​​sachez juste que c’est une note.

dnl $Id$
dnl config.m4 for extension helloworld

dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.

dnl If your extension references something external, use with:##指定PHP模块的工作方式,动态编译选项,如果想通过.so的方式接入扩展,请去掉前面的dnl注释PHP_ARG_WITH(helloworld, for helloworld support,
Make sure that the comment is aligned:
[  --with-helloworld             Include helloworld support])

dnl Otherwise use enable:##指定PHP模块的工作方式,静态编译选项,如果想通过enable的方式来启用,去掉dnl注释PHP_ARG_ENABLE(helloworld, whether to enable helloworld support,
Make sure that the comment is aligned:
[  --enable-helloworld           Enable helloworld support])if test "$PHP_HELLOWORLD" != "no"; then
  dnl Write more examples of tests here...

  dnl # --with-helloworld -> check with-path
  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
  dnl SEARCH_FOR="/include/helloworld.h"  # you most likely want to change this
  dnl if test -r $PHP_HELLOWORLD/$SEARCH_FOR; then # path given as parameter
  dnl   HELLOWORLD_DIR=$PHP_HELLOWORLD
  dnl else # search default path list
  dnl   AC_MSG_CHECKING([for helloworld files in default path])
  dnl   for i in $SEARCH_PATH ; do
  dnl     if test -r $i/$SEARCH_FOR; then
  dnl       HELLOWORLD_DIR=$i
  dnl       AC_MSG_RESULT(found in $i)
  dnl     fi
  dnl   done
  dnl fi
  dnl 
  dnl if test -z "$HELLOWORLD_DIR"; then
  dnl   AC_MSG_RESULT([not found])
  dnl   AC_MSG_ERROR([Please reinstall the helloworld distribution])
  dnl fi

  dnl # --with-helloworld -> add include path
  dnl PHP_ADD_INCLUDE($HELLOWORLD_DIR/include)
  dnl # --with-helloworld -> check for lib and symbol presence
  dnl LIBNAME=helloworld # you may want to change this
  dnl LIBSYMBOL=helloworld # you most likely want to change this 

  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
  dnl [
  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $HELLOWORLD_DIR/$PHP_LIBDIR, HELLOWORLD_SHARED_LIBADD)
  dnl   AC_DEFINE(HAVE_HELLOWORLDLIB,1,[ ])
  dnl ],[
  dnl   AC_MSG_ERROR([wrong helloworld lib version or lib not found])
  dnl ],[
  dnl   -L$HELLOWORLD_DIR/$PHP_LIBDIR -lm
  dnl ])
  dnl  ##用于说明这个扩展编译成动态链接库的形式
  dnl PHP_SUBST(HELLOWORLD_SHARED_LIBADD)  ##用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开
  PHP_NEW_EXTENSION(helloworld, helloworld.c, $ext_shared)fi

php_helloworld.h

J'ai découvert que de nombreux manuels écrits il y a longtemps sur Internet ont des fonctions déclarées dans le fichier d'en-tête. Il semble que les versions plus récentes ne l'exigent pas. Parce que le framework généré par défaut ne voit pas de mots comme "PHP_FUNCTION(confirm_helloworld_compiled)" dans le fichier d'en-tête. Alors ne vous inquiétez pas trop pour ce fichier. (Mais c'est une bonne pratique de déclarer la fonction à implémenter dans le fichier d'en-tête)

Sachez que le numéro de version qui sera utilisé ci-dessous helloworld.c est défini ici

#define PHP_HELLOWORLD_VERSION "0.1.0"

helloworld.c

Structure du code

De nombreuses macros de PHP_XXX sont définies dans main/php.h

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

##包含头文件(引入所需要的宏、API定义等)
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_helloworld.h"

static int le_helloworld;

##PHP核心定义的一个宏,与ZEND_FUNCTION相同,用于定义扩展函数(这个函数是系统默认生成的,用于确认之用)
PHP_FUNCTION(confirm_helloworld_compiled)
{
    char *arg = NULL;
    int arg_len, len;
    char *strg;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
        return;
    }

    len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "helloworld", arg);
    RETURN_STRINGL(strg, len, 0);
}

##定义PHP中可以调用的函数
PHP_FUNCTION(helloworld) {
    php_printf("Hello World!\n");
    RETURN_TRUE;
}

##初始化module时运行
PHP_MINIT_FUNCTION(helloworld)
{
    /* If you have INI entries, uncomment these lines 
    REGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}

##当module被卸载时运行
PHP_MSHUTDOWN_FUNCTION(helloworld)
{
    /* uncomment this line if you have INI entries
    UNREGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}

##当一个REQUEST请求初始化时运行
PHP_RINIT_FUNCTION(helloworld)
{
    return SUCCESS;
}

##当一个REQUEST请求结束时运行
PHP_RSHUTDOWN_FUNCTION(helloworld)
{
    return SUCCESS;
}

##声明模块信息函数,即可以在phpinfo看到的信息
PHP_MINFO_FUNCTION(helloworld)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "helloworld support", "enabled");
    php_info_print_table_end();

    /* Remove comments if you have entries in php.ini
    DISPLAY_INI_ENTRIES();
    */
}

##声明(引入)Zend(PHP)函数块
const zend_function_entry helloworld_functions[] = {
    PHP_FE(confirm_helloworld_compiled, NULL)       /* For testing, remove later. */
    ##上一讲中就是在这里添加了自己定义的函数模块
    PHP_FE(helloworld,  NULL)       /*  */
    ##zend引擎认为结束的标记,老版本的是“{NULL,NULL,NULL}”,后面PHP源代码直接定义了个宏PHP_FE_END,这里就直接用这个了。虽然都一个意思但看过去爽多了
    ##如果遇到PHP_FE_END未定义undefine的问题,请见附录1
    PHP_FE_END  /* Must be the last line in helloworld_functions[] */
};

##声明 Zend模块,是不是感觉下面的模块名字很熟悉,对的,就是前文讲到的PHP流程中会用到的,现在懂了为什么要先讲流程了吧~
zend_module_entry helloworld_module_entry = {
    STANDARD_MODULE_HEADER,
    "helloworld",
    helloworld_functions,
    PHP_MINIT(helloworld),
    PHP_MSHUTDOWN(helloworld),
    PHP_RINIT(helloworld),      /* Replace with NULL if there's nothing to do at request start */
    PHP_RSHUTDOWN(helloworld),  /* Replace with NULL if there's nothing to do at request end */
    PHP_MINFO(helloworld),
    PHP_HELLOWORLD_VERSION,
    STANDARD_MODULE_PROPERTIES
};

##实现get_module()函数
#ifdef COMPILE_DL_HELLOWORLD
ZEND_GET_MODULE(helloworld)
#endif

Structure du module

1. fichiers (Introduisez les macros requises, les définitions d'API, etc.); le fichier d'en-tête que le module

doit inclure n'est qu'un seul php.h, qui se trouve dans le répertoire principal. Ce fichier contient diverses macros et définitions d'API nécessaires à la construction du module.

2. Déclarez les fonctions exportées (pour la déclaration des blocs de fonctions Zend) ;

La macro ZEND_FUNCTION déclare une nouvelle fonction C compilée à l'aide de l'API interne de Zend. Cette fonction C est de type void, prend INTERNAL_FUNCTION_PARAMETERS (c'est une autre macro) comme paramètres et le nom de la fonction est préfixé par zif_.
PHP_FUNCTION est le même que celui-ci et la macro a été définie dans /main/php.h

#define PHP_FUNCTION            ZEND_FUNCTION

3 Déclarez le bloc fonction Zend

Vous l'avez maintenant déclaré ; La fonction est exportée, mais Zend ne sait pas comment l'appeler, elle doit donc être importée dans Zend. L'introduction de ces fonctions se fait via un tableau contenant N structures zend_function_entry. Chaque élément du tableau correspond à une fonction visible de l'extérieur, et chaque élément contient le nom d'une fonction telle qu'elle apparaît en PHP et le nom défini dans le code C.

4. Déclarez le module Zend ;

Les informations du module Zend sont enregistrées dans une structure nommée zend_module_entry, qui contient toutes les informations du module qui doivent être fournies à Zend.

5. Implémentez la fonction get_module()

这个函数只用于动态可加载模块

6.实现导出函数。

实现想要扩展的函数,PHP_FUNCTION(helloworld)

ps:模块部分是学习这篇文章的,本来写好了,后面发现他写的比我好就借鉴了PHP扩展代码结构详解

附录

1.error: ‘PHP_FE_END’ undeclared here (not in a function)错误。

原因:是因为zend引擎版本太老了。
1、切换到php的源码目录,
2、执行下面两行

# sed -i 's|PHP_FE_END|{NULL,NULL,NULL}|' ./ext/**/*.c
# sed -i 's|ZEND_MOD_END|{NULL,NULL,NULL}|' ./ext/**/*.c

3.切换到mcrypt目录,如php-5.x.x/ext/mcrypt/。再次执行make命令,一切恢复正常。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
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