Skip to content

Commit 01ebfe6

Browse files
committed
update
1 parent 641e359 commit 01ebfe6

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

3/function_implement.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,58 @@ typedef struct _zend_ast_decl {
135135

136136
![](../img/ast_function.png)
137137

138+
具体编译为opcodes的过程在`zend_compile_func_decl()`中:
139+
```c
140+
void zend_compile_func_decl(znode *result, zend_ast *ast)
141+
{
142+
zend_ast_decl *decl = (zend_ast_decl *) ast;
143+
zend_ast *params_ast = decl->child[0]; //参数列表
144+
zend_ast *uses_ast = decl->child[1]; //use列表
145+
zend_ast *stmt_ast = decl->child[2]; //函数内部
146+
zend_ast *return_type_ast = decl->child[3]; //返回值类型
147+
zend_bool is_method = decl->kind == ZEND_AST_METHOD; //是否为成员函数
148+
149+
//这里保存当前正在编译的zend_op_array:CG(active_op_array),然后重新为函数生成一个新的zend_op_array,
150+
//函数编译完再将旧的还原
151+
zend_op_array *orig_op_array = CG(active_op_array);
152+
zend_op_array *op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); //新分配zend_op_array
153+
...
154+
155+
zend_compile_params(params_ast, return_type_ast); //编译参数
156+
if (uses_ast) {
157+
zend_compile_closure_uses(uses_ast);
158+
}
159+
zend_compile_stmt(stmt_ast); //编译函数内部语法
160+
...
161+
pass_two(CG(active_op_array));
162+
...
163+
CG(active_op_array) = orig_op_array; //还原之前的
164+
}
165+
```
166+
> __编译过程主要有这么几个处理:__
167+
168+
> (1)保存当前正在编译的zend_op_array,新分配一个结构,因为每个函数、include的文件都对应独立的一个zend_op_array,通过CG(active_op_array)记录当前编译所属zend_op_array,所以开始编译函数时就需要将这个值保存下来,等到函数编译完成再还原回去;
169+
170+
```php
171+
$a = 123; //当前为CG(active_op_array) = zend_op_array_1,编译到这时此opcode加到zend_op_array_1
172+
173+
//新分配一个zend_op_array_2,并将当前CG(active_op_array)保存到origin_op_array,
174+
//然后将CG(active_op_array)=zend_op_array_2
175+
function test(){
176+
$b = 234; //编译到zend_op_array_2
177+
}//函数编译结束,将CG(active_op_array) = origin_op_array,切回zend_op_array_1
178+
$c = 345; //编译到zend_op_array_1
179+
```
180+
> (2)编译参数列表,函数的参数我们在上一小节已经介绍,完整的参数会有三个组成:参数类型(可选)、参数名、默认值(可选),这三部分分别保存在参数节点的三个child节点中,编译参数的过程有两个关键操作:
181+
182+
>> 操作1:为每个参数编号
183+
184+
>> 操作2:每个参数生成一条opcode,如果是可变参数其opcode=ZEND_RECV_VARIADIC,如果有默认值则为ZEND_RECV_INIT,否则为ZEND_RECV
185+
186+
> 上面的例子中$a编号为96,$b为112,同时生成了两条opcode:ZEND_RECV、ZEND_RECV_INIT,调用的时候会根据具体传参数量跳过部分opcode,比如这个函数我们这么调用`my_function($a)`则ZEND_RECV这条opcode就直接跳过了,然后执行ZEND_RECV_INIT将默认值写到112位置,具体的编译过程在`zend_compile_params()`中,这里不再展开。
187+
188+
> (3)编译函数内部语法,这个跟普通PHP代码编译过程无异。
189+
138190
### 3.2.2 内部函数
139191
上一节已经提过,内部函数指的是由内核、扩展提供的C语言编写的function,这类函数不需要经历opcode的编译过程,所以效率上要高于PHP用户自定义的函数,调用时与普通的C程序没有差异。
140192

0 commit comments

Comments
 (0)