zoukankan      html  css  js  c++  java
  • [php-src]理解Php内核中的函数与INI

    内容均以php-5.6.14为例.

    一. 函数结构

    内核中定义一个php函数使用 PHP_FUNCTION 宏 包装,扩展也不例外,该宏在 ./main/php.h:343

    有着一系列类似以 PHP 命名的 Zend 宏包装器,它们是:

    /* PHP-named Zend macro wrappers */
    /* 以PHP命名的Zend宏包装器 */
    
    #define PHP_FN                  ZEND_FN
    #define PHP_MN                  ZEND_MN
    #define PHP_NAMED_FUNCTION      ZEND_NAMED_FUNCTION
    #define PHP_FUNCTION            ZEND_FUNCTION             /* PHP_FUNCTION 就是 ZEND_FUNCTION */
    #define PHP_METHOD              ZEND_METHOD
    
    #define PHP_RAW_NAMED_FE ZEND_RAW_NAMED_FE
    #define PHP_NAMED_FE    ZEND_NAMED_FE
    #define PHP_FE          ZEND_FE
    #define PHP_DEP_FE      ZEND_DEP_FE
    #define PHP_FALIAS      ZEND_FALIAS
    #define PHP_DEP_FALIAS  ZEND_DEP_FALIAS
    #define PHP_ME          ZEND_ME
    #define PHP_MALIAS      ZEND_MALIAS
    #define PHP_ABSTRACT_ME ZEND_ABSTRACT_ME
    #define PHP_ME_MAPPING  ZEND_ME_MAPPING
    #define PHP_FE_END      ZEND_FE_END
    
    #define PHP_MODULE_STARTUP_N    ZEND_MODULE_STARTUP_N
    #define PHP_MODULE_SHUTDOWN_N   ZEND_MODULE_SHUTDOWN_N
    #define PHP_MODULE_ACTIVATE_N   ZEND_MODULE_ACTIVATE_N
    #define PHP_MODULE_DEACTIVATE_N ZEND_MODULE_DEACTIVATE_N
    #define PHP_MODULE_INFO_N       ZEND_MODULE_INFO_N
    
    #define PHP_MODULE_STARTUP_D    ZEND_MODULE_STARTUP_D
    #define PHP_MODULE_SHUTDOWN_D   ZEND_MODULE_SHUTDOWN_D
    #define PHP_MODULE_ACTIVATE_D   ZEND_MODULE_ACTIVATE_D
    #define PHP_MODULE_DEACTIVATE_D ZEND_MODULE_DEACTIVATE_D
    #define PHP_MODULE_INFO_D       ZEND_MODULE_INFO_D
    
    /* Compatibility macros */
    /* 兼容性宏 */ 
    #define PHP_MINIT       ZEND_MODULE_STARTUP_N
    #define PHP_MSHUTDOWN   ZEND_MODULE_SHUTDOWN_N
    #define PHP_RINIT       ZEND_MODULE_ACTIVATE_N
    #define PHP_RSHUTDOWN   ZEND_MODULE_DEACTIVATE_N
    #define PHP_MINFO       ZEND_MODULE_INFO_N
    #define PHP_GINIT       ZEND_GINIT
    #define PHP_GSHUTDOWN   ZEND_GSHUTDOWN
    
    #define PHP_MINIT_FUNCTION      ZEND_MODULE_STARTUP_D      /* 可用来定义模块初始时执行一些操作 */
    #define PHP_MSHUTDOWN_FUNCTION  ZEND_MODULE_SHUTDOWN_D      /* 可用来定义模块卸载时执行一些操作 */
    #define PHP_RINIT_FUNCTION      ZEND_MODULE_ACTIVATE_D      /* 可用来定义请求初始化时执行一些操作 */
    #define PHP_RSHUTDOWN_FUNCTION  ZEND_MODULE_DEACTIVATE_D     /* 可用来定义请求结束时执行一些操作 */
    #define PHP_MINFO_FUNCTION      ZEND_MODULE_INFO_D        /* 用来定义模块的信息,如phpinfo中的 */
    #define PHP_GINIT_FUNCTION      ZEND_GINIT_FUNCTION        /* 用来初始化全局变量 */
    #define PHP_GSHUTDOWN_FUNCTION  ZEND_GSHUTDOWN_FUNCTION
    
    #define PHP_MODULE_GLOBALS      ZEND_MODULE_GLOBALS

     ZEND_FUNCTION 在 ./Zend/zend_API.h:68

    #define ZEND_FN(name) zif_##name
    #define ZEND_MN(name) zim_##name
    #define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
    #define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
    #define ZEND_METHOD(classname, name)    ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))

    函数参数 INTERNAL_FUNCTION_PARAMETERS 在 ./Zend/zend.h:290

    #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
    #define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC

    也就是说,使用 PHP_FUNCTION(abc) 定义一个abc函数,预处理结果是 zif_abc(INTERNAL_FUNCTION_PARAMETERS),下面做个试验:

    /* 1.c */
    #include <stdio.h>
    
    #define ZEND_FN(name) zif_##name
    #define ZEND_MN(name) zim_##name
    #define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
    #define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
    #define ZEND_METHOD(classname, name)    ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
    
    #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC
    
    int main()
    {
        ZEND_FUNCTION(abc); 
    
        return 0;
    }

    下次看到 ZEND_FN() 就可以想到是 zend function name,看到 ZEND_MN() 就能想到 zend method name.

    使用 `gcc -E -o 1.i 1.c` 生成预处理文件 1.i ,`tail 1.i` 看文件最后的结果,内核中一个函数的形式如下:

    int main()
    {
        void zif_abc(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC);
    
        return 0;
    }

    PHP_MINIT_FUNCTION 和 ZEND_MINIT_FUNCTION 的效果是一样的,看下面:

    ./Zend/zend_API.h:126, 748

    ....
    /* Name macros */
    #define ZEND_MODULE_STARTUP_N(module)       zm_startup_##module
    #define ZEND_MODULE_SHUTDOWN_N(module)      zm_shutdown_##module
    #define ZEND_MODULE_ACTIVATE_N(module)      zm_activate_##module
    #define ZEND_MODULE_DEACTIVATE_N(module)    zm_deactivate_##module
    #define ZEND_MODULE_POST_ZEND_DEACTIVATE_N(module)  zm_post_zend_deactivate_##module
    #define ZEND_MODULE_INFO_N(module)          zm_info_##module
    #define ZEND_MODULE_GLOBALS_CTOR_N(module)  zm_globals_ctor_##module
    #define ZEND_MODULE_GLOBALS_DTOR_N(module)  zm_globals_dtor_##module
    
    /* Declaration macros */
    #define ZEND_MODULE_STARTUP_D(module)       int ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS)
    #define ZEND_MODULE_SHUTDOWN_D(module)      int ZEND_MODULE_SHUTDOWN_N(module)(SHUTDOWN_FUNC_ARGS)
    #define ZEND_MODULE_ACTIVATE_D(module)      int ZEND_MODULE_ACTIVATE_N(module)(INIT_FUNC_ARGS)
    #define ZEND_MODULE_DEACTIVATE_D(module)    int ZEND_MODULE_DEACTIVATE_N(module)(SHUTDOWN_FUNC_ARGS)
    #define ZEND_MODULE_POST_ZEND_DEACTIVATE_D(module)  int ZEND_MODULE_POST_ZEND_DEACTIVATE_N(module)(void)
    #define ZEND_MODULE_INFO_D(module)          void ZEND_MODULE_INFO_N(module)(ZEND_MODULE_INFO_FUNC_ARGS)
    #define ZEND_MODULE_GLOBALS_CTOR_D(module)  void ZEND_MODULE_GLOBALS_CTOR_N(module)(zend_##module##_globals *module##_globals TSRMLS_DC)
    #define ZEND_MODULE_GLOBALS_DTOR_D(module)  void ZEND_MODULE_GLOBALS_DTOR_N(module)(zend_##module##_globals *module##_globals TSRMLS_DC)
    ....
    ....
    /* For compatibility */
    #define ZEND_MINIT          ZEND_MODULE_STARTUP_N
    #define ZEND_MSHUTDOWN      ZEND_MODULE_SHUTDOWN_N
    #define ZEND_RINIT          ZEND_MODULE_ACTIVATE_N
    #define ZEND_RSHUTDOWN      ZEND_MODULE_DEACTIVATE_N
    #define ZEND_MINFO          ZEND_MODULE_INFO_N
    #define ZEND_GINIT(module)      ((void (*)(void* TSRMLS_DC))(ZEND_MODULE_GLOBALS_CTOR_N(module)))
    #define ZEND_GSHUTDOWN(module)  ((void (*)(void* TSRMLS_DC))(ZEND_MODULE_GLOBALS_DTOR_N(module)))
    
    #define ZEND_MINIT_FUNCTION         ZEND_MODULE_STARTUP_D
    #define ZEND_MSHUTDOWN_FUNCTION     ZEND_MODULE_SHUTDOWN_D
    #define ZEND_RINIT_FUNCTION         ZEND_MODULE_ACTIVATE_D
    #define ZEND_RSHUTDOWN_FUNCTION     ZEND_MODULE_DEACTIVATE_D
    #define ZEND_MINFO_FUNCTION         ZEND_MODULE_INFO_D
    #define ZEND_GINIT_FUNCTION         ZEND_MODULE_GLOBALS_CTOR_D
    #define ZEND_GSHUTDOWN_FUNCTION     ZEND_MODULE_GLOBALS_DTOR_D
    ....

    所以如果我们要定义自己的启动函数原型,可以定义 ZEND_MINIT_FUNCTION 的兼容性宏,多文件时传入其它模块。

    #define MY_STARTUP_FUNCTION(module)    ZEND_MINIT_FUNCTION(my_##module) 

    这个 MY_STARTUP_FUNCTION 作用和 ZEND_MINIT_FUNCTION 一样,在这里是用在多文件中定义启动其它主文件模块的函数。

    或者,直接定义成最终形式:

    #define MY_STARTUP_FUNCTION(module)    ZEND_MODULE_STARTUP_N(my_##module)(INIT_FUNC_ARGS)

    这两种都是原型的定义。

    自定义 启动/卸载 函数调用的宏:

    #define MY_STARTUP(module)    ZEND_MODULE_STARTUP_N(my_##module)(INIT_FUNC_ARGS_PASSTHRU)

    #define MY_SHUTDOWN(module)    ZEND_MODULE_SHUTDOWN_N(my_##module)(INIT_FUNC_ARGS_PASSTHRU)

    这个 MY_STARTUP 是用在主模块的初始化函数中启动附加组件的。从参数可以看出,它是直接调用。

    或者,采用高层宏的形式:

    #define MY_STARTUP(module)    ZEND_MINIT(my_##module)(INIT_FUNC_ARGS_PASSTHRU) 

    模块操作的参数能说明一些问题:

    ./Zend/zend_modules.h:30

    ....
    #define INIT_FUNC_ARGS      int type, int module_number TSRMLS_DC
    #define INIT_FUNC_ARGS_PASSTHRU type, module_number TSRMLS_CC
    #define SHUTDOWN_FUNC_ARGS  int type, int module_number TSRMLS_DC
    #define SHUTDOWN_FUNC_ARGS_PASSTHRU type, module_number TSRMLS_CC
    #define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module TSRMLS_DC
    #define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module TSRMLS_CC
    ....

    上面 zif_abc 函数参数中还有一个看起来奇怪的东西 TSRMLS_DC,线程安全资源管理的宏;

    ./TSRM/TSRM.h:166

    #ifdef ZTS
    
    ..........
    
    #define TSRMLS_FETCH()          void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
    #define TSRMLS_FETCH_FROM_CTX(ctx)  void ***tsrm_ls = (void ***) ctx
    #define TSRMLS_SET_CTX(ctx)     ctx = (void ***) tsrm_ls
    #define TSRMG(id, type, element)    (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
    #define TSRMLS_D    void ***tsrm_ls
    #define TSRMLS_DC   , TSRMLS_D
    #define TSRMLS_C    tsrm_ls
    #define TSRMLS_CC   , TSRMLS_C
    
    #else    /* non ZTS */
    
    #define TSRMLS_FETCH()
    #define TSRMLS_FETCH_FROM_CTX(ctx)
    #define TSRMLS_SET_CTX(ctx)
    #define TSRMLS_D    void
    #define TSRMLS_DC
    #define TSRMLS_C
    #define TSRMLS_CC
    
    #endif    /* ZTS */

    TSRMLS_* 系列总共有四个,其展开意思是 thread safe resource manager local stroage;

    在不启用zend线程安全的时候,它们并没有内容可以替换,ZTS 就是 zend thread safe;

    TSRMLS_DC 和 TSRMLS_CC 对应比 TSRMLS_D 和 TSRMLS_C 多加了一个逗号,

    TSRMLS_DC 就是 , TSRMLS_D,也就是 , void ***tsrm_ls ;它在函数定义中使用,D有define的意思.

    TSRMLS_CC就是 , TSRMLS_C,也就是 , tsrm_ls ;它在函数调用中使用,C有call的意思.

    这里有篇关于TSRM的靠谱文章:揭秘TSRM(Introspecting TSRM)

    二. 参数

    int ht,            用户实际传入的参数数目,ZEND_NUM_ARGS() 获取.

    zval *return_value,      指向由return_value填充的PHP变量的指针,(所指向的变量值)返回给函数调用者,默认类型是 IS_NULL.

    zval **return_value_ptr,  当返回PHP的引用,设置为指向变量的指针.(需要引用形式的返回值时使用)

    zval *this_ptr,         如果此函数是类的方法调用,相当于 $this 对象.

    int return_value_used,     用户调用此函数有没有用到函数返回值,用到就是1.

    在 [php-src]窥探Php内核中的变量 中讲到了设置变量的值,ZVAL_*() 系列函数,第一个参数就是 return_value,如果我们不用 RETURN_*() 系列函数主动返回某个值的话,同样可以用 ZVAL_*() ,效果是一样的。

    long t = 10;
    RETURN_LONG(t); // 同 RETVAL_LONG(t);return; 也相当于 ZVAL_LONG(return_value, t);return;

    内核中修改传递的参数:

    {
        zval *arg;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
            RETURN_NULL();
        }   
    
        // PHP5.4 参数不再支持引用方式传递,所以不需要判断 arg->is_ref__gc
        convert_to_string(arg);
        ZVAL_STRING(arg, "abc", 1); 
    
        return;
    }
    <?php
    $a = "AAA";
    test($a);
    // allow_call_time_pass_reference 已从5.4中移除,所以下面的用法是非法的,只能在函数定义时使用,如:function test(&$var)
    // test(&$a); // PHP Fatal error:  Call-time pass-by-reference has been removed
    // 参考:http://php.net/manual/zh/ini.core.php
    echo $a; // abc

    知道了上面的原理,我们直接对参数值进行修改,别忘了函数原型中提醒用户会更改原值。

    三. zend_API.h 中的宏函数

    array_init() 在 ./Zend/zend_API.h:363

    #define array_init(arg)         _array_init((arg), 0 ZEND_FILE_LINE_CC)
    #define array_init_size(arg, size) _array_init((arg), (size) ZEND_FILE_LINE_CC)
    #define object_init(arg)        _object_init((arg) ZEND_FILE_LINE_CC TSRMLS_CC)
    #define object_init_ex(arg, ce) _object_init_ex((arg), (ce) ZEND_FILE_LINE_CC TSRMLS_CC)
    #define object_and_properties_init(arg, ce, properties) _object_and_properties_init((arg),  (ce), (properties) ZEND_FILE_LINE_CC TSRMLS_CC)
    
    ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC);

    _array_init() 的实现在 ./Zend/zend_API.c:1009

    /* Argument parsing API -- andrei */
    ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */
    {
        ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg));
    
        _zend_hash_init(Z_ARRVAL_P(arg), size, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
        Z_TYPE_P(arg) = IS_ARRAY;
        return SUCCESS;
    }
    /* }}} */

    ZEND_FILE_LINE_DC 在 zend.h 第217行:

    #if ZEND_DEBUG
    #define ZEND_FILE_LINE_D                const char *__zend_filename, const uint __zend_lineno
    #define ZEND_FILE_LINE_DC               , ZEND_FILE_LINE_D
    #define ZEND_FILE_LINE_ORIG_D           const char *__zend_orig_filename, const uint __zend_orig_lineno
    #define ZEND_FILE_LINE_ORIG_DC          , ZEND_FILE_LINE_ORIG_D
    #define ZEND_FILE_LINE_RELAY_C          __zend_filename, __zend_lineno
    #define ZEND_FILE_LINE_RELAY_CC         , ZEND_FILE_LINE_RELAY_C
    #define ZEND_FILE_LINE_C                __FILE__, __LINE__
    #define ZEND_FILE_LINE_CC               , ZEND_FILE_LINE_C
    #define ZEND_FILE_LINE_EMPTY_C          NULL, 0
    #define ZEND_FILE_LINE_EMPTY_CC         , ZEND_FILE_LINE_EMPTY_C
    #define ZEND_FILE_LINE_ORIG_RELAY_C     __zend_orig_filename, __zend_orig_lineno
    #define ZEND_FILE_LINE_ORIG_RELAY_CC    , ZEND_FILE_LINE_ORIG_RELAY_C
    #define ZEND_ASSERT(c)                  assert(c)
    #else
    #define ZEND_FILE_LINE_D
    #define ZEND_FILE_LINE_DC
    #define ZEND_FILE_LINE_ORIG_D
    #define ZEND_FILE_LINE_ORIG_DC
    #define ZEND_FILE_LINE_RELAY_C
    #define ZEND_FILE_LINE_RELAY_CC
    #define ZEND_FILE_LINE_C
    #define ZEND_FILE_LINE_CC
    #define ZEND_FILE_LINE_EMPTY_C
    #define ZEND_FILE_LINE_EMPTY_CC
    #define ZEND_FILE_LINE_ORIG_RELAY_C
    #define ZEND_FILE_LINE_ORIG_RELAY_CC
    #define ZEND_ASSERT(c)
    #endif  /* ZEND_DEBUG */

    上面 __file_name 就是函数一个形参名,没特殊含义,用两个下划线有非常局部(私有)的感觉。

    API函数的存在,能让我们更方便在内核中完成某项功能。

    看到这里,你或许对函数原型和函数前面的 ZEND_API 感到困惑,这是什么,定义它们的文件有:

    ./configure.in:22

    ./Zend/configure.in:16

    ./main/php_config.h.in:8

    ./main/php_config.h:6

    ./Zend/zend_config.win32.h:78

    configure.in 和 ./Zend/configure.in 由 autoconf 处理,并生成配置文件,它们都含下面这段:

    AH_TOP([
    #if defined(__GNUC__) && __GNUC__ >= 4
    # define ZEND_API __attribute__ ((visibility("default")))
    # define ZEND_DLEXPORT __attribute__ ((visibility("default")))
    #else
    # define ZEND_API
    # define ZEND_DLEXPORT
    #endif
    
    #define ZEND_DLIMPORT
    
    #undef uint
    #undef ulong
    
    /* Define if you want to enable memory limit support */
    #define MEMORY_LIMIT 0
    ])

    __attribute__ ((visibility("default"))) 用于设置可见性,default使符号在所有情况下都被输出. 参考:控制符的可见性

    ps:

      __GNUC__ 是gcc编译器编译代码时预定义的一个宏。需要针对gcc编写代码时, 可以使用该宏进行条件编译。

      __GNUC__ 的值表示gcc的版本。需要针对gcc特定版本编写代码时,也可以使用该宏进行条件编译。

      __GNUC__ 的类型是“int”,该宏被扩展后, 得到的是整数字面值。可以通过仅预处理,查看宏扩展后的文本。

    ./Zend/zend_config.win32.h

    #ifdef LIBZEND_EXPORTS
    #   define ZEND_API __declspec(dllexport)
    #else
    #   define ZEND_API __declspec(dllimport)
    #endif

    ./configure.in  ->生成  ./main/php_config.h.in  ->生成  ./main/php_config.h

    和 ZEND_API 效果一样的有 PHPAPI,

    ./main/php.h:60:# define PHPAPI __attribute__ ((visibility("default")))

    四. 错误输出及运行时信息

     1). 错误输出函数

     
    定义php错误级别的宏 ./Zend/zend_errors.h
    #ifndef ZEND_ERRORS_H
    #define ZEND_ERRORS_H
    
    #define E_ERROR             (1<<0L)
    #define E_WARNING           (1<<1L)
    #define E_PARSE             (1<<2L)
    #define E_NOTICE            (1<<3L)
    #define E_CORE_ERROR        (1<<4L)
    #define E_CORE_WARNING      (1<<5L)
    #define E_COMPILE_ERROR     (1<<6L)
    #define E_COMPILE_WARNING   (1<<7L)
    #define E_USER_ERROR        (1<<8L)
    #define E_USER_WARNING      (1<<9L)
    #define E_USER_NOTICE       (1<<10L)
    #define E_STRICT            (1<<11L)
    #define E_RECOVERABLE_ERROR (1<<12L)
    #define E_DEPRECATED        (1<<13L)
    #define E_USER_DEPRECATED   (1<<14L)
    
    #define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | 
    E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE |
    E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT) #define E_CORE (E_CORE_ERROR | E_CORE_WARNING) #endif /* ZEND_ERRORS_H */

    定义错误输出函数的宏 ./main/php.h:292

    #define php_error zend_error
    #define error_handling_t zend_error_handling_t
    
    BEGIN_EXTERN_C()
    static inline ZEND_ATTRIBUTE_DEPRECATED void php_set_error_handling(error_handling_t error_handling, zend_class_entry *exception_class TSRMLS_DC)
    {
        zend_replace_error_handling(error_handling, exception_class, NULL TSRMLS_CC);
    }
    static inline ZEND_ATTRIBUTE_DEPRECATED void php_std_error_handling() {}
    
    PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) PHP_ATTRIBUTE_FORMAT(printf, 4, 0);
    
    #ifdef ZTS
    #define PHP_ATTR_FMT_OFFSET 1
    #else
    #define PHP_ATTR_FMT_OFFSET 0
    #endif
    
    /* PHPAPI void php_error(int type, const char *format, ...); */
    PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...)
        PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 3, PHP_ATTR_FMT_OFFSET + 4); 
    PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const      char *format, ...)
        PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 4, PHP_ATTR_FMT_OFFSET + 5);
    PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2,  int type, const char *format, ...)
        PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 5, PHP_ATTR_FMT_OFFSET + 6);
    #ifdef PHP_WIN32
    PHPAPI void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2         TSRMLS_DC);
    #endif
    END_EXTERN_C()
    
    #define php_error_docref php_error_docref0

    内核中很多地方出现 BEGIN_EXTERN_C() 和 END_EXTERN_C() 将程序包起来的情况,它们实际上是下面这种东西,你一看便知:

    #ifdef __cplusplus
    #define BEGIN_EXTERN_C() extern "C" {
    #define END_EXTERN_C() }
    #else
    #define BEGIN_EXTERN_C()
    #define END_EXTERN_C()
    #endif

    如果定义了__cplusplus,就把C程序用 extern "C" { } 包起来当C++处理,用于指定编译和链接规则,实现C和C++的混合编程. 参考:C++项目中的extern "C" {}

    zend_error 原型在 ./Zend/zend.h:711,实现在 ./Zend/zend.c:1031

    ZEND_API void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);

    php_error_docref0 实现在 ./main.c:947

    /* {{{ php_error_docref0 */
    /* See: CODING_STANDARDS for details. */
    PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) 
    {
        va_list args;
    
        va_start(args, format);
        php_verror(docref, "", type, format, args TSRMLS_CC);
        va_end(args);
    }
    /* }}} */

    php_error 即 zend_error,php_error_docref 即 php_error_docref0

    php_error 与 php_error_docref 的使用基本相同,参数位置不一样、效果略有差异;

    由于 php_error 只是 zend_error 的别名,我们应该尽量使用 zend_error.

    如:php_error_docref(NULL TSRMLS_CC, E_WARNING, "has been disabled for security reasons");

    将输出:Warning: testing(): has been disabled for security reasons in /home/www/1.php on line 3

    如:zend_error(E_WARNING, "has been disabled for security reasons");

    将输出:Warning: has been disabled for security reasons in /home/www/1.php on line 3

     2). 获得运行时函数信息的函数

    get_active_function_name(TSRMLS_C)    查看当前执行的函数名.
    zend_get_executed_filename(TSRMLS_C)  查看当前执行的文件名.
    zend_get_executed_lineno(TSRMLS_C)    查看当前执行到哪一行代码.

    ./Zend/zend_execute.h:353

    ZEND_API const char *get_active_function_name(TSRMLS_D);

    ./Zend/zend_execute_API.c:366

    ZEND_API const char *get_active_function_name(TSRMLS_D) /* {{{ */
    {
        if (!zend_is_executing(TSRMLS_C)) {
            return NULL;
        }    
        switch (EG(current_execute_data)->function_state.function->type) {
            case ZEND_USER_FUNCTION: {
                    const char *function_name = ((zend_op_array *) EG(current_execute_data)-             >function_state.function)->function_name;
    
                    if (function_name) {
                        return function_name;
                    } else {
                        return "main";
                    }
                }
                break;
            case ZEND_INTERNAL_FUNCTION:
                return ((zend_internal_function *) EG(current_execute_data)->function_state.function)-   >function_name;
                break;
            default:
                return NULL;
        }    
    }
    /* }}} */

     3). 示例

    zend_error( E_WARNING, 
      "%s() has been disabled for security reasons; current file is %s; current line is %i",
      get_active_function_name(TSRMLS_C),
      zend_get_executed_filename(TSRMLS_C),
      zend_get_executed_lineno(TSRMLS_C) );
    
    zend_error(E_WARNING, "this is a warning message
    ");

    给内核传递函数参数信息的宏:(用 zend_parse_parameters 解析参数时,可以不使用 arginfo 指定参数类型 )

    ./Zend/zend_API.h:108

    #define ZEND_ARG_INFO(pass_by_ref, name)       { #name, sizeof(#name)-1, NULL, 0, 0,   pass_by_ref, 0, 0 },
    ....
    
    #define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)  
        static const zend_arg_info name[] = {                                                                       
            { NULL, 0, NULL, required_num_args, 0, return_reference, 0, 0 },
    #define ZEND_BEGIN_ARG_INFO(name, _unused)  
        ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
    #define ZEND_END_ARG_INFO()     };

    【写一个color扩展的welcome函数】

    定义函数并添加为可用:

    /* {{{ arginfo */
    ZEND_BEGIN_ARG_INFO_EX(arginfo_welcome, 0, 0, 0)
        ZEND_ARG_INFO(0, arg)
    ZEND_END_ARG_INFO()
    /* }}} */
    
    /* {{{ 自定义welcome */
    PHP_FUNCTION(welcome)
    {
        php_printf("It works, Welcome!
    ");
    }
    /* }}} */
    
    /* {{{ color_functions[]
     *  
     * Every user visible function must have an entry in color_functions[]. 可用的函数都加到里面
     */
    const zend_function_entry color_functions[] = {
        PHP_FE(confirm_color_compiled,  NULL)       /* For testing, remove later. */
        PHP_FE(welcome, arginfo_welcome)
        PHP_FALIAS(wel, welcome, arginfo_welcome)  /* alias */
        PHP_FE_END  /* Must be the last line in color_functions[] */
    };  
    /* }}} */

    make && sudo make install

    运行 php5.6.14 -r 'welcome();' 就会输出:It works, Welcome!

    注意:如果你本机装了多个php,一不小心打成了 php -r 'welcome();',那么你得到的永远都是undefined function

    为扩展增加phpinfo信息:

    /* {{{ PHP_MINFO_FUNCTION
     */
    PHP_MINFO_FUNCTION(color)
    {
        php_info_print_table_start(); // 开始表格, 无参数
        // php_info_print_table_header(2, "Color Support", "enabled"); // 表格头,第一个参数是列数,后面是可变参数,第一个参数是几后面就是几个
    php_info_print_table_row(2, "Color Support", "enabled");
    php_info_print_table_row(2, "author", "weiChen");       // 表格行,与header头仅颜色不一样,同样第一个参数是列数,后面是列的内容
    php_info_print_table_row(2, "dependencies", "No"); php_info_print_table_end(); // 结束表格, 无参数 DISPLAY_INI_ENTRIES(); // 开启此行向 phpinfo 中显示INI配置 } /* }}} */

    注意模块信息的修改除了重新编译安装,还需要重启Php使之生效.

      

    增加可供查看的 INI 配置项:

      1). 模块初始阶段引入 结束阶段去除

    /* {{{ PHP_MINIT_FUNCTION
     */
    PHP_MINIT_FUNCTION(color)
    {
        REGISTER_INI_ENTRIES();
        return SUCCESS;
    }
    /* }}} */
    
    /* {{{ PHP_MSHUTDOWN_FUNCTION
     */
    PHP_MSHUTDOWN_FUNCTION(color)
    {
        UNREGISTER_INI_ENTRIES();
        return SUCCESS;
    }
    /* }}} */

      2). 具体配置项

    static PHP_INI_MH(onChangeTest)  // 定义回调函数onChangeTest
    {
        php_printf("ini entry has changed to %s
    ", new_value);
        return SUCCESS;
    }
    
    /* {{{ PHP_INI
     */
    PHP_INI_BEGIN() // 开始
    

    // 1.配置项名称, 2.初始值, 3.权限(PHP_INI_PERDIR、PHP_INI_SYSTEM、PHP_INI_USER、PHP_INI_ALL), 4.改变这个值时的回调函数
    // PHP_INI_PERDIR | PHP_INI_SYSTEM 指令只允许在php.ini或httpd.conf等配置中修改,PHP_INI_USER 只能在用户脚本中修改 如ini_set().
    // 具体配置项 PHP_INI_ENTRY("color.test", "very good", PHP_INI_ALL, onChangeTest)
    //STD_PHP_INI_ENTRY("color.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_color_globals, color_globals) //STD_PHP_INI_ENTRY("color.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_color_globals, color_globals)

    PHP_INI_END() // 结束 /* }}} */

    宏的转换关系都在 ./Zend/zend_ini.h 中:

    PHP_INI_ENTRY -> ZEND_INI_ENTRY -> ZEND_INI_ENTRY_EX -> ZEND_INI_ENTRY3_EX

    STD_PHP_INI_ENTRY -> STD_ZEND_INI_ENTRY -> ZEND_INI_ENTRY2 -> ZEND_INI_ENTRY2_EX -> ZEND_INI_ENTRY3_EX

    由于 ZEND_INI_ENTRY 只需要传4个参数,到最后变成 ZEND_INI_ENTRY3_EX 时后面3个参数已经默认为 NULL .

    当不需要后面的参数时用 PHP_INI_ENTRY,想传参的时候用 STD_PHP_INI_ENTRY,最后两个参数是由下面这个扩展内的函数初始化的.

    /* {{{ php_color_init_globals
     */
    /* Uncomment this function if you have INI entries
    static void php_color_init_globals(zend_color_globals *color_globals)
    {
        color_globals->global_value = 0;
        color_globals->global_string = NULL;
    }
    */
    /* }}} */

    ./Zend/zend_ini.h:114

    #define ZEND_INI_BEGIN()        static const zend_ini_entry ini_entries[] = {
    #define ZEND_INI_END()      { 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL } };
    #define ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, arg1, arg2, arg3, displayer) 
        { 0, modifiable, name, sizeof(name), on_modify, arg1, arg2, arg3, default_value, sizeof(default_value)-1, NULL, 0, 0, 0, displayer },

    最终的 PHP_INI 区块实际编译成一个如下结构的数组:

    static const zend_ini_entry ini_entries[] = {
        { 0, modifiable, name, sizeof(name), on_modify, arg1, arg2, arg3, default_value, sizeof(default_value)-1, NULL, 0, 0, 0, displayer },
        { 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL }
    }

      3). 访问配置项的宏 ( Zend/zend_ini.h:157 )

    #define INI_INT(name) zend_ini_long((name), sizeof(name), 0)
    #define INI_FLT(name) zend_ini_double((name), sizeof(name), 0)
    #define INI_STR(name) zend_ini_string_ex((name), sizeof(name), 0, NULL)
    #define INI_BOOL(name) ((zend_bool) INI_INT(name))
    
    #define INI_ORIG_INT(name)  zend_ini_long((name), sizeof(name), 1)
    #define INI_ORIG_FLT(name)  zend_ini_double((name), sizeof(name), 1)
    #define INI_ORIG_STR(name)  zend_ini_string((name), sizeof(name), 1)
    #define INI_ORIG_BOOL(name) ((zend_bool) INI_ORIG_INT(name))

    头四个用来获取INI设置的当前值,后四个用来读取未经修改的INI值 ( Zend/zend_ini.h:102 ).

    ZEND_API long zend_ini_long(char *name, uint name_length, int orig);
    ZEND_API double zend_ini_double(char *name, uint name_length, int orig);
    ZEND_API char *zend_ini_string(char *name, uint name_length, int orig);
    ZEND_API char *zend_ini_string_ex(char *name, uint name_length, int orig, zend_bool *exists);

    实现在 Zend/zend_ini.c:348

    /*
     * Data retrieval
     */
    
    ZEND_API long zend_ini_long(char *name, uint name_length, int orig) /* {{{ */
    {
        zend_ini_entry *ini_entry;
        TSRMLS_FETCH();  // 在需要访问全局变量的代码块开头使用TSRMLS_FETCH()来提取上下文
    
        if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == SUCCESS) {
         if (orig && ini_entry->modified) { return (ini_entry->orig_value ? strtol(ini_entry->orig_value, NULL, 0) : 0); } else { return (ini_entry->value ? strtol(ini_entry->value, NULL, 0) : 0); } } return 0; } /* }}} */

    编译扩展、重启php生效:make && sudo make install && sudo sh /home/weiChen/reloadPhp.sh

    开发文档:https://github.com/farwish/php-core-hack

    推荐文章:用C/C++扩展你的PHP

    Link: http://www.cnblogs.com/farwish/p/5248686.html

  • 相关阅读:
    Highcharts 柱图 每个柱子外围的白色边框
    进阶ES6 点滴认知
    layer 问题 汇总
    iframe 常见问题 解析
    【腾讯开源】前端预处理器语言图形编译工具 Koala使用指南
    ruby环境sass编译中文出现Syntax error: Invalid GBK character错误解决方法
    Sass 混合宏、继承、占位符 详解
    git 命令篇
    git 继续前进篇
    zh-cn en-uk、zh-tw表示语言(文化)代码与国家地区对照表(最全的各国地区对照表)
  • 原文地址:https://www.cnblogs.com/farwish/p/5248686.html
Copyright © 2011-2022 走看看