Home Backend Development PHP Tutorial 深入理解PHP内核(五)函数的内部结构

深入理解PHP内核(五)函数的内部结构

Jun 23, 2016 pm 01:16 PM

php的函数包括用户定义的函数、内部函数(print_r count...)、匿名函数、变量函数($func = 'print_r'; $func(array('a','b'));)

PHP内核源码中将函数分为以下类型

#define ZEND_INTERNAL_FUNCTION              1#define ZEND_USER_FUNCTION                  2  #define ZEND_OVERLOADED_FUNCTION            3#define ZEND_EVAL_CODE                      4#define ZEND_OVERLOADED_FUNCTION_TEMPORARY  5
Copy after login

一、用户函数(ZEND_USER_FUNCTION)

  函数不一定显式的有返回值,在PHP的实现中即使没有显式的返回,PHP内核也会帮我们返回NULL。

  ZEND在执行过程中,会将运行时信息存储于_zend_execute_data中:

struct _zend_execute_data {    //...省略部分代码    zend_function_state function_state;    zend_function *fbc; /* Function Being Called */    //...省略部分代码};
Copy after login

  在程序初始化的过程中,function_state也会进行初始化,function_state由两个部分组成:

typedef struct _zend_function_state {    zend_function *function;    void **arguments;} zend_function_state;
Copy after login

  *arguments是一个指向函数参数的指针,而函数体本事存储于*function中,*function是一个zend_function结构体,它最终存储了用户自定义函数的一切信息,具体结构如下:

typedef union _zend_function {    zend_uchar type;    /* MUST be the first element of this struct! */     struct {        zend_uchar type;  /* never used */        char *function_name;    //函数名称        zend_class_entry *scope; //函数所在的类作用域        zend_uint fn_flags;     //函数类型,如用户自定义则为 #define ZEND_USER_FUNCTION 2          union _zend_function *prototype; //函数原型        zend_uint num_args;     //参数数目        zend_uint required_num_args; //需要的参数数目        zend_arg_info *arg_info;  //参数信息指针        zend_bool pass_rest_by_reference;        unsigned char return_reference;  //返回值    } common;     zend_op_array op_array;   //函数中的操作‰    zend_internal_function internal_function;  } zend_function;
Copy after login

  zend_function的结构体中的op_array存储了该函数中的所有操作,当函数被调用时,ZEND就会将这个op_array中的opline一条条顺序执行,并将最后的结果返回。函数的定义和执行是分开的,一个函数可以作为一个独立的运行单元存在。

二、内部函数(ZEND_INTERNAL_FUNCTION)

  ZEND_INTERNAL_FUNCTION函数是由扩展或者Zend/PHP内核提供的,用c/c++编写,可以直接执行的函数,以下为内部函数的结构

typedef struct _zend_internal_function {    /* Common elements */    zend_uchar type;    char * function_name;    zend_class_entry *scope;    zend_uint fn_flags;    union _zend_function *prototype;    zend_uint num_args;    zend_uint required_num_args;    zend_arg_info *arg_info;    zend_bool pass_rest_by_reference;    unsigned char return_reference;    /* END of common elements */     void (*handler)(INTERNAL_FUNCTION_PARAMETERS);    struct _zend_module_entry *module;} zend_internal_function;
Copy after login

  在模块初始化的时候,ZE会遍历每个载入的扩展模块,然后将模块中function_entry中指明的每一个函数(module->functions),创建一个zend_internal_function结构,并将其type设置为ZEND_INTERNAL_FUNCTION,将这个结构填入全局的函数表(HashTable结构);函数设置及注册过程见Zend/zene_API.c文件中的zend_register_function函数,这个函数除了处理函数页也处理类的方法,包括那些魔术方法。

  内部函数的结构与用户自定义函数结构基本类似,有一些不同:

  •   调用方法,handler字段,如果是ZEND_INTERNAL_FUNCTION,那么ZEND就会调用zend_execute_internal,通过zend_internal_function.handler来执行这个函数。而用户自定义函数需要生成中间代码,然后通过中间代码映射到相对就把方法调用。
  • 内置函数在结构中多了一个module字段,表示属于哪个模块。不同的扩展模块不同
  • type字段,在用户自定义函数中,type字段几乎无用,而内置函数中的type字段作为几种内部函数的区分。
  • 三、变量函数

      如果一个变量名后边有圆括号,php将寻找与变量的值同名的函数,并且尝试执行。

      变量函数$func

    $func = 'print_r';$func('i am print_r function.');
    Copy after login

      编译后中间代码

    function name:  (null)number of ops:  9compiled vars:  !0 = $funcline     # *  op                           fetch          ext  return operands--------------------------------------------------------------------------------   2     0  >   EXT_STMT         1      ASSIGN                                                   !0, 'print_r'   3     2      EXT_STMT         3      INIT_FCALL_BY_NAME                                       !0         4      EXT_FCALL_BEGIN         5      SEND_VAL                                                 'i+am+print_r+function.'         6      DO_FCALL_BY_NAME                              1         7      EXT_FCALL_END         8    > RETURN                                  1
    Copy after login

      内部函数

    print_r('i am print_r function.');
    Copy after login

      编译后中间代码

    function name:  (null)number of ops:  6compiled vars:  noneline     # *  op                           fetch          ext  return  operands---------------------------------------------------------------------------------   2     0  >   EXT_STMT         1      EXT_FCALL_BEGIN         2      SEND_VAL                                                 'i+am+print_r+function.'         3      DO_FCALL                                      1          'print_r'         4      EXT_FCALL_END         5    > RETURN                                                   1
    Copy after login

      对比发现,二者在调用中间代码上存在一些区别,变量函数是DO_FCALL_BY_NAME,而内部函数是DO_FCALL。这在语法解析时就已经决定了,见Zend/zend_complie.c文件的zend_do_end_function_call函数中部分代码:

    if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {        opline->opcode = ZEND_DO_FCALL;        opline->op1 = *function_name;        ZVAL_LONG(&opline->op2.u.constant, zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name->u.constant) + 1));    } else {        opline->opcode = ZEND_DO_FCALL_BY_NAME;        SET_UNUSED(opline->op1);    }
    Copy after login

      如果不是方法,并且不是动态调用,并且函数名为字符串变量,则其生成的中间代码为ZEND_DO_FCALL。其他情况则为ZEND_DO_FCALL_BY_NAME。另外将变量函数作为回调函数,其处理过程在Zend/zend_complie.c文件的zend_do_pass_param函数中,最终会体现在中间代码执行过程中的ZEND_SEND_VAL_SPEC_CONST_HADNLER等函数中。

     

    四、匿名函数

      匿名函数是一类不需要指定表示符,而又可以被调用的函数或子例程,匿名函数可以方便的作为参数传递给其他函数。

      

    Statement of this Website
    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

    Hot AI Tools

    Undresser.AI Undress

    Undresser.AI Undress

    AI-powered app for creating realistic nude photos

    AI Clothes Remover

    AI Clothes Remover

    Online AI tool for removing clothes from photos.

    Undress AI Tool

    Undress AI Tool

    Undress images for free

    Clothoff.io

    Clothoff.io

    AI clothes remover

    Video Face Swap

    Video Face Swap

    Swap faces in any video effortlessly with our completely free AI face swap tool!

    Hot Tools

    Notepad++7.3.1

    Notepad++7.3.1

    Easy-to-use and free code editor

    SublimeText3 Chinese version

    SublimeText3 Chinese version

    Chinese version, very easy to use

    Zend Studio 13.0.1

    Zend Studio 13.0.1

    Powerful PHP integrated development environment

    Dreamweaver CS6

    Dreamweaver CS6

    Visual web development tools

    SublimeText3 Mac version

    SublimeText3 Mac version

    God-level code editing software (SublimeText3)

    Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

    JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

    How does session hijacking work and how can you mitigate it in PHP? How does session hijacking work and how can you mitigate it in PHP? Apr 06, 2025 am 12:02 AM

    Session hijacking can be achieved through the following steps: 1. Obtain the session ID, 2. Use the session ID, 3. Keep the session active. The methods to prevent session hijacking in PHP include: 1. Use the session_regenerate_id() function to regenerate the session ID, 2. Store session data through the database, 3. Ensure that all session data is transmitted through HTTPS.

    Describe the SOLID principles and how they apply to PHP development. Describe the SOLID principles and how they apply to PHP development. Apr 03, 2025 am 12:04 AM

    The application of SOLID principle in PHP development includes: 1. Single responsibility principle (SRP): Each class is responsible for only one function. 2. Open and close principle (OCP): Changes are achieved through extension rather than modification. 3. Lisch's Substitution Principle (LSP): Subclasses can replace base classes without affecting program accuracy. 4. Interface isolation principle (ISP): Use fine-grained interfaces to avoid dependencies and unused methods. 5. Dependency inversion principle (DIP): High and low-level modules rely on abstraction and are implemented through dependency injection.

    How to debug CLI mode in PHPStorm? How to debug CLI mode in PHPStorm? Apr 01, 2025 pm 02:57 PM

    How to debug CLI mode in PHPStorm? When developing with PHPStorm, sometimes we need to debug PHP in command line interface (CLI) mode...

    How to automatically set permissions of unixsocket after system restart? How to automatically set permissions of unixsocket after system restart? Mar 31, 2025 pm 11:54 PM

    How to automatically set the permissions of unixsocket after the system restarts. Every time the system restarts, we need to execute the following command to modify the permissions of unixsocket: sudo...

    Framework Security Features: Protecting against vulnerabilities. Framework Security Features: Protecting against vulnerabilities. Mar 28, 2025 pm 05:11 PM

    Article discusses essential security features in frameworks to protect against vulnerabilities, including input validation, authentication, and regular updates.

    Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

    Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

    See all articles