1. Function assembly functions correspond to a set of independent assembly instructions, and then call instructions to implement the function calls. php compiled opcode array, corresponding to assembly instructions.
The implementation of the PHP user-defined function is to compile the function into a separate opcode array, the call is assigned a separate execution stack to execute opcode in turn, so the custom function is nothing special to Zend, but the opcode is packaged, the instructions outside the function in the PHP script , the whole can be considered a function (or understood as the main function more intuitive).
2. Storage structure of functions
typedef Union _zend_function zend_function;//zend_compile.hUnion _zend_function {Zend_uchar type; /*must is the first element of this struct!*/ struct{Zend_uchar type; /*never used*/Zend_uchar arg_flags[3];/*Bitset of Arg_info.pass_by_reference*/uint32_t fn_flags; Zend_string*function_name; Zend_class_entry*scope;//The class that the member method belongs to, used in the object-oriented implementationUnion _zend_function *prototype; uint32_t Num_args; //Number of parametersuint32_t Required_num_args;//number of required parametersZend_arg_info *arg_info;//parameter Information} Common; Zend_op_array Op_array; //The function is actually compiled into a normal zend_op_arrayzend_internal_function internal_function;};
This is a union,php function in addition to the user-defined function there is an intrinsic function, the intrinsic function is extended or the kernel provides the C function, such as time, array series and so on. Internal functions are mostly used internal_function
, and user-defined functions are compiled with a normal opcode array, using op_array
(note: Op_array, internal_function are embedded two structures, not a single pointer)
There is a very important value in the life cycle of a PHP script, and the executor_globals(EG
宏实际就是对executor_globals
的操作)
type is struct _zend_executor_globals
that it records all the data in the PHP life cycle, if you have ever written PHP extensions.
EG(function_table)
is a hash table that records all of the functions in PHP. PHP compiles user-defined functions into separate opcodes at compile-time, saving them in EG(function_table)
. The call is reassigned to the new Zend_execute_data (equivalent to the run stack), and then the opcodes of the function is executed, the call is restored to the old, and the zend_execute_data
execution continues.
3. Parameters of the function
function parameters in the implementation of the function is the same as the local variables, the compile time there will be a separate number , the parameter name is saved in Zend_op_array.vars, the number is first from the parameters, so according to the order of the parameters of the number is 0, 1, 2 ... (Converting to a relative memory offset is 96, 112, 128 ...), the function call will first copy the value of the parameter to the respective location of each parameter at the call location.
Parameter also has a ZEND_ARG_INFO structure for storing parameter information
struct _zend_arg_info { // parameter name zend_string *class_name; // an explicitly declared parameter type, such as (array $param _1) // If the parameter is referenced, the value of & is 1. // NULL is allowed, note: This value is not used to indicate whether a parameter is required // is a variable parameter, that is ... Usage, same as Golang's usage, a new usage of more than 5.6: function My_func ($a, ... $b) {...}} Zend_arg_info;
4. Compilation of functions
PHP code compilation process, mainly php->ast->opcodes conversion, function is actually a set of PHP code compiled into a separate opcodes, function call is different opcodes between the switch, So the process of compiling the function is basically consistent with the normal PHP code.
Process:
A. Save the Zend_op_array that is currently being compiled, assigning a new structure because each function, include file corresponds to a separate zend_op_array, and the current compilation belongs to Zend_op_ through CG (Active_op_array) Array, so you need to save this value when you start compiling the function, wait until the function is compiled and then revert back to the current Zend_op_array (not the newly generated function) to generate a zend_declare_function opcode , which is the function declaration operation.
B. Compile the parameter list, the complete parameters will have three components: parameter type (optional), argument name, default value (optional), the three parts are stored in the parameter node of the three child nodes, the process of compiling parameters has two key operations: for each parameter number, each parameter generates a opcode
C. Compile function internal syntax, which is the same as the normal PHP code compilation process
D. Convert the operands of Is_const, Is_var, Is_tmp_var, and return values to memory offsets, setting the processing of each instruction handler
Finally, two Zend_op_array are generated:
PHP in the progressive compilation of the discovery of a function to generate a zend_declare_function opcode, and then transferred to the function of the compiler function, compile and then jump back to continue the following compilation, here repeatedly mentioned Zend_declare_ function This opcode is because there is an important operation after the completion of the compilation of functions:zend_do_early_binding(),核心工作就是 将function、class加到CG(function_table)、CG(class_table)中 ,加入成功了就直接把 ZEND_DECLARE_FUNCTION 这条opcode干掉了,加入失败的话则保留,这个相当于 有一部分opcode在『编译时』提前执行了 ,这也是为什么PHP中可以先调用函数再声明函数的原因。
5. Anonymous function anonymous function (Anonymous functions), also called the closure function (closures, allows temporary creation of a function without a specified name.) The value most often used as the callback function (callback) parameter. Of course, there are other applications as well.
6. Intrinsic function intrinsic function refers to functions written by kernel, extended C language, such functions do not need to undergo the opcode compilation process, so the efficiency is higher than the PHP user-defined function, the call is not different from the normal C program.
The structure of the function is zend_function to union, in which internal_function
is the intrinsic function
//zend_complie.htypedefstruct_zend_internal_function {/*Common Elements*/Zend_uchar type; Zend_uchar arg_flags[3];/*Bitset of Arg_info.pass_by_reference*/uint32_t fn_flags; Zend_string*function_name; Zend_class_entry*scope; Zend_function*prototype; uint32_t Num_args; uint32_t Required_num_args; Zend_internal_arg_info*Arg_info; /*END of common elements*/ void(*handler) (internal_function_parameters);//function pointer, expanded: void (*handler) (Zend_execute_data *execute_data, Zval *return_value) struct_zend_module_entry *module; void*reserved[zend_max_reserved_resources];} zend_internal_function;
The zend_internal_function
head is a zend_op_array
common with exactly the same structure. Intrinsic functions conflict with user-defined functions, users cannot overwrite internal functions in PHP code, and error errors are indicated when executing PHP scripts.
Inside the definition of the function, we just need to create a normal C function and then create a zend_internal_function
struct to be added to EG (function_table) (or maybe CG (function_table), depending on which stage of registration) can be used, Intrinsic functions are typically registered in the Php_module_startup phase, typically by defining intrinsic functions as standard extensions.
Implementation of PHP functions