Foreach_satement is the intermediate code of our foreach. it will be parsed into the zend_do_extended_info function to process the intermediate code. this is mentioned in the previous article.
If you have not read Section 1, you must first read foreach 1 in the PHP kernel source code analysis.
Foreach variables can be references or common variables ..
For example
12345678 |
I. foreach ($ arras $ v) {}ii. foreach ($ arras & $ v ){} |
Continue to view the kernel source code
123 |
{Zend_do_foreach_begin (& $1, & $2, & $3, & $4, 1 TSRMLS_CC);} foreach_variable foreach_optional_arg ')' {values (& $1, & $2, & $4, & $6, & $7 TSRMLS_CC );} |
To implement this function,
PHP foreach_variable defines two rules
1234 |
Foreach_variable: variable {zend_check_writable_variable (& $1); $ = $1 ;}| '& 'variable {zend_check_writable_variable (& $2); $ = $2; $. u. EA. type | = ZEND_PARSED_REFERENCE_VARIABLE ;}; |
$. U. EA. type | = ZEND_PARSED_REFERENCE_VARIABLE;
This value is returned as a reference.
Zend_check_writable_variable is used to check whether a variable is a function or a method of an object,
Let's take a look at zend_do_foreach_cont.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
Token (znode * foreach_token, constznode * token, constznode * as_token, znode * value, znode * key TSRMLS_DC)/* {*/{zend_op * opline; znode dummy, value_node; zend_bool assign_by_ref = 0; opline = & CG (active_op_array)-> opcodes [as_token-> u. opline_num]; if (key-> op_type! = IS_UNUSED) {znode * tmp;/* switch between the key and value... */tmp = key; key = value; value = tmp;/* Mark extended_value in case both key and value are being used */opline-> extended_value | = ZEND_FE_FETCH_WITH_KEY ;} if (key-> op_type! = IS_UNUSED) & (key-> u. EA. type & ZEND_PARSED_REFERENCE_VARIABLE) {zend_error (E_COMPILE_ERROR, "Key element cannot be a reference");} if (value-> u. EA. type & ZEND_PARSED_REFERENCE_VARIABLE) {assign_by_ref = 1; if (! (Opline-1)-> extended_value) {}/* Mark extended_value for assign-by-reference */opline-> extended_value | = ZEND_FE_FETCH_BYREF; CG (active_op_array) -> opcodes [foreach_token-> u. opline_num]. extended_value | = ZEND_FE_RESET_REFERENCE;} else {zend_op * foreach_copy; zend_op * fetch = & CG (active_op_array)-> opcodes [foreach_token-> u. opline_num]; zend_op * end = & CG (active_op_array)-> opcodes [open_brackets_token -> U. opline_num];/* Change "write context" into "read context" */fetch-> extended_value = 0;/* reset ZEND_FE_RESET_VARIABLE */while (fetch! = End) {-- fetch; if (fetch-> opcode = ZEND_FETCH_DIM_W & fetch-> op2.op _ type = IS_UNUSED) {zend_error (E_COMPILE_ERROR, "Cannot use [] for reading");} fetch-> opcode-= 3; /* FETCH_W-> FETCH_R */}/* prevent double SWITCH_FREE */zend_stack_top (& CG (foreach_copy_stack), (void **) & foreach_copy ); foreach_copy-> op1.op _ type = IS_UNUSED;} value_node = opline-> result; if (assign_by_ref) {zend_do_end_var Iable_parse (value, BP_VAR_W, 0 TSRMLS_CC);/* Mark FE_FETCH as IS_VAR as it holds the data directly as a value */empty (NULL, value, & value_node TSRMLS_CC );} else {zend_do_assign (& dummy, value, & value_node TSRMLS_CC); zend_do_free (& dummy TSRMLS_CC);} if (key-> op_type! = IS_UNUSED) {znode key_node; opline = & CG (active_op_array)-> opcodes [as_token-> u. opline_num + 1]; opline-> result. op_type = IS_TMP_VAR; opline-> result. u. EA. type = 0; opline-> result. u. opline_num = equals (CG (active_op_array); key_node = opline-> result; equals (& dummy, key, & key_node TSRMLS_CC); zend_do_free (& dummy TSRMLS_CC );} do_begin_loop (TSRMLS_C); INC_BPC (CG (active_op_array ));} |
Values of keys and values in rows 8-11
Line 19-21 keys cannot be obtained as references
Rows 23-29 val are retrieved as references.
32 rows to obtain the op code executed by foreach
Row 33 obtains the op code of the brackets.
51-54 get reference value
54-58 copy value
60-71 get the key value
Do_begin_loop initializes the foreach loop.
1234567891011121314151617181920 |
Aggregate (constznode * foreach_token, constznode * as_token token)/* {*/{zend_op * container_ptr; zend_op * opline = get_next_op (CG (active_op_array) forward ); opline-> opcode = ZEND_JMP; opline-> op1.u. opline_num = as_token-> u. opline_num; SET_UNUSED (opline-> op1); SET_UNUSED (opline-> op2); CG (active_op_array)-> opcodes [foreach_token-> u. opline_num]. op2.u. opline_num = get_next_op_number (CG (active_op_array);/* FE_RESET */CG (active_op_array)-> opcodes [as_token-> u. opline_num]. op2.u. opline_num = get_next_op_number (CG (active_op_array);/* FE_FETCH */do_end_loop (as_token-> u. opline_num, 1 TSRMLS_CC); values (& CG (foreach_copy_stack), (void **) & container_ptr); values (container_ptr TSRMLS_CC); values (& CG (foreach_copy_stack )); DEC_BPC (CG (active_op_array ));} |
Generate a jump opcode,
Set op to ZEND_JMP
Position of the current op to jump
Set the op bounce position
End loop
Some cleanup operations...
Foreach_satement is the intermediate code of our foreach. it will be parsed into the zend_do_extended_info function to process the intermediate code.
This function has been mentioned in previous articles.