Deeply understand the principle of passing parameters in PHP. First, let's talk about a question that comes to mind today. When writing php extensions, it seems that the parameter (that is, the variable passed to zend_parse_parameters) does not need to be free. Example: * (zend_parse_param first describes a question that comes to mind today. When writing php extensions, it seems that the parameter (that is, the variable passed to zend_parse_parameters) does not need to be free. Example:
* (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, , &str, &str_len) ==
}
Running properly:
test("Hello World");
There is no need to worry about memory leakage of the test function. php will automatically help us reclaim the variables used to save parameters.
How exactly does php do it? To explain this problem, we still need to see how php transmits parameters.
Introduction to EG (argument_stack)
To put it simply, an EG in php stores a stack specifically used to store parameters, called argument_stack. Whenever a function call occurs, php will press the input parameter into EG (argument_stack ). Once the function call ends, EG (argument_stack) is cleared and waits for the next function call.
There are some differences between the implementation of php5.2 and 5.3 on the structure and purpose of the struct of EG (argument_stack. This article mainly takes 5.2 as an example. the changes of 5.3 + will be discussed later.
It is about argument_stack in 5.2, which looks simple and clear. The stack top and bottom are fixed to NULL. Parameters received by the function are pushed to the stack sequentially from left to right. Note that an additional long value will be pushed into the stack, indicating the number of parameters in the stack (10 in total ).
So what is the parameter pushed into argument_stack? Actually, they are zval type pointers. The zva they point to may be a CV variable, an is_ref = 1 variable, a constant number, or a constant string.
In php5.2, EG (argument_stack) is implemented as zend_ptr_stack:
typedef ** **
Initialize argument_stack
The initialization of argument_stack occurs before php processes specific requests. more accurately, it is in the startup process of the php interpreter.
In the init_executor function, we find the following two rows:
zend_ptr_stack_init(&&EG(argument_stack), ( *) NULL);
These two lines represent the initialization of EG (argument_stack), followed by a NULL. Since EG is a global variable, all data in EG (argument_stack) is 0 before zend_ptr_stack_init is called.
Zend_ptr_stack_init is easy to implement.
ZEND_API zend_ptr_stack_init(zend_ptr_stack *->top_element = stack->elements = ( **) emalloc(( *)*->max =->top =
Once argument_stack is initialized, it is immediately pushed into NULL. I don't need to go into it here. this NULL actually has no meaning.
After NULL is added to the stack, the actual memory distribution of the entire argument_stack is as follows:
Parameter entry stack
After being pushed to the first NULL, once a parameter is added to the stack, argument_stack performs the following actions:
stack->top++*(stack->top_element++) =
We use a simple php code to illustrate the problem:
foo( ("hello world");
The above code imports a string constant when calling foo. Therefore, what is actually pushed into the stack is a zval pointing to the storage of "hello world. Use vld to view the compiled opcode:
line # * op fetch ext return operands--------------------------------------------------------------------------------- 3 0 > NOP 6 1 SEND_VAL OP1[ IS_CONST (458754) 'hello world' ] 2 DO_FCALL 1 OP1[ IS_CONST (458752) 'foo' ] 15 3 > RETURN OP1[ IS_CONST (0) 1 ]
What the SEND_VAL command actually does is to press "hello world" into argument_stack.
** = & Opline-> (!
Point to zval zend_ptr_stack_push (&}
The argument_stack after the stack entry is complete is:
Number of parameters
As mentioned above, not all parameters are actually finished in the stack. Php will also press an additional number to indicate the number of parameters, which does not happen in the SEND_XXX command. In fact, php will import the number of parameters into the stack before the function is actually executed.
Continue with the above example. the DO_FCALL command is used to call the foo function. Before calling foo, php will automatically fill in the last part of argument_stack.
extended_value) zend_ptr_stack_2_push(&EG(argument_stack), ( *)(zend_uintptr_t)opline-> (EX(function_state).function->type == (EX(function_state).function->type == {
After the number of input parameters and NULL, the entire argument_stack for foo call has been completed.
GET parameters
Continue with the above example. Let's go deep into the foo function to see what foo's opcode looks like.
line # * op fetch ext return operands--------------------------------------------------------------------------------- 3 0 > RECV OP1[ IS_CONST (0) 1 ] 4 1 SEND_VAL OP1[ IS_CONST (5) 123 ] 2 DO_FCALL 1 OP1[ IS_CONST (459027) 'print_r' ] 5 3 > RETURN OP1[ IS_CONST (0) null ]
The first command is RECV, which is literally used to obtain parameters in the stack. Actually, SEND_VAL and RECV are similar. Before each function call, SEND_VAL performs RECV within the function. The RECV command is not required. A recv is generated only when a user-defined function is called. The extended functions we have compiled, built-in php functions, do not have RECV.
Note that each SEND_VAL and RECV can only process one parameter. That is to say, if there are multiple parameters in the parameter passing process, several SEND_VAL and several RECV will be generated. Here is an interesting topic. what is the order of input parameters and parameters?
The answer is that SEND_VAL will press the stack from left to right, and get the parameters from left to right in the same way as RECV.
Stack bottom (zend_ptr_stack_get_arg (arg_num, (**) & param TSRMLS_CC) = ** zend_verify_arg_type (zend_function *) EG (active_op_array), arg_num, * = get_zval_ptr_ptr (& opline-> result, EX (Ts), & (PZVAL_IS_REF (**
Both zend_assign_to_variable_reference and zend_receive complete "GET parameters ". "Getting parameters" is not easy to understand. What exactly does it actually do?
In the end, it is easy to add the parameter to the symbol table during the current function execution, which corresponds to EG (current_execute_data)-> symbol_table. In this example, after RECV is completed, the symbol_table in the function contains a symbol 'str', whose value is "hello world ".
However, argument_stack does not have any changes, because RECV only reads parameters and does not generate pop-like operations on stacks.
Clear argument_stack
The print_r inside foo is also a function call, so it also produces a stack-> Clear operation. Therefore, the argument_stack before print_r execution is:
After print_r is executed, argument_stack returns to foo's RECV state.
The process of calling print_r is not the focus of this article. We are concerned about how php clears argument_stack after calling foo.
As shown in the do_fcall code snippet shown above
inline **p = EG(argument_stack).top_element- delete_count = ()(zend_uintptr_t) *-= (delete_count+ (--delete_count>=*q = *(zval **)(--*p =&=
Note that zval pointer in the stack is cleared here, and zval_ptr_dtor is used. Zval_ptr_dtor will reduce the refcount by 1. Once the refcount is reduced to 0, the memory area where the variable is saved will be actually recycled.
In this example, after foo is called, the zval status of "hello world" is saved as follows:
value "hello world"refcount 1type 6is_ref 0
Since refcount has only 1 left, zval_ptr_dtor will actually destroy "hello world" from the memory.
The argument_stack memory status after Stack cancellation is: