Home  >  Article  >  Backend Development  >  Automatic generation of extension framework for php extension

Automatic generation of extension framework for php extension

黄舟
黄舟Original
2017-08-14 09:32:121875browse

Preface

Previous article: Novice learns PHP extension: hello world. Regardless of the reason, he forcibly says hello with PHP extension. The framework automatically generated by ext_skel will be explained in detail in this article as a memo.

Text

Usage of 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 and extension-related processes

1. The startup and termination of the PHP program are conceptually separate. of.

One is when the php module is loaded, the module startup function is called by the engine (PHP_MINIT_FUNCTION). This causes the engine to do some initialization such as resource types, registering INI variables, etc., and these data are resident in memory, corresponding to a termination (PHP_MSHUTDOWN_FUNCTION)

The other is when the PHP request starts, the request Don't call the previous startup function (PHP_RINIT_FUNCTION), which corresponds to the termination after the request is completed (PHP_RSHUTDOWN_FUNCTION)

2. As PHP starts, it will start to process the MINIT method of all its loaded extensions. (The full name is Module Initialization, which is a function defined by each module.) (PHP_MINIT_FUNCTION), execute it once. During this time, the extension can define some of its own constants, classes, resources, etc., all of which will be used by the user-side PHP script. s things. The stuff defined here will be resident in memory and can be used by all requests until the PHP module is turned off.

3. When a request comes, PHP will quickly open up a new environment, rescan its own extensions, and traverse and execute their respective RINIT methods (full name Request Initialization) (PHP_RINIT_FUNCTION). At this time, a The extension may initialize variables that will be used in this request, etc., and may also initialize variables in the client (i.e. PHP script) later, etc.

4. When the request passes through the business code and is executed to the end, PHP will start the recycling program and execute the RSHUTDOWN (full name Request Shutdown) (PHP_RSHUTDOWN_FUNCTION) method of all loaded extensions, using variables in the kernel Once the execution is completed, everything used in this request will be released, including all variables in the variable table, all memory requested in this request, etc.

5 After the request processing is completed, the ones that should be closed are also closed, and PHP will enter the MSHUTDOWN (full name Module Shutdown) (PHP_MSHUTDOWN_FUNCTION) stage. At this time, PHP will issue an ultimatum to all extensions. If any extension still has unfulfilled wishes, let it go. In its own MSHUTDOWN method, this is the last chance. Once PHP finishes executing the extended MSHUTDOWN, it will enter the self-destruct program. (The last chance to clear the memory applied for without authorization, otherwise the memory will leak)

Summary, the process I understand:
PHP_MINIT_FUNCTION (executed once per process)
|
Execute many PHP_RINIT_FUNCTION
|
Execute many PHP_RSHUTDOWN_FUNCTION
|
PHP_MSHUTDOWN_FUNCTION (executed once per process)

Attached are the diagrams of multi-threading and multi-process
Automatic generation of extension framework for php extension

Automatic generation of extension framework for php extension

##config.m4

dnl means commenting out this line, the same as // in php. I won’t study why it is DNL, ​​just know that it is a 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

I found that many textbooks written long ago on the Internet have functions declared in the header file. It seems that newer versions do not require it. Because the framework generated by default does not see words like "PHP_FUNCTION(confirm_helloworld_compiled)" in the header file. So don't worry too much about this file. (But it is a good practice to declare the function to be implemented in the header file)

Know that the version number that will be used below helloworld.c is defined here

#define PHP_HELLOWORLD_VERSION "0.1.0"

helloworld.c

Code structure

Many macros of PHP_XXX are defined in 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

Module structure

1. Include the header file (introduce the required macros , API definition, etc.);

The header file that must be included in the module is only one php.h, which is located in the main directory. This file contains various macros and API definitions necessary to build the module.

2. Declare exported functions (for declaration of Zend function blocks);

ZEND_FUNCTION macro declares a new C function compiled using Zend internal API. This C function is of type void, takes INTERNAL_FUNCTION_PARAMETERS (this is another macro) as parameters, and the function name is prefixed with zif_.

PHP_FUNCTION is the same as this. The macro has been defined in /main/php.h

#define PHP_FUNCTION            ZEND_FUNCTION

3. Declare the Zend function block;

Now you have declared the exported function , but Zend doesn't know how to call it, so it must be introduced into Zend. The introduction of these functions is completed through an array containing N zend_function_entry structures. Each item in the array corresponds to an externally visible function, and each item contains the name of a function as it appears in PHP and the name defined in C code.

4. Declare Zend module;

Zend module information is saved in a structure called zend_module_entry, which contains all module information that needs to be provided to Zend.

5. Implement the get_module() function;

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

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命令,一切恢复正常。

The above is the detailed content of Automatic generation of extension framework for php extension. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn