diff --git a/2/static_var.md b/2/static_var.md index d382cb4..c134cf2 100644 --- a/2/static_var.md +++ b/2/static_var.md @@ -87,7 +87,7 @@ if (by_ref) { * __ZEND_FETCH_W:__ 这条opcode对应的操作是创建一个IS_INDIRECT类型的zval,指向static_variables中对应静态变量的zval * __ZEND_ASSIGN_REF:__ 它的操作是引用赋值,即将一个引用赋值给CV变量 -通过上面两条opcode可以确定静态变量的读写过程:首先根据变量名在static_variables中取出对应的zval,然后将它修改为引用类型并赋值给局部变量,也就是说`static $count = 4;`包含了两个操作,严格的将`$count`并不是真正的静态变量,它只是一个指向静态变量的局部变量,执行时实际操作是:`$count = & static_variables["count"];`。上面例子$count与static_variables["count"]间的关系如图所示。 +通过上面两条opcode可以确定静态变量的读写过程:首先根据变量名在static_variables中取出对应的zval,然后将它修改为引用类型并赋值给局部变量,也就是说`static $count = 4;`包含了两个操作,严格的说`$count`并不是真正的静态变量,它只是一个指向静态变量的局部变量,执行时实际操作是:`$count = & static_variables["count"];`。上面例子$count与static_variables["count"]间的关系如图所示。  diff --git a/2/zval.md b/2/zval.md index 6f96b7e..dd82bc0 100644 --- a/2/zval.md +++ b/2/zval.md @@ -278,7 +278,7 @@ $b[] = 3;  -不是所有类型都可以copy的,比如对象、资源,实时上只有string、array两种支持,与引用计数相同,也是通过`zval.u1.type_flag`标识value是否可复制的: +不是所有类型都可以copy的,比如对象、资源,事实上只有string、array两种支持,与引用计数相同,也是通过`zval.u1.type_flag`标识value是否可复制的: ```c #define IS_TYPE_COPYABLE (1<<4) ``` diff --git a/3/zend_class.md b/3/zend_class.md index e0bc8f5..62eea77 100644 --- a/3/zend_class.md +++ b/3/zend_class.md @@ -1,7 +1,7 @@ ### 3.4.1 类 类是现实世界或思维世界中的实体在计算机中的反映,它将某些具有关联关系的数据以及这些数据上的操作封装在一起。在面向对象中类是对象的抽象,对象是类的具体实例。 -在PHP中类编译阶段的产物,而对象是运行时产生的,它们归属于不同阶段。 +在PHP中类是编译阶段的产物,而对象是运行时产生的,它们归属于不同阶段。 PHP中我们这样定义一个类: ```php @@ -416,7 +416,7 @@ void zend_compile_class_const_decl(zend_ast *ast) zend_class_entry *ce = CG(active_class_entry); uint32_t i; - for (i = 0; i < list->children; ++i) { //不清楚这个地方为什么要用list,试了几个例子这个节点都只有一个child,即for只循环一次 + for (i = 0; i < list->children; ++i) { //const声明了多个常量,遍历编译每个子节点 zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; //常量名节点 zend_ast *value_ast = const_ast->child[1];//常量值节点 @@ -446,7 +446,6 @@ void zend_compile_prop_decl(zend_ast *ast) zend_class_entry *ce = CG(active_class_entry); uint32_t i, children = list->children; - //也不清楚这里为啥用循环,测试的情况child只有一个 for (i = 0; i < children; ++i) { zend_ast *prop_ast = list->child[i]; //这个节点类型为:ZEND_AST_PROP_ELEM zend_ast *name_ast = prop_ast->child[0]; //属性名节点 diff --git a/4/loop.md b/4/loop.md index 19ce394..f698d0e 100644 --- a/4/loop.md +++ b/4/loop.md @@ -241,7 +241,7 @@ foreach($arr as $k=>$v){ 了解了foreach的实现、运行机制我们再回头看下其编译过程: -* __(1)__ 编译"拷贝"数组/对象操作的opcode:`ZEND_FE_RESET_R`,如果value是引用则是`ZEND_FE_RESET_RW`,执行时如果发现数组或对象属性为空则直接跳出遍历,所以这条opcode还需要知道跳出的位置,这个位置需要编译完foreach以后才能确定; +* __(1)__ 编译拷贝数组、对象操作的指令:ZEND_FE_RESET_R,如果value是引用则是ZEND_FE_RESET_RW。执行时如果发现遍历的变量不是数组、对象,则抛出一个warning,然后跳出循环,所以这条指令还需要知道跳出的位置,这个位置需要编译完foreach以后才能确定; * __(2)__ 编译fetch数组/对象当前单元key、value的opcode:`ZEND_FE_FETCH_R`,如果是引用则是`ZEND_FE_FETCH_RW`,此opcode还需要知道当遍历已经到达数组末尾时跳出遍历的位置,与步骤(1)的opcode相同,另外还有一个关键操作,前面已经说过遍历的key、value实际就是普通的局部变量,它们的内存存储位置正是在这一步分配确定的,分配过程与普通局部变量的过程完全相同,如果value不是一个CV变量(比如:foreach($arr as $v["xx"]){...})则还会编译其它操作的opcode; * __(3)__ 如果foreach定义了key则编译一条赋值opcode,此操作是对key进行赋值; * __(4)__ 编译循环体statement; diff --git a/7/extension_intro.md b/7/extension_intro.md index f6aafc8..020a179 100644 --- a/7/extension_intro.md +++ b/7/extension_intro.md @@ -270,7 +270,7 @@ __(5)PHP_ADD_INCLUDE(path):__ 添加include路径,即:`gcc -Iinclude_dir`, __(6)PHP_CHECK_LIBRARY(library, function [, action-found [, action-not-found [, extra-libs]]]):__ 检查依赖的库中是否存在需要的function,action-found为存在时执行的动作,action-not-found为不存在时执行的动作,比如扩展里使用到线程pthread,检查pthread_create(),如果没找到则终止./configure执行: ```sh -PHP_ADD_INCLUDE(pthread, pthread_create, [], [ +PHP_CHECK_LIBRARY(pthread, pthread_create, [], [ AC_MSG_ERROR([not find pthread_create() in lib pthread]) ]) ``` diff --git a/7/func.md b/7/func.md index 74ae72a..4c4a47c 100644 --- a/7/func.md +++ b/7/func.md @@ -376,8 +376,25 @@ my_func_1(array($object, 'method')); #### 7.6.2.11 其它标识符 除了上面介绍的这些解析符号以外,还有几个有特殊用法的标识符:"|"、"+"、"*",它们并不是用来表示某种数据类型的。 -* __|:__ 表示此后的参数为可选参数,可以不传,比如解析规则为:"al|b",则可以传2个或3个参数,如果是:"alb",则必须传3个,否则将报错; -* __+/*:__ 用于可变参数,注意这里与PHP函数...的用法不太一样,PHP中可以把函数最后一个参数前加...,表示调用时可以传多个参数,这些参数都会插入...参数的数组中,"*/+"也表示这个参数是可变的,但内核中只能接收一个值,即使传了多个后面那些也解析不到,"*"、"+"的区别在于"*"表示可以不传可变参数,而"+"表示可变参数至少有一个。 +* __|:__ 表示此后的参数为可选参数,可以不传,比如解析规则为:"al|b",则可以传2个或3个参数,如果是:"alb",则必须传3个,否则将报错 +* __+、* :__ 用于可变参数,`+、*`的区别在于 * 表示可以不传可变参数,而 + 表示可变参数至少有一个。可变参数将被解析到zval数组,可以通过一个整形参数,用于获取具体的数量,例如: +```c +PHP_FUNCTION(my_func_1) +{ + zval *args; + int argc; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + //... +} +``` +argc获取的就是可变参数的数量,args为参数数组,指向第一个参数,可以通过args[i]获取其它参数,比如这样传参: +```php +my_func_1(array(), 1, false, "ddd"); +``` +那么传入的4个参数就可以在解析后通过args[0]、args[1]、args[2]、args[3]获取。 ### 7.6.3 引用传参 上一节介绍了如何在内部函数中解析参数,这里还有一种情况没有讲到,那就是引用传参: diff --git a/7/implement.md b/7/implement.md index bebf3d4..7c84d1c 100644 --- a/7/implement.md +++ b/7/implement.md @@ -1,5 +1,5 @@ ## 7.2 扩展的实现原理 -PHP中扩展通过`zend_module_entry`这个结构来表示,此结构定义了扩展的全部信息:扩展名、扩展版本、扩展提供的函数列表以及PHP四个执行阶段的hook函数等,每一个扩展都需要定义一个此结构的变量,而且这个变量的名称格式必须是:`{mudule_name}_module_entry`,内核正是通过这个结构获取到扩展提供的功能的。 +PHP中扩展通过`zend_module_entry`这个结构来表示,此结构定义了扩展的全部信息:扩展名、扩展版本、扩展提供的函数列表以及PHP四个执行阶段的hook函数等,每一个扩展都需要定义一个此结构的变量,而且这个变量的名称格式必须是:`{module_name}_module_entry`,内核正是通过这个结构获取到扩展提供的功能的。 扩展可以在编译PHP时一起编译(静态编译),也可以单独编译为动态库,动态库需要加入到php.ini配置中去,然后在`php_module_startup()`阶段把这些动态库加载到PHP中: ```c diff --git a/8/namespace.md b/8/namespace.md index b9b7c6d..58ac9fe 100644 --- a/8/namespace.md +++ b/8/namespace.md @@ -259,9 +259,9 @@ typedef struct _zend_file_context { ``` 简单总结下use的几种不同用法: * __a.导入命名空间:__ 导入的名称保存在FC(imports)中,编译使用的语句时搜索此符号表进行补全 -* __b.导入类:__ 导入的名称保存在FC(imports)中,与a不同的时如果不会根据"\"切割后的最后一节检索,而是直接使用类名查找 +* __b.导入类:__ 导入的名称保存在FC(imports)中,与a不同的是不会根据"\"切割后的最后一节检索,而是直接使用类名查找 * __c.导入函数:__ 通过`use function`导入到FC(imports_function),补全时先查找FC(imports_function),如果没有找到则继续按照a的情况处理 -* __d.导入常量:__ 通过`use const`导入到FC(imports_const),不全是先查找FC(imports_const),如果没有找到则继续按照a的情况处理 +* __d.导入常量:__ 通过`use const`导入到FC(imports_const),补全时先查找FC(imports_const),如果没有找到则继续按照a的情况处理 ```php use aa\bb; //导入namespace @@ -427,7 +427,7 @@ zend_string *zend_resolve_non_class_name( return zend_prefix_with_ns(name); } ``` -可以看到,函数与常量的的补全逻辑只是优先用原始名称去FC(imports_function)或FC(imports_const)查找,如果没有找到再去FC(imports)中匹配。如果我们这样导入了一个函数:`use aa\bb\my_func;`,编译`my_func()`会在FC(imports_function)中根据"my_func"找到"aa\bb\my_func",从而使用完整的这个名称。 +可以看到,函数与常量的的补全逻辑只是优先用原始名称去FC(imports_function)或FC(imports_const)查找,如果没有找到再去FC(imports)中匹配。如果我们这样导入了一个函数:`use function aa\bb\my_func;`,编译`my_func()`会在FC(imports_function)中根据"my_func"找到"aa\bb\my_func",从而使用完整的这个名称。 ### 8.3.3 动态用法 前面介绍的这些命名空间的使用都是名称为CONST类型的情况,所有的处理都是在编译环节完成的,PHP是动态语言,能否动态使用命名空间呢?举个例子: diff --git a/README.md b/README.md index dbec699..566fac0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,13 @@ ## 反馈 [交流&吐槽](https://github.com/pangudashu/php7-internal/issues/3) [错误反馈](https://github.com/pangudashu/php7-internal/issues/2) - +## 纸质版 +
+