├── PHP源码学习笔记 ├── PHPBOOK │ ├── 00.目录.md │ ├── 01.PHP的生命周期.md │ ├── 02.PHP变量在内核中的实现.md │ ├── 03.内存管理.md │ ├── 04.配置编译环境.md │ ├── 05.第一个扩展.md │ ├── 06.函数的返回值.md │ ├── 07.函数的参数.md │ ├── 08.Array与HashTable.md │ ├── 09.PHP中的资源类型.md │ ├── 10.PHP中的面向对象上篇.md │ ├── 11.PHP中的面向对象下篇.md │ ├── 12.启动与终止的那点事.md │ ├── 13.ini配置文件.md │ ├── 14.流式访问.md │ ├── 15.流的实现.md │ ├── 16.有趣的流.md │ ├── 17.配置和链接.md │ ├── 18.扩展生成器.md │ ├── 19.设置宿主环境.md │ ├── 20.高级嵌入式.md │ └── README.md └── PHP内核探索 │ ├── 00.常用宏解释.md │ ├── 10.函数.md │ ├── 11.类与对象.md │ ├── 12.资源.md │ └── 13.Zend虚拟机.md ├── README.md ├── git.sh └── redis源码学习笔记 └── 01.内存管理.md /PHP源码学习笔记/PHPBOOK/00.目录.md: -------------------------------------------------------------------------------- 1 | 《PHP扩展开发及内核应用-学习笔记》目录 2 | === 3 | 4 | 1. [PHP的生命周期](01.PHP的生命周期.md) 5 | 2. [PHP变量在内核中的实现](02.PHP变量在内核中的实现.md) 6 | 3. [内存管理](03.内存管理.md) 7 | 4. [配置编译环境](04.配置编译环境.md) 8 | 5. [第一个扩展](05.第一个扩展.md) 9 | 6. [函数的返回值](06.函数的返回值.md) 10 | 7. [函数的参数](07.函数的参数.md) 11 | 8. [Array与HashTable](08.Array与HashTable.md) 12 | 9. [PHP中的资源类型](09.PHP中的资源类型.md) 13 | 10. [PHP中的面向对象上篇](10.PHP中的面向对象上篇.md) 14 | 11. [PHP中的面向对象下篇](11.PHP中的面向对象下篇.md) 15 | 12. [启动与终止的那点事](12.启动与终止的那点事.md) 16 | 13. [ini配置文件](13.ini配置文件.md) 17 | 14. [流式访问](14.流式访问.md) 18 | 15. [流的实现](15.流的实现.md) 19 | 16. [有趣的流](16.有趣的流.md) 20 | 17. [配置和链接](17.配置和链接.md) 21 | 18. [扩展生成器](18.扩展生成器.md) 22 | 19. [设置宿主环境](19.设置宿主环境.md) 23 | 20. [高级嵌入式](20.高级嵌入式.md) 24 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/01.PHP的生命周期.md: -------------------------------------------------------------------------------- 1 | PHP 的生命周期 2 | === 3 | 4 | 1.1 让我们从 SAPI 开始 5 | --- 6 | 7 | + SPAI (Server Application Programming Interface) 8 | > SAPI 就是 PHP 和外部环境的代理器。它把外部环境抽象后, 为内部的 PHP 提供一套固定的, 统一的接口, 使得 PHP 自身实现能够不受错综复杂的外部环境影响,保持一定的独立性。相关链接:[深入理解 Zend SAPIs](http://www.laruence.com/2008/08/12/180.html) 9 | 10 | + CLI (Command-line Interface) 11 | 12 | 1.2 PHP 的启动与终止 13 | --- 14 | 15 | + PHP 程序的启动与终止可以看作两个概念 16 | + 作为 Apache 的一个模块启动与终止 17 | + 当 Apache 分配一个页面请求的时候,PHP会有一次启动与终止 18 | 19 | + MINIT (Module Initialization) 20 | > PHP_MINIT_FUNCTION() 宏函数,这里定义的常量、类、资源等都会随着 Apache 常驻内存。 21 | 22 | + RINIT (Request Initialization) 23 | > PHP_RINIT_FUNCTION() 宏函数,将初始化在本次请求中会使用到的变量与 PHP 脚本中的变量。 24 | 25 | + RSHUTDOWN (Request Shutdown) 26 | > PHP_RSHUTDOWN_FUNCTION() 宏函数,会释放掉这次请求使用过的所有东西,包括变量表的所有变量、所有在这次请求中申请的内存等。 27 | 28 | + MSHUTDOWN (Module Shutdown) 29 | > PHP_MSHUTDOWN_FUNCTION() 宏函数,当 Apache 要停止时,会执行所有扩展中的 MSHUTDOWN 方法。 30 | 31 | 1.3 PHP 的生命周期 32 | --- 33 | + PHP 通过 SAPI 与宿主通信的四种方式 34 | + 直接以CLI/CGI模式调用 35 | + 多进程模式 36 | + 多线程模式 37 | + Embedded(嵌入式,在自己的 C 程序中调用 Zend Engine) 38 | 39 | + CLI/CGI 40 | > 这时 PHP 的生命周期完全在一个单独的请求中完成。虽然简单,不过我们以前提过的两种 init 和两种 shutdown 仍然都会被执行。 41 | 42 | + 多进程模式 43 | > 当 Apache 启动的时候,会立即把自己 fork 出好几个子进程,每一个进程都有自己独立的内存空间,也就代表了有自己独立的变量、函数等。因为是 fork 出来的,所以各个进程间的数据是彼此独立,不会受到外界的干扰。 44 | 45 | + 多线程模式 46 | > 在这种模式下,只有一个服务器进程在运行着,但会同时运行很多线程,这样可以减少一些资源开销,像 Module init 和 Module shutdown 就只需要运行一次就行了,一些全局变量也只需要初始化一次, 因为线程独具的特质,使得各个请求之间方便的共享一些数据成为可能。 47 | 48 | + Embed 49 | > 它可能随时嵌入到某个程序里,然后被当作脚本的一部分在一个请求的时候执行,控制权在PHP和原程序间来回传递。相关链接:[使用PHP Embed SAPI实现Opcodes查看器](http://www.laruence.com/2008/09/23/539.html) 50 | 51 | 1.4 线程安全 52 | --- 53 | 54 | + TSRM 55 | > Thread Safe Resource Management 56 | 57 | + 线程安全与非线程安全 58 | > 在一个没有线程的程序中,我们倾向于把全局变量声明在源文件的顶部,编辑器会自动的为它分配资源供我们在声明语句之下的程序逻辑中使用。 59 | > 但在一个多线程的程序中,如果我们需要每个线程都拥有自己独立的资源的话, 便需要为每个线程独立开辟出一个区域来存放它们各自的资源, 在使用资源的时候,每个线程便会只在自己的那块区域里找。 60 | 61 | + Thread-Safe Data Pools 62 | > 在扩展的 Module Init 里,扩展可以调用 ts_allocate_id() 来告诉 TRSM 自己需要多少资源。TRSM 接收后更新系统使用的资源,并得到一个指向刚分配的那份资源的 id。 63 | 64 | + 当不在线程环境时 65 | > 因为在 PHP 的线程安全构建中访问全局资源涉及到在线程数据池查找对应的偏移量,这是一些额外的负载,结果就是它比对应的非线程方式(直接从编译期已经计算好的真实的全局变量地址中取出数据)慢一些。 非线程构建还有进程隔离的优势,这样给定的请求碰到完全出乎意料的情况时,它也不会影响其他进程,即便是产生段错误也不会导致整个 webserver 瘫痪。 66 | 67 | + 访问全局变量 68 | > 当 PHP 因为 SAPI 需要或通过 enable-maintainer-zts 选项安装等原因以线程安全方式构建时,这个值会被自动的定义,并可以用一组 #ifdef ZTS 这样的指令集去测试它的值。 69 | 70 | + 即便你不需要线程也要考虑线程 71 | > 正常的PHP构建默认是关闭线程安全的,只有在被构建的sapi明确需要线程安全或线程安全在./configure阶段显式的打开时,才会以线程安全方式构建。 72 | > 当线程安全启用时,一个名为 tsrm_ls 的特殊指针被增加到了很多的内部函数原型中。这个指针允许 PHP 区分不同线程的数据。 73 | 74 | + 当ZTS禁用时,这些定义都被展开为空;当ZTS开启时,它们展开如下: 75 | ```c 76 | #define TSRMLS_D void ***tsrm_ls 77 | #define TSRMLS_DC , void ***tsrm_ls 78 | #define TSRMLS_C tsrm_ls 79 | #define TSRMLS_CC , tsrm_ls 80 | ``` 81 | 82 | + 寻回丢失的 tsrm_ls 83 | > 外部的库并不知道 php 的线程安全模型,解决方案是名为 TSRMLS_FETCH() 的 Zend 宏函数。将它放到代码片段的顶部,这个宏将执行给予当前线程上下文的查找,并定义本地的 tsrm_ls 指针拷贝, 这个宏可以在任何地方使用并且不用通过函数调用传递 tsrm_ls。 84 | > 要注意到这一点:TSRMLS_FETCH 调用需要一定的处理时间。这在单次迭代中并不明显,但是随着线程数增多,随着调用 TSRMLS_FETCH() 的点增多,扩展就会显现出这个瓶颈。因此,请谨慎的使用它。 85 | 86 | links 87 | --- 88 | 89 | + [目录](00.目录.md) 90 | + 上一节: 没有了 91 | + 下一节: [02.PHP变量在内核中的实现](02.PHP变量在内核中的实现.md) 92 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/02.PHP变量在内核中的实现.md: -------------------------------------------------------------------------------- 1 | PHP 变量在内核中的实现 2 | === 3 | 4 | 所有的编程语言都要提供一种数据的存储与检索机制,PHP 也不例外。其它语言大都需要在使用变量之前先定义,并且它的类型也是无法再次改变的,而 PHP 却允许自由的使用变量而无须提前定义,甚至可以随时随意的对已存在的变量转换成其它任何 PHP 支持的数据类型。当程序在运行的时候,PHP还会自动的根据需求转换变量的类型。 5 | 6 | 2.1 变量的类型 7 | --- 8 | 9 | + PHP在内核中是通过 zval 这个结构体来存储变量的,它的定义在 Zend/zend.h 文件里,由四个成员组成: 10 | ```c 11 | struct _zval_struct { 12 | zvalue_value value; /* 变量的值 */ 13 | zend_uint refcount__gc; 14 | zend_uchar type; /* 变量当前的数据类型 */ 15 | zend_uchar is_ref__gc; 16 | }; 17 | typedef struct _zval_struct zval; 18 | ``` 19 | 20 | + 保存变量值的 value 是 zvalue_value 类型(PHP5),它是一个 union,同样定义在了 Zend/zend.h 文件里: 21 | ```c 22 | typedef union _zvalue_value { 23 | long lval; /* long value */ 24 | double dval; /* double value */ 25 | struct { 26 | char *val; 27 | int len; 28 | } str; 29 | HashTable *ht; /* hash table value */ 30 | zend_object_value obj; 31 | } zvalue_value; 32 | ``` 33 | 34 | + PHP 的八种数据类型 35 | + IS_NULL 36 | + IS_BOOL 37 | + IS_LONG 38 | + IS_DOUBLE 39 | + IS_STRING 40 | + IS_ARRAY 41 | + IS_OBJECT 42 | + IS_RESOURCE 43 | 44 | + 检查变量类型的宏函数 45 | ```c 46 | #define Z_TYPE(zval) (zval).type 47 | #define Z_TYPE_P(zval_p) Z_TYPE(*zval_p) 48 | #define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp) 49 | ``` 50 | 51 | 2.2 变量的值 52 | --- 53 | 54 | + PHP内核提供了三个基础宏来方便对变量的值进行操作,这几个宏同样以Z_开头,并且P结尾和PP结尾的同上一节中的宏一样, 分别代表这参数是指针还是指针的指针。 55 | + 针对 IS_LONG 类型的 LAVL 组合 (Z_LVAL、Z_LAVL_P、Z_LAVL_PP) 56 | + 针对 IS_BOOL 类型的 BVAL 组合 (Z_BVAL、Z_BVAL_P、Z_BVAL_PP) 57 | + 针对 IS_DOUBLE 类型的 DVAL 组合 (Z_DVAL、Z_DVAL_P、Z_DVAL_PP) 58 | + 针对 IS_STRING 类型的 STRVAL 组合 (Z_STRVAL、Z_STRVAL_P、Z_STRVAL_PP) 59 | + 针对 IS_STRING 类型的 STRLEN 组合 (Z_STRLEN、Z_STRLEN_P、Z_STRLEN_PP) 60 | + 针对 IS_ARRAY 类型的 ARRVAL 组合 (Z_ARRVAL、Z_ARRVAL_P、Z_ARRVAL_PP) 61 | + IS_OBJECT 类型是一个复杂的结构体 (zend_object_value) 62 | + OBJ_HANDLE 返回 handle 标识符 63 | + OBJ_HT handle 表 64 | + OBJCE 定义类 65 | + OBJPROP HashTable 的属性 66 | + OBJ_HANDLER 在 OBJ_HT 中操作一个特殊的handler方法 67 | + IS_RESOURCE 类型是一个整数,使用 RESVAL 组合 (Z_RESVAL、Z_RESVAL_P、Z_RESVAL_PP) 68 | 69 | + 有关值操作的宏都定义在 ./Zend/zend_operators.h 文件里: 70 | ```c 71 | //操作整数的 72 | #define Z_LVAL(zval) (zval).value.lval 73 | #define Z_LVAL_P(zval_p) Z_LVAL(*zval_p) 74 | #define Z_LVAL_PP(zval_pp) Z_LVAL(**zval_pp) 75 | 76 | //操作IS_BOOL布尔型的 77 | #define Z_BVAL(zval) ((zend_bool)(zval).value.lval) 78 | #define Z_BVAL_P(zval_p) Z_BVAL(*zval_p) 79 | #define Z_BVAL_PP(zval_pp) Z_BVAL(**zval_pp) 80 | 81 | //操作浮点数的 82 | #define Z_DVAL(zval) (zval).value.dval 83 | #define Z_DVAL_P(zval_p) Z_DVAL(*zval_p) 84 | #define Z_DVAL_PP(zval_pp) Z_DVAL(**zval_pp) 85 | 86 | //操作字符串的值和长度的 87 | #define Z_STRVAL(zval) (zval).value.str.val 88 | #define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p) 89 | #define Z_STRVAL_PP(zval_pp) Z_STRVAL(**zval_pp) 90 | 91 | #define Z_STRLEN(zval) (zval).value.str.len 92 | #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p) 93 | #define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp) 94 | 95 | #define Z_ARRVAL(zval) (zval).value.ht 96 | #define Z_ARRVAL_P(zval_p) Z_ARRVAL(*zval_p) 97 | #define Z_ARRVAL_PP(zval_pp) Z_ARRVAL(**zval_pp) 98 | 99 | //操作对象的 100 | #define Z_OBJVAL(zval) (zval).value.obj 101 | #define Z_OBJVAL_P(zval_p) Z_OBJVAL(*zval_p) 102 | #define Z_OBJVAL_PP(zval_pp) Z_OBJVAL(**zval_pp) 103 | 104 | #define Z_OBJ_HANDLE(zval) Z_OBJVAL(zval).handle 105 | #define Z_OBJ_HANDLE_P(zval_p) Z_OBJ_HANDLE(*zval_p) 106 | #define Z_OBJ_HANDLE_PP(zval_p) Z_OBJ_HANDLE(**zval_p) 107 | 108 | #define Z_OBJ_HT(zval) Z_OBJVAL(zval).handlers 109 | #define Z_OBJ_HT_P(zval_p) Z_OBJ_HT(*zval_p) 110 | #define Z_OBJ_HT_PP(zval_p) Z_OBJ_HT(**zval_p) 111 | 112 | #define Z_OBJCE(zval) zend_get_class_entry(&(zval) TSRMLS_CC) 113 | #define Z_OBJCE_P(zval_p) Z_OBJCE(*zval_p) 114 | #define Z_OBJCE_PP(zval_pp) Z_OBJCE(**zval_pp) 115 | 116 | #define Z_OBJPROP(zval) Z_OBJ_HT((zval))->get_properties(&(zval) TSRMLS_CC) 117 | #define Z_OBJPROP_P(zval_p) Z_OBJPROP(*zval_p) 118 | #define Z_OBJPROP_PP(zval_pp) Z_OBJPROP(**zval_pp) 119 | 120 | #define Z_OBJ_HANDLER(zval, hf) Z_OBJ_HT((zval))->hf 121 | #define Z_OBJ_HANDLER_P(zval_p, h) Z_OBJ_HANDLER(*zval_p, h) 122 | #define Z_OBJ_HANDLER_PP(zval_p, h) Z_OBJ_HANDLER(**zval_p, h) 123 | 124 | #define Z_OBJDEBUG(zval,is_tmp) (Z_OBJ_HANDLER((zval),get_debug_info)? \ 125 | Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&is_tmp TSRMLS_CC): \ 126 | (is_tmp=0,Z_OBJ_HANDLER((zval),get_properties)?Z_OBJPROP(zval):NULL)) 127 | #define Z_OBJDEBUG_P(zval_p,is_tmp) Z_OBJDEBUG(*zval_p,is_tmp) 128 | #define Z_OBJDEBUG_PP(zval_pp,is_tmp) Z_OBJDEBUG(**zval_pp,is_tmp) 129 | 130 | //操作资源的 131 | #define Z_RESVAL(zval) (zval).value.lval 132 | #define Z_RESVAL_P(zval_p) Z_RESVAL(*zval_p) 133 | #define Z_RESVAL_PP(zval_pp) Z_RESVAL(**zval_pp) 134 | ``` 135 | 136 | 2.3 创建 PHP 变量 137 | --- 138 | 139 | + MAKE_STD_ZVAL(pzv) 140 | > 这个宏会用内核的方式来申请一块内存并将其地址付给 pzv,并初始化它的 refcount__gc 和 is_ref__gc 两个属性,它不但会自动的处理内存不足问题,还会在内存中选个最优的位置来申请。 141 | 142 | + ALLOC_INIT_ZVAL(pzv) 143 | > 类似 MAKE_STD_ZVAL() 宏,唯一的不同便是它会将 pzv 所指的 zval 的类型设置为 IS_NULL。 144 | 145 | + 内核中提供一些宏来简化操作,可以只用一步便设置好 zval 的类型和值: 146 | ```c 147 | ZVAL_NULL(pvz) /* 将 pzv 所指的 zavl 设置为 IS_NULL 类型 */ 148 | ZVAL_BOOL(pzv, b) /* 将 pzv 所指的 zval 设置为 IS_BOOL 类型,值是 b */ 149 | ZVAL_FALSE(pzv) /* 将 pzv 所指的 zval 设置为 IS_BOOL 类型,值是 false */ 150 | ZVAL_LONG(pzv, l) /* 将 pzv 所指的 zval 设置为 IS_LONG 类型,值是 l */ 151 | ZVAL_DOUBLE(pzv, d) /* 将 pzv 所指的 zval 设置为 IS_DOUBLE 类型,值是 d */ 152 | ZVAL_STRINGL(pzv,str,len,dup) /* str 和len 两个参数分别对应内核中保存了字符串的地址和它的长度,dup 参数指明了该字符串是否需要被复制。值为 1 将先申请一块新内存并赋值该字符串,然后把新内存的地址复制给pzv,为 0 时则是直接把 str 的地址赋值给 zval */ 153 | ZVAL_STRING(pzv, str, dup) /* 与 ZVAL_STRINGL 的区别:如果你想在某一位置截取该字符串或已经知道了这个字符串的长度,那么可以使用宏 ZVAL_STRINGL(zval, string, length, duplicate),它显式的指定字符串长度,而不是使用 strlen()。这个宏用该字符串长度作为参数,它是二进制安全的,而且速度也比 ZVAL_STRING 快,因为少了个 strlen() */ 154 | ZVAL_RESOURCE(pzv, res) /* 这个宏约等于 ZVAL_LONG,PHP 中的资源类型的值其实就是一个整数,所以 ZVAL_RESOURCE 和 ZVAL_LONG 的工作差不多, 只不过它会把 zval 的类型设置为 IS_RESOURCE */ 155 | ``` 156 | 157 | 2.4 变量的存储方式 158 | --- 159 | 160 | + _zend_execution_globals 161 | > 户在 PHP 中定义的变量都可以在一个 HashTable 中找到,当 PHP 中定义了一个变量,内核会自动的把它的信息储存到一个用 HashTable 实现的符号表里。 162 | > 全局作用域的符号表是在调用扩展的 RINIT 方法(一般都是 MINIT 方法里)前创建的,并在 RSHUTDOWN 方法执行后自动销毁。 163 | 164 | + _zend_executor_globals 定义在 Zend/zend_globals.h 文件中 165 | ```c 166 | struct _zend_executor_globals { 167 | ... 168 | HashTable symbol_table; 169 | HashTable *active_symbol_table; 170 | ... 171 | }; 172 | ``` 173 | + symbol_table 174 | > 可以通过 EG(symbol_table) 宏来访问,代表着 PHP 的全局变量,如$GLOBALS,从根本上来讲,$GLOBALS 是 EG(symbol_table) 的一层封装。 175 | 176 | + active_symbol_table 177 | > 可以通过 EG(active_symbol_table) 的方法来访问,代表的是处于当前作用域的变量符号表。 178 | 179 | + 一个变量赋值的例子 180 | ```c 181 | { 182 | zval *fooval; 183 | 184 | MAKE_STD_ZVAL(fooval); 185 | ZVAL_STRING(fooval, "bar", 1); 186 | ZEND_SET_SYMBOL( EG(active_symbol_table) , "foo" , fooval); 187 | } 188 | ``` 189 | 190 | 2.5 变量的检索 191 | --- 192 | 193 | + zend_hash_find() 194 | > zend_hash_find() 函数是内核提供的操作 HashTable 的 API 之一,用来找到当前某个作用域下用户已经定义好的变量。 195 | 196 | + HashTable 197 | > 一个 HashTable 有很多元素,在内核里叫做 bucket。然而每个 bucket 的大小是固定的, 所以如果我们想在 bucket 里存储任意数据时,最好的办法便是申请一块内存保存数据, 然后在 bucket 里保存它的指针。 198 | 199 | 2.6 类型转换 200 | --- 201 | 202 | + convert_to_*() 类型转换系列函数 203 | ```c 204 | ZEND_API void convert_to_string(zval *op); 205 | ZEND_API void convert_to_long(zval *op); 206 | ZEND_API void convert_to_double(zval *op); 207 | ZEND_API void convert_to_null(zval *op); 208 | ZEND_API void convert_to_boolean(zval *op); 209 | ZEND_API void convert_to_array(zval *op); 210 | ZEND_API void convert_to_object(zval *op); 211 | ``` 212 | 213 | + 特殊的转换函数 214 | ```c 215 | convert_to_string() /* 其实是一个宏函数,调用的另外一个函数 */ 216 | convert_to_resource() /* 因为资源的值在用户层面上,根本就没有意义,内核不会对它的值(不是指那个数字)进行转换 */ 217 | ``` 218 | 219 | links 220 | --- 221 | 222 | + [目录](00.目录.md) 223 | + 上一节: [01.PHP的生命周期](01.PHP的生命周期.md) 224 | + 下一节: [03.内存管理](03.内存管理.md) 225 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/03.内存管理.md: -------------------------------------------------------------------------------- 1 | 内存管理 2 | === 3 | 4 | 3.1 内存管理 5 | --- 6 | 7 | + strdup() 函数用来复制一个字符串 8 | > 注意:在 PHP 内核中,大多数情况下都不应改直接使用 C 语言中自带着 malloc、free、strdup、realloc、calloc 等操作内存的函数,而应使用内核提供的操作内存的函数,这样可以由内核整体统一的来管理内存。 9 | 10 | + Free the Mallocs 11 | > 在书写代码时需要保证在层层嵌套调用中对某块内存的使用都是正确的,且会及时释放的。 12 | 13 | + 错误处理 14 | > 为了实现从用户端(PHP语言中)跳出,需要使用一种方法来完全跳出一个活动请求。 15 | > 这个功能是在内核中实现的:在一个请求的开始设置一个跳出地址,然后在任何 die() 或 exit() 调用或在遇到任何关键错误 (E_ERROR) 时执行一个 longjmp() 以跳转到该跳出地址。 16 | > 当 php_error_docref 函数被调用的时候便会触发内核中的错误处理机制,根据错误级别来决定是否调用 longjmp 来终止当前请求并退出 call_function 函数,从而 efree 函数便永远不会被执行了。 17 | 18 | + Zend 内存管理器 19 | > Zend Memory Manager,简称 ZendMM、ZMM。 20 | > 除了提供隐式的内存清除功能之外,ZendMM 还能够根据 php.ini 中 memory_limit 设置来控制每一次内存请求行为。 21 | 22 | + void *estrndup(void *ptr,int len) 23 | > 此函数能够分配 len+1 个字节的内存并且从 ptr 处复制 len 个字节到最新分配的块。 24 | 25 | 3.2 引用计数 26 | --- 27 | 28 | + 写时复制机制 29 | ```c 30 | $a = 1; 31 | $b = $a; 32 | $b += 5; 33 | ``` 34 | > 内核首先查看 refcount__gc 属性,如果它大于 1 则为这个变化的变量从原 zval 结构中复制出一份新的专属于 $b 变量的 zval 来,并改变其值。 35 | 36 | + Change on Write 37 | ```c 38 | $a = 1; 39 | $b = &$a; 40 | $b += 5; 41 | ``` 42 | > 尽管它的 refcount 等于 2,但是因为它的 is_ref 等于 1,所以也不会被复制,内核会直接的修改这个 zval 的值。 43 | 44 | + Separation Anxiety 45 | ```c 46 | $a = 1; 47 | $b = $a; 48 | $c = &$a; 49 | ``` 50 | > 在这种情况下,变量的值必须分离成两份完全独立的存在。$a 与 $c 共用一个 zval,$b 自己用一个 zval,尽管他们拥有同样的值,但是必须至少通过两个 zval 来实现。 51 | 52 | links 53 | --- 54 | 55 | + [目录](00.目录.md) 56 | + 上一节: [02.PHP变量在内核中的实现](02.PHP变量在内核中的实现.md) 57 | + 下一节: [04.配置编译环境](04.配置编译环境.md) 58 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/04.配置编译环境.md: -------------------------------------------------------------------------------- 1 | 配置编译环境 2 | === 3 | 4 | 4.1 编译前的准备 5 | --- 6 | 7 | + *nix Tools 8 | ```shell 9 | gcc --version 10 | ``` 11 | 12 | + 不同版本系统安装 GCC 13 | + yum install gcc 14 | + apt-get install gcc 15 | + pkg-add -r gcc 16 | + emerge gcc. 17 | 18 | + 其他需要的程序 19 | + make 20 | + autoconf 21 | + automake 22 | + libtool 23 | 24 | + checkout PHP 源码 25 | ```shell 26 | svn checkout https://svn.php.net/repository/php/php-src --depth immediates php-src 27 | cd php-src 28 | ``` 29 | + 也可以 checkout 特定的版本 30 | + PHP 5.3: svn checkout https://svn.php.net/repository/php/php-src/branches/PHP_5_3 php-src-5.3 31 | + PHP 5.4: svn checkout https://svn.php.net/repository/php/php-src/branches/PHP_5_4 php-src-5.4 32 | + PHP HEAD: svn checkout https://svn.php.net/repository/php/php-src/trunk php-src-trunk 33 | 34 | + Git 获取 PHP 源码 35 | ```shell 36 | git clone https://github.com/php/php-src.git 37 | ``` 38 | 39 | + Git 获取特定版本 40 | + PHP 5.3: git checkout PHP-5.3 41 | + PHP 5.4: git checkout PHP-5.4 42 | + PHP HEAD: git checkout master 43 | 44 | 4.2 PHP编译前的 config 配置 45 | --- 46 | 47 | PHP编译前的 configure 有两个特殊的选项,打开它们对我们开发 PHP 扩展或者进行 PHP 嵌入式开发时非常有帮助。但是当我们正常使用 PHP 的时候,则不应该开启这两个选项。 48 | 49 | + --enable-debug 50 | > 激活调试模式。它将激活PHP源码中几个非常关键的函数,最典型的功能便是在每一个请求结束后给出这一次请求中内存的泄漏情况。 51 | 52 | + --enable-maintainer-zts 53 | > 激活 php 的线程安全机制 (Thread Safe Resource Manager(TSRM)/Zend Thread Safety(ZTS)),使我们开发出的程序是线程安全的。 54 | 55 | + --enable-embed 56 | > 主要用在做 php 的嵌入式开发的场景中。 57 | 58 | 4.3 Unix/Linux平台下的编译 59 | --- 60 | 61 | + 编译 PHP5.3 例子 62 | 63 | ```shell 64 | cd /php-5.3 65 | ./configure --prefix=/walu/php/ --enable-debug --enable-maintainer-zts 66 | make 67 | make test 68 | make clean //自愿执行,非必须。 69 | ``` 70 | 71 | links 72 | --- 73 | 74 | + [目录](00.目录.md) 75 | + 上一节: [03.内存管理](03.内存管理.md) 76 | + 下一节: [05.第一个扩展](05.第一个扩展.md) 77 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/05.第一个扩展.md: -------------------------------------------------------------------------------- 1 | 第一个扩展 2 | === 3 | 4 | 5.1 一个扩展的基本结构 5 | --- 6 | 7 | + 配置文件 8 | + PHP_ARG_ENABLE 函数有三个参数:第一个参数是扩展名(注意不用加引号),第二个参数是当运行 ./configure 脚本时显示的内容,最后一个参数则是在调用 ./configure --help 时显示的帮助信息。 9 | > enable 多代表不依赖外部库便可以直接编译,而 with 大多需要依赖于第三方的 lib。 10 | + 如果扩展使用了多个文件,便可以将这多个文件名罗列在函数的参数里。 11 | ```shell 12 | PHP_NEW_EXTENSION(sample, sample.c sample2.c sample3.c, $ext_shared) // 最后的 $ext_shared 参数用来声明这个扩展不是一个静态模块,而是在 php 运行时动态加载的。 13 | ``` 14 | 15 | 5.2 编译我们的扩展 16 | --- 17 | 18 | + 在 *nix 下编译 19 | + 根据 config.m4 文件生成一个 configure 脚本、Makefile 等文件,这一步有 phpize 来做。 20 | 21 | + 加载扩展 22 | ```shell 23 | extension_dir=/usr/local/lib/php/modules/ 24 | extension=extname.so 25 | ``` 26 | 27 | 5.3 静态编译 28 | --- 29 | 30 | + 在 *nix 上执行静态编译 31 | + 首先需要使用 buildconf 命令生成新的 configure 脚本。 32 | ``` 33 | ./buildconf --force 34 | ``` 35 | + 接下来需要重新走一遍 PHP 的编译过程,便可以把扩展以静态编译的方式加入到 PHP 主程序中了。不要忘记使用 --enable-extname 参数开启扩展。 36 | 37 | 5.4 编写函数 38 | --- 39 | 40 | + ZEND_FUNCTION() 宏函数 41 | + 相关宏定义 42 | ```c 43 | #define PHP_FUNCTION ZEND_FUNCTION 44 | 45 | #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) 46 | #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) 47 | #define ZEND_FN(name) zif_##name 48 | ``` 49 | + zif 是 zend internal function 的意思,zif_ 前缀是可供PHP语言调用的函数在 C 语言中的函数名称前缀。 50 | + ZEND_FE() 宏 51 | ```c 52 | #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) 53 | #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, 54 | ``` 55 | + Zend Internal Functions 56 | > 需要手动来为扩展中的函数命名,这一步操作通过 ZEND_NAMED_FUNCTION(diy_walu_hello) 来代替 ZEND_FUNCTION(hello_hello)。前者由我们指定名称,后者自己加上前缀。 57 | > 并且用 ZEND_NAMED_FE() 宏来代替 ZEND_FE() 宏。 58 | 59 | + Function Aliases 60 | + ZEND_NAMED_FE 宏 61 | ```c 62 | #define ZEND_NAMED_FE(zend_name, name, arg_info) ZEND_FENTRY(zend_name, name, arg_info, 0) 63 | ``` 64 | + ZEND_FALIAS 宏 65 | ```c 66 | #define ZEND_FALIAS(name, alias, arg_info) ZEND_FENTRY(name, ZEND_FN(alias), arg_info, 0) 67 | ``` 68 | 69 | links 70 | --- 71 | 72 | + [目录](00.目录.md) 73 | + 上一节: [04.配置编译环境](04.配置编译环境.md) 74 | + 下一节: [06.函数的返回值](06.函数的返回值.md) 75 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/06.函数的返回值.md: -------------------------------------------------------------------------------- 1 | 函数的返回值 2 | === 3 | 4 | PHP 语言中函数的返回值是通过 return 来完成的,C语言也一样使用return关键字。 5 | 6 | 6.1 一个特殊的参数:return_value 7 | --- 8 | 9 | + INTERNAL_FUNCTION_PARAMETERS 宏 10 | 11 | ```c 12 | #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC 13 | ``` 14 | 15 | + int ht 16 | + zval *return_value,我们在函数内部修改这个指针,函数执行完成后,内核将把这个指针指向的zval返回给用户端的函数调用者。 17 | + zval **return_value_ptr,return_value 指针 18 | + zval *this_ptr,如果此函数是一个类的方法,那么这个指针的含义和PHP语言中$this变量差不多。 19 | + int return_value_used,代表用户端在调用此函数时有没有使用到它的返回值。 20 | 21 | + 与 return_value 有关的宏 22 | + RETVAL_* 宏 23 | ```c 24 | //这些宏都定义在Zend/zend_API.h文件里 25 | #define RETVAL_RESOURCE(l) ZVAL_RESOURCE(return_value, l) 26 | #define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b) 27 | #define RETVAL_NULL() ZVAL_NULL(return_value) 28 | #define RETVAL_LONG(l) ZVAL_LONG(return_value, l) 29 | #define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d) 30 | #define RETVAL_STRING(s, duplicate) ZVAL_STRING(return_value, s, duplicate) 31 | #define RETVAL_STRINGL(s, l, duplicate) ZVAL_STRINGL(return_value, s, l, duplicate) 32 | #define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value) 33 | #define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor) 34 | #define RETVAL_FALSE ZVAL_BOOL(return_value, 0) 35 | #define RETVAL_TRUE ZVAL_BOOL(return_value, 1) 36 | ``` 37 | 38 | + RETURN_* 宏 39 | ```c 40 | //这些宏都定义在Zend/zend_API.h文件里 41 | #define RETURN_RESOURCE(l) { RETVAL_RESOURCE(l); return; } 42 | #define RETURN_BOOL(b) { RETVAL_BOOL(b); return; } 43 | #define RETURN_NULL() { RETVAL_NULL(); return;} 44 | #define RETURN_LONG(l) { RETVAL_LONG(l); return; } 45 | #define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; } 46 | #define RETURN_STRING(s, duplicate) { RETVAL_STRING(s, duplicate); return; } 47 | #define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; } 48 | #define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; } 49 | #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } 50 | #define RETURN_FALSE { RETVAL_FALSE; return; } 51 | #define RETURN_TRUE { RETVAL_TRUE; return; } 52 | ``` 53 | + 不返回值可以么 54 | > zend internal function 的形参中有一个比较常用的名 为return_value_used 的参数,它用来标志这个函数的返回值在用户端有没有用到。 55 | 56 | + 以引用的形式返回值 57 | > return_value_ptr 是定义 zend internal function 时的另外一个重要参数,它是一个 zval** 类型的指针,并且指向函数的返回值。调用 zval_ptr_dtor() 函数后,默认的return_value 便被废弃了。 58 | > arginfo 是一种特殊的结构体,用来提前向内核告知此函数具有的一些特定的性质。 59 | 60 | 6.2 引用与函数的执行结果 61 | --- 62 | 63 | + 运行时传递引用:Call-time Pass-by-ref 64 | > 在传递参数的时候使用 & 操作符,便可以传递变量的引用过去,而不是 copy 一份。 65 | 66 | + 编译时的传递引用Compile-time Pass-by-ref 67 | + arginfo 结构体 68 | 69 | ```c 70 | typedef struct _zend_arg_info { 71 | const char *name; /* 参数的名称*/ 72 | zend_uint name_len; /* 参数名称的长度*/ 73 | const char *class_name; /* 类名 */ 74 | zend_uint class_name_len; /* 类名长度*/ 75 | zend_bool array_type_hint; /* 数组类型提示 */ 76 | zend_bool allow_null; /* 是否允许为NULL */ 77 | zend_bool pass_by_reference; /* 是否引用传递 */ 78 | zend_bool return_reference; /* 返回值是否为引用形式 */ 79 | int required_num_args; /* 必要参数的数量 */ 80 | } zend_arg_info; 81 | ``` 82 | + 生成 zend_arg_info 结构的数组的宏 83 | 84 | ```c 85 | #define ZEND_BEGIN_ARG_INFO(name, pass_rest_by_reference) ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, ZEND_RETURN_VALUE, -1) 86 | #define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ 87 | static const zend_arg_info name[] = { \ 88 | { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, 89 | #define ZEND_ARG_INFO(pass_by_ref, name) { #name, sizeof(#name)-1, NULL, 0, 0, 0, pass_by_ref, 0, 0 }, 90 | #define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, NULL, 0, 0, 0, pass_by_ref, 0, 0 }, 91 | #define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, sizeof(#name)-1, #classname, sizeof(#classname)-1, 0, allow_null, pass_by_ref, 0, 0 }, 92 | #define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, sizeof(#name)-1, NULL, 0, 1, allow_null, pass_by_ref, 0, 0 }, 93 | #define ZEND_END_ARG_INFO() }; 94 | ``` 95 | 96 | links 97 | --- 98 | 99 | + [目录](00.目录.md) 100 | + 上一节: [05.第一个扩展](05.第一个扩展.md) 101 | + 下一节: [07.函数的参数](07.函数的参数.md) 102 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/07.函数的参数.md: -------------------------------------------------------------------------------- 1 | 函数的参数 2 | === 3 | 4 | 7.1 zend_parse_parameters 5 | --- 6 | 7 | zend_parse_parameters() 函数的前几个参数用内核里宏来生成,形式为:ZEND_NUM_ARGS() TSRMLS_CC,注意两者之间有个空格,但是没有逗号。从名字可以看出,ZEND_NUM_ARGS() 代表着参数的个数。 8 | 紧接着需要传递给 zend_parse_parameters() 函数的参数是一个用于格式化的字符串,就像 printf 的第一个参数一样。 9 | 10 | + type_spec 是格式化字符串,其常见的含义如下 11 | ```c 12 | b Boolean 13 | l Integer 整型 14 | d Floating point 浮点型 15 | s String 字符串 16 | r Resource 资源 17 | a Array 数组 18 | o Object instance 对象 19 | O Object instance of a specified type 特定类型的对象 20 | z Non-specific zval 任意类型 21 | Z zval** 类型 22 | f 表示函数、方法名称 23 | ``` 24 | 25 | + 对应的 C 语言里的数据类型 26 | ```c 27 | b zend_bool 28 | l long 29 | d double 30 | s char*, int 前者接收指针,后者接收长度 31 | r zval* 32 | a zval* 33 | o zval* 34 | O zval*, zend_class_entry* 35 | z zval* 36 | Z zval** 37 | ``` 38 | 39 | + 函数参数的默认值 40 | ```c 41 | | 它之前的参数都是必须的,之后的都是非必须的,也就是有默认值的。 42 | ! 如果接收了一个PHP语言里的 null 变量,则直接把其转成C语言里的 NULL,而不是封装成 IS_NULL 类型的 zval。 43 | / 如果传递过来的变量与别的变量共用一个 zval,而且不是引用,则进行强制分离,新的 zval 的 is_ref__gc==0, and refcount__gc==1。 44 | ``` 45 | 46 | + zend_get_arguments() 47 | > 如果想让扩展能够兼容老版本 的PHP,或者只想以 zval* 为载体来接收参数,便可以使用 zend_get_parameters() 函数来接收参数。 48 | > zend_get_parameters() 与zend_parse_parameters() 不同,从名字上便可以看出,它直接获取而不做解析。 49 | 50 | + 可变参数 51 | > zend_get_parameter_** 函数,专门用来解决参数很多或者无法提前知道参数数目的问题,如:zend_get_parameters_array_ex。 52 | 53 | 7.2 Arg Info 与类型绑定 54 | --- 55 | 56 | 通过 arg info 的方式来实现类型绑定的功能只对 ZE2 有效,也就是PHP5+。如果想在 PHP4 上实现相应的功能,那需要用 zend_get_parameters() 函数来接收参数,然后通过 Z_TYPE_P() 宏函数来检测参数的类型或者通过 convert_to_type() 函数进行类型转换。 57 | 58 | links 59 | --- 60 | 61 | + [目录](00.目录.md) 62 | + 上一节: [06.函数的返回值](06.函数的返回值.md) 63 | + 下一节: [08.Array与HashTable](08.Array与HashTable.md) 64 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/08.Array与HashTable.md: -------------------------------------------------------------------------------- 1 | Array 与 HashTable 2 | === 3 | 4 | 8.1 数组(C中的)与链表 5 | --- 6 | 7 | + 数组 8 | > 这里用的不是 Array 而是 Vector,可能指的是 C++ 里的 Vector,它与数组几乎是完全一样的,唯一的不同便是可以实现动态存储。 9 | > 使用数组最大的好处便是速度,读写都可以在 O(1) 内完成,因为它每个元素的大小都是一致的,只要知道下标,便可以瞬间计算出其对应的元素在内存中的位置,从而直接取出或者写入。 10 | 11 | + 链表 12 | > 链表中的每一个元素都至少有两个元素,一个指向它的下一个元素,一个用来存放它自己的数据。 13 | > 链表可以被用来实现 FIFO 模式,达到先进者先出的目的。 14 | 15 | + HashTable 16 | > HashTable 既具有双向链表的优点,同时具有能与数据匹敌的操作性能,这个数据结构几乎是 PHP 内核实现的基础。 17 | 18 | 8.2 操作 HashTable 的 API 19 | --- 20 | 21 | + 创建 HashTable 22 | + 创建并初始化一个 HashTable 使用 zend_hash_init 函数即可,它的定义如下: 23 | 24 | ```c 25 | int zend_hash_init( 26 | HashTable *ht, // 指针,指向一个 HashTable 27 | uint nSize, // HashTable 可以拥有的元素的最大数量,永远是 2 的次方,计算方式:nSize = pow(2, ceil(log(nSize, 2))) 28 | hash_func_t pHashFunction, // 是早期 Zend Engine 中的一个参数,为了兼容没有去掉它,所以直接赋成 NULL。 29 | dtor_func_t pDestructor, // 代表着一个回调函数,当删除或者修改 HashTable 中其中一个元素时候便会调用。 30 | zend_bool persistent // 如果为 true,HashTable 将永远存在于内存中,而不会在 RSHUTDOWN 阶段自动被注销掉。 31 | ); 32 | ``` 33 | 34 | + 添加与修改 35 | 36 | ```c 37 | int zend_hash_add( 38 | HashTable *ht, //待操作的ht 39 | char *arKey, //索引,如"my_key" 40 | uint nKeyLen, //字符串索引的长度,如6 41 | void **pData, //要插入的数据,注意它是void **类型的。int *p,i=1;p=&i,pData=&p;。 42 | uint nDataSize, 43 | void *pDest //如果操作成功,则pDest=*pData; 44 | ); 45 | 46 | int zend_hash_update( 47 | HashTable *ht, 48 | char *arKey, 49 | uint nKeyLen, 50 | void *pData, 51 | uint nDataSize, 52 | void **pDest 53 | ); 54 | 55 | int zend_hash_index_update( 56 | HashTable *ht, 57 | ulong h, 58 | void *pData, 59 | uint nDataSize, 60 | void **pDest 61 | ); 62 | 63 | int zend_hash_next_index_insert( 64 | HashTable *ht, 65 | void *pData, 66 | uint nDataSize, 67 | void **pDest 68 | ); 69 | ``` 70 | 71 | + 查找 72 | + 读取 73 | 74 | ```c 75 | int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength,void **pData); // 字符串索引 76 | int zend_hash_index_find(HashTable *ht, ulong h, void **pData); // 数字索引 77 | ``` 78 | + 检测 key 是否存在 79 | 80 | ```c 81 | int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLen); // 字符串索引 82 | int zend_hash_index_exists(HashTable *ht, ulong h); // 数字索引 83 | ``` 84 | 85 | + 提速 86 | 87 | ```c 88 | ulong zend_get_hash_value(char *arKey, uint nKeyLen); 89 | ``` 90 | 91 | > 当需要对同一个字符串的 key 进行许多操作时候,比如先检测有没,然后插入,然后修改等等,这时便可以使用 zend_get_hash_value 函数来对我们的操作进行加速!这个函数的返回值可以和 quick 系列函数使用,达到加速的目的。 92 | 93 | ```c 94 | int zend_hash_quick_add( 95 | HashTable *ht, 96 | char *arKey, 97 | uint nKeyLen, 98 | ulong hashval, 99 | void *pData, 100 | uint nDataSize, 101 | void **pDest 102 | ); 103 | 104 | int zend_hash_quick_update( 105 | HashTable *ht, 106 | char *arKey, 107 | uint nKeyLen, 108 | ulong hashval, 109 | void *pData, 110 | uint nDataSize, 111 | void **pDest 112 | ); 113 | 114 | int zend_hash_quick_find( 115 | HashTable *ht, 116 | char *arKey, 117 | uint nKeyLen, 118 | ulong hashval, 119 | void **pData 120 | ); 121 | 122 | int zend_hash_quick_exists( 123 | HashTable *ht, 124 | char *arKey, 125 | uint nKeyLen, 126 | ulong hashval 127 | ); 128 | ``` 129 | + 复制与合并(Copy And Merge) 130 | 131 | ```c 132 | void zend_hash_copy( 133 | HashTable *target, 134 | HashTable *source, 135 | copy_ctor_func_t pCopyConstructor, 136 | void *tmp, 137 | uint size 138 | ); 139 | 140 | void zend_hash_merge( 141 | HashTable *target, 142 | HashTable *source, 143 | copy_ctor_func_t pCopyConstructor, 144 | void *tmp, 145 | uint size, 146 | int overwrite 147 | ); 148 | 149 | typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht,void *source_data, zend_hash_key *hash_key, void *pParam); 150 | void zend_hash_merge_ex( 151 | HashTable *target, 152 | HashTable *source, 153 | copy_ctor_func_t pCopyConstructor, 154 | uint size, 155 | merge_checker_func_t pMergeSource, 156 | void *pParam 157 | ); 158 | ``` 159 | 160 | + 遍历 161 | 162 | ```c 163 | typedef int (*apply_func_t)(void *pDest TSRMLS_DC); 164 | void zend_hash_apply(HashTable *ht,apply_func_t apply_func TSRMLS_DC); 165 | 166 | typedef int (*apply_func_arg_t)(void *pDest,void *argument TSRMLS_DC); 167 | void zend_hash_apply_with_argument(HashTable *ht,apply_func_arg_t apply_func, void *data TSRMLS_DC); 168 | ``` 169 | + 上述函数对传给它们的回调函数的返回值有一个共同的约定: 170 | 171 | ```c 172 | ZEND_HASH_APPLY_KEEP // 结束当前请求,进入下一个循环。与 PHP 语言 forech 语句中的一次循环执行完毕或者遇到 continue 关键字的作用一样。 173 | ZEND_HASH_APPLY_STOP // 跳出,与 PHP 语言 forech 语句中的 break 关键字的作用一样。 174 | ZEND_HASH_APPLY_REMOVE // 删除当前的元素,然后继续处理下一个。相当于在PHP语言中:unset($foo[$key]);continue; 175 | ``` 176 | + 向前遍历 HashTable 177 | 178 | ```c 179 | /* reset() */ 180 | void zend_hash_internal_pointer_reset(HashTable *ht); 181 | 182 | /* key() */ 183 | int zend_hash_get_current_key(HashTable *ht,char **strIdx, unit *strIdxLen,ulong *numIdx, zend_bool duplicate); 184 | 185 | /* current() */ 186 | int zend_hash_get_current_data(HashTable *ht, void **pData); 187 | 188 | /* next()/each() */ 189 | int zend_hash_move_forward(HashTable *ht); 190 | 191 | /* prev() */ 192 | int zend_hash_move_backwards(HashTable *ht); 193 | 194 | /* end() */ 195 | void zend_hash_internal_pointer_end(HashTable *ht); 196 | 197 | /* 其他的...... */ 198 | int zend_hash_get_current_key_type(HashTable *ht); 199 | int zend_hash_has_more_elements(HashTable *ht); 200 | ``` 201 | 202 | + zend_hash_get_current_key() 函数的返回值 203 | 204 | ```c 205 | HASH_KEY_IS_STRING // 当前元素的索引是字符串类型的。 206 | HASH_KEY_IS_LONG // 当前元素的索引是数字型的。 207 | HASH_KEY_NON_EXISTANT // HashTable 中的内部指针已经移动到尾部,不指向任何元素。 208 | ``` 209 | 210 | + 删除 211 | 212 | ```c 213 | int zend_hash_del(HashTable *ht, char *arKey, uint nKeyLen); // 删除字符串索引数据 214 | int zend_hash_index_del(HashTable *ht, ulong h); // 删除数字索引数据 215 | void zend_hash_clean(HashTable *ht); // 将 HashTable 中的元素全部删除 216 | void zend_hash_destroy(HashTable *ht); // 将这个 HashTable 自身也销毁 217 | ``` 218 | 219 | + 排序、比较 and Going to the Extreme(s) 220 | 221 | ```c 222 | zend_hash_sort(target_hash, zend_qsort,array_data_compare, 1 TSRMLS_CC); 223 | ``` 224 | 225 | 8.3 在内核中操作PHP语言中数组 226 | --- 227 | 228 | + 创建{数组} 229 | 230 | ```c 231 | ZEND_FUNCTION(sample_array) 232 | { 233 | // return_value 是 zval* 类型的,所以直接对它调用 array_init() 函数,即把它初始化成了一个空数组。 234 | array_init(return_value); 235 | } 236 | ``` 237 | 238 | + 增 239 | > 将{数组}初始化后,接下来就要向其添加元素了。因为 PHP 语言中有多种类型的变量,所以也对应的有多种类型的 add_assoc_()、add_index_、add_next_index_*() 函数。 240 | 241 | ```c 242 | //add_assoc_*系列函数: 243 | add_assoc_null(zval *aval, char *key); 244 | add_assoc_bool(zval *aval, char *key, zend_bool bval); 245 | add_assoc_long(zval *aval, char *key, long lval); 246 | add_assoc_double(zval *aval, char *key, double dval); 247 | add_assoc_string(zval *aval, char *key, char *strval, int dup); 248 | add_assoc_stringl(zval *aval, char *key,char *strval, uint strlen, int dup); 249 | add_assoc_zval(zval *aval, char *key, zval *value); 250 | //备注:其实这些函数都是宏,都是对add_assoc_*_ex函数的封装。 251 | 252 | //add_index_*系列函数: 253 | ZEND_API int add_index_long (zval *arg, ulong idx, long n); 254 | ZEND_API int add_index_null (zval *arg, ulong idx ); 255 | ZEND_API int add_index_bool (zval *arg, ulong idx, int b ); 256 | ZEND_API int add_index_resource (zval *arg, ulong idx, int r ); 257 | ZEND_API int add_index_double (zval *arg, ulong idx, double d); 258 | ZEND_API int add_index_string (zval *arg, ulong idx, const char *str, int duplicate); 259 | ZEND_API int add_index_stringl (zval *arg, ulong idx, const char *str, uint length, int duplicate); 260 | ZEND_API int add_index_zval (zval *arg, ulong index, zval *value); 261 | 262 | //add_next_index_long函数: 263 | ZEND_API int add_next_index_long (zval *arg, long n ); 264 | ZEND_API int add_next_index_null (zval *arg ); 265 | ZEND_API int add_next_index_bool (zval *arg, int b ); 266 | ZEND_API int add_next_index_resource (zval *arg, int r ); 267 | ZEND_API int add_next_index_double (zval *arg, double d); 268 | ZEND_API int add_next_index_string (zval *arg, const char *str, int duplicate); 269 | ZEND_API int add_next_index_stringl (zval *arg, const char *str, uint length, int duplicate); 270 | ZEND_API int add_next_index_zval (zval *arg, zval *value); 271 | ``` 272 | 273 | links 274 | --- 275 | 276 | + [目录](00.目录.md) 277 | + 上一节: [07.函数的参数](07.函数的参数.md) 278 | + 下一节: [09.PHP中的资源类型](09.PHP中的资源类型.md) 279 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/09.PHP中的资源类型.md: -------------------------------------------------------------------------------- 1 | PHP中的资源类型 2 | === 3 | 4 | 9.1 复合类型的数据-资源 5 | --- 6 | 7 | + 资源类型在内核中的结构 8 | 9 | ```c 10 | typedef struct _zend_rsrc_list_entry 11 | { 12 | void *ptr; 13 | int type; 14 | int refcount; 15 | } zend_rsrc_list_entry; 16 | ``` 17 | 18 | + 资源类型变量的使用 19 | > 资源类型的变量在实现中也是有类型区分的。 20 | > 为了区分不同类型的资源,比如一个是文件句柄,一个是 mysql 链接,需要为其赋予不同的分类名称。 21 | 22 | + 创建资源 23 | > ZEND_REGISTER_RESOURCE() 宏。 24 | > 资源并不局限于文件句柄,可以申请一块内存,并且指向它的指针来作为一种资源。所以资源可以对应任意类型的数据。 25 | 26 | + 销毁资源 27 | > zend_register_list_destructors_ex 第一个参数代表一个回调函数,会在脚本中的相应类型的资源变量被释放掉的时候触发,比如作用域结束了,或者被 unset() 掉了。 28 | 29 | + Decoding Resources 解码资源 30 | > ZEND_FETCH_RESOURCE()宏函数。 31 | 32 | ```c 33 | #define ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id,default_id, resource_type_name, resource_type) 34 | rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC,default_id, resource_type_name, NULL,1, resource_type); 35 | ZEND_VERIFY_RESOURCE(rsrc); 36 | ``` 37 | 38 | + Forcing Destruction 强制销毁 39 | > 资源数据是保存在 HashTable 中的,虽然可以通过 zend_hash_index_find() 或 zend_hash_next_index_insert() 之类的函数操作这个储存资源的 HashTable,但这绝不是一个好主意,因为在后续的版本中,PHP可能会修改有关这一部分的实现方式,到那时上述方法便不起作用了,所以为了更好的兼容性,请使用标准的宏函数或者 api 函数。 40 | 41 | 9.2 Persistent Resources 持久资源 42 | --- 43 | 44 | + Delayed Destruction 延时销毁 45 | > 持久资源存储在另一个 HashTable 中:EG(persistent_list)。与 EG(regular_list) 有个明显的区别,它每个值的索引都是字符串类型的,而且它的每个值也不会在每次请求结束后被释放掉,只能手动通过 zend_hash_del() 来删除,或者在进程结束后类似于 MSHUTDOWN 阶段将EG (persistent_list) 整体清除,最常见的情景便是操作系统关闭了 Web Server。 46 | 47 | + Reuse 重用 48 | > 因为所有的 PHP 扩展都共用同一个 HashTable 来保存持久资源,所以在为资源的索引起名时,一定要唯一,同时必须简单,方便在其它的函数中构造出来。 49 | 50 | + Liveness Checking and Early Departure 51 | > 当使用资源,尤其是持久资源时,一定要保证获取出来的资源仍然是有效的、可以使用的。如果它失效了,必须将其从 persistent list 中移除。 52 | 53 | + Agnostic Retrieval 54 | > ZEND_FETCH_RESOURCE2() 宏。它与 ZEND_FETCH_RESOURCE() 宏函数的唯一区别就是它可以接收两种类型参数。 55 | 56 | 9.3 资源自有的引用计数 57 | --- 58 | 59 | zval 通过引用计数来节省内存的,某个 zval 对应的资源在实现时也使用了引用计数这种概念,也就是有了两种引用计数。 60 | 资源对应的 zval 的类型 是IS_RESOURCE,它并不保存最终的数据,而只保存一个数字,即 EG(regular_list) 中的数字索引。 61 | 62 | links 63 | --- 64 | 65 | + [目录](00.目录.md) 66 | + 上一节: [08.Array与HashTable](08.Array与HashTable.md) 67 | + 下一节: [10.PHP中的面向对象上篇](10.PHP中的面向对象上篇.md) 68 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/10.PHP中的面向对象上篇.md: -------------------------------------------------------------------------------- 1 | PHP 中的面向对象上篇 2 | === 3 | 4 | 10.1 zend_class_entry 5 | --- 6 | 7 | zend_class_entry 是内核中定义的一个结构体,是内核实现 PHP 语言中类与对象的一个非常基础、关键的结构类型。 8 | 9 | 定义一个类的例子: 10 | 11 | ```c 12 | zend_class_entry *myclass_ce; 13 | 14 | static zend_function_entry myclass_method[] = { 15 | { NULL, NULL, NULL } 16 | }; 17 | 18 | ZEND_MINIT_FUNCTION(sample3) 19 | { 20 | zend_class_entry ce; 21 | 22 | //"myclass"是这个类的名称。 23 | INIT_CLASS_ENTRY(ce, "myclass",myclass_method); 24 | myclass_ce = zend_register_internal_class(&ce TSRMLS_CC); 25 | return SUCCESS; 26 | } 27 | ``` 28 | 29 | 10.2 定义一个类 30 | --- 31 | 32 | + 定义类对应的 zend_class_entry 33 | > 定义类的第一步,便是先定义好这个类的 zend_class_entry,这一步操作是在 MINIT 阶段完成的。 34 | > 某个类的 zend_class_entry 会经常用到,所以一般会把它保存在一个变量里,供扩展中其它地方的程序使用。 35 | 36 | + 为类定义属性 37 | > 可以用 zend_declare_property* 系列函数来完成这项操作,为某个类定义属性一般会需要三个信息:属性的名称、属性的默认值与属性的访问权限等。 38 | 39 | + 为类定义方法 40 | 41 | ```c 42 | // 与类相关的掩码标志 43 | #define ZEND_ACC_STATIC 0x01 /* fn_flags, zend_property_info.flags */ 44 | #define ZEND_ACC_ABSTRACT 0x02 /* fn_flags */ 45 | #define ZEND_ACC_FINAL 0x04 /* fn_flags */ 46 | #define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08 /* fn_flags */ 47 | #define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10 /* ce_flags */ 48 | #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 /* ce_flags */ 49 | #define ZEND_ACC_FINAL_CLASS 0x40 /* ce_flags */ 50 | #define ZEND_ACC_INTERFACE 0x80 /* ce_flags */ 51 | #define ZEND_ACC_INTERACTIVE 0x10 /* fn_flags */ 52 | #define ZEND_ACC_PUBLIC 0x100 /* fn_flags, zend_property_info.flags */ 53 | #define ZEND_ACC_PROTECTED 0x200 /* fn_flags, zend_property_info.flags */ 54 | #define ZEND_ACC_PRIVATE 0x400 /* fn_flags, zend_property_info.flags */ 55 | #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) 56 | #define ZEND_ACC_CHANGED 0x800 /* fn_flags, zend_property_info.flags */ 57 | #define ZEND_ACC_IMPLICIT_PUBLIC 0x1000 /* zend_property_info.flags; unused (1) */ 58 | #define ZEND_ACC_CTOR 0x2000 /* fn_flags */ 59 | #define ZEND_ACC_DTOR 0x4000 /* fn_flags */ 60 | #define ZEND_ACC_CLONE 0x8000 /* fn_flags */ 61 | #define ZEND_ACC_ALLOW_STATIC 0x10000 /* fn_flags */ 62 | #define ZEND_ACC_SHADOW 0x20000 /* fn_flags */ 63 | #define ZEND_ACC_DEPRECATED 0x40000 /* fn_flags */ 64 | #define ZEND_ACC_CLOSURE 0x100000 /* fn_flags */ 65 | #define ZEND_ACC_CALL_VIA_HANDLER 0x200000 /* fn_flags */ 66 | ``` 67 | 68 | + 为类定义常量 69 | 70 | ```c 71 | // 这个内容比较简单,只涉及到一组函数,可以查看 Zend/zend_API.h 72 | ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value TSRMLS_DC); 73 | ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length TSRMLS_DC); 74 | ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, long value TSRMLS_DC); 75 | ZEND_API int zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value TSRMLS_DC); 76 | ZEND_API int zend_declare_class_constant_double(zend_class_entry *ce, const char *name, size_t name_length, double value TSRMLS_DC); 77 | ZEND_API int zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length TSRMLS_DC); 78 | ZEND_API int zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value TSRMLS_DC); 79 | ``` 80 | 81 | 10.3 定义一个接口 82 | --- 83 | 84 | 使用 ZEND_ABSTRACT_ME() 宏函数来为这个接口添加函数,它的作用是声明一个类似虚函数的东西,不用实现。 85 | 86 | 但是这个宏函数只能实现 public 类型函数的声明,如果有其它特殊需要,需要使用 ZEND_FENTRY() 宏函数来实现,因为 ZEND_ABSTRACT_ME() 只不过是后者的一种封装。 87 | 88 | 10.4 继承与实现接口 89 | --- 90 | 91 | 这里的 ZEND_ABSTRACT_ME() 宏函数比较特殊,它会声明一个 abstract public 类型的函数,这个函数不需要实现,因此也就不需要相应的 ZEND_METHOD(i_myinterface,hello) 的实现。一个接口是不能设计出某个非 public 类型的方法的,因为接口暴露给使用者的都应该是一些公开的信息。 92 | 93 | 只要掩码中有 ZEND_ACC_ABSTRACT便代表是一个不需要具体实现的方法。ZEND_FENTRY 其实是 ZEND_ME 和 ZEND_FE 的最终实现,相关宏代码: 94 | 95 | ```c 96 | #define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, 97 | #define ZEND_FN(name) zif_##name 98 | #define ZEND_MN(name) zim_##name 99 | #define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0) 100 | #define ZEND_ME(classname, name, arg_info, flags) ZEND_FENTRY(name, ZEND_MN(classname##_##name), arg_info, flags) 101 | ``` 102 | 103 | links 104 | --- 105 | 106 | + [目录](00.目录.md) 107 | + 上一节: [09.PHP中的资源类型](09.PHP中的资源类型.md) 108 | + 下一节: [11.PHP中的面向对象下篇](11.PHP中的面向对象下篇.md) 109 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/11.PHP中的面向对象下篇.md: -------------------------------------------------------------------------------- 1 | PHP 中的面向对象下篇 2 | === 3 | 4 | PHP 语言中的面向对象其实是分为三个部分来实现的:class、object、refrence。 5 | class 就是所说的类,可以直观的理解为前面所描述的 zend_class_entry。 6 | object 就是实际的对象。 7 | 每一个 zval 并不直接包含具体的 object,而是通过一个索引 refrence 与其联系。 8 | 每个 class 都有很多个 object 实例,并把它们统一的放在一个数组里,每个 zval 只要记住相应的 key 就行了。在传递 zval 时候,实际上传递的是一个索引,而不是内存中具体的对象数据。 9 | 10 | 11.1 生成对象的实例 11 | --- 12 | 13 | 一个 object 在 PHP 内核中是如何实现的: 14 | 15 | ```c 16 | typedef struct _zend_object_value { 17 | zend_object_handle handle; 18 | zend_object_handlers *handlers; 19 | } zend_object_value; 20 | 21 | //此外再回顾一下 zval 的值 value 的结构。 22 | typedef union _zvalue_value { 23 | long lval; /* long value */ 24 | double dval; /* double value */ 25 | struct { 26 | char *val; 27 | int len; 28 | } str; 29 | HashTable *ht; /* hash table value */ 30 | zend_object_value obj; 31 | } zvalue_value; 32 | ``` 33 | 34 | zend_object_value 结构体包含两个成员: 35 | 36 | ```c 37 | zend_object_handle handle // 最终实现是一个 unsigned int 值,Zend 会把每个对象放进数组里,这个 handle 就是此实例的索引。 38 | zend_object_handlers *handlers // 这里是一组函数指针,可以通过它来对对象进行一些操作。 39 | ``` 40 | 41 | 11.2 读写对象的属性 42 | --- 43 | 44 | + 读取对象的属性 45 | 46 | ```c 47 | ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, int name_length, zend_bool silent TSRMLS_DC); // 用于读取对象的属性 48 | ZEND_API zval *zend_read_static_property(zend_class_entry *scope, char *name, int name_length, zend_bool silent TSRMLS_DC); // 用于读取静态属性 49 | ``` 50 | 51 | + 更新对象的属性 52 | 53 | ```c 54 | ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, char *name, int name_length, zval *value TSRMLS_DC); // 用来更新对象的属性 55 | ZEND_API int zend_update_static_property(zend_class_entry *scope, char *name, int name_length, zval *value TSRMLS_DC); // 用来更新类的静态属性 56 | ``` 57 | 58 | + 一些其它的快捷函数 59 | 60 | ```c 61 | ZEND_API void zend_update_property_null(zend_class_entry *scope, zval *object, char *name, int name_length TSRMLS_DC); 62 | ZEND_API void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 63 | ZEND_API void zend_update_property_long(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 64 | ZEND_API void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC); 65 | ZEND_API void zend_update_property_string(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value TSRMLS_DC); 66 | ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value, int value_length TSRMLS_DC); 67 | 68 | ZEND_API int zend_update_static_property_null(zend_class_entry *scope, char *name, int name_length TSRMLS_DC); 69 | ZEND_API int zend_update_static_property_bool(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 70 | ZEND_API int zend_update_static_property_long(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 71 | ZEND_API int zend_update_static_property_double(zend_class_entry *scope, char *name, int name_length, double value TSRMLS_DC); 72 | ZEND_API int zend_update_static_property_string(zend_class_entry *scope, char *name, int name_length, const char *value TSRMLS_DC); 73 | ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, char *name, int name_length, const char *value, int value_length TSRMLS_DC); 74 | ``` 75 | 76 | links 77 | --- 78 | 79 | + [目录](00.目录.md) 80 | + 上一节: [10.PHP中的面向对象上篇](10.PHP中的面向对象上篇.md) 81 | + 下一节: [12.启动与终止的那点事](12.启动与终止的那点事.md) 82 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/12.启动与终止的那点事.md: -------------------------------------------------------------------------------- 1 | 启动与终止的那点事 2 | === 3 | 4 | 12.1 关于生命周期 5 | --- 6 | 7 | 每个启动或者关闭的方法在 return SUCCESS 时退出。如果其中任何的函数 return FAILURE,PHP 为了避免出现严重问题而将请求中止。 8 | 对于多进程的 SAPIS(Apache1 & Apache2-prefork),多个 web server 进程会 fork 出多个 mod_php 实例。每个 mod_php 实例都必须加载属于这个实例 的扩展模块,这意味着 MINIT 函数会被执行多次。但是它在每个进程空间中只会执行一次。 9 | 10 | 12.2 MINFO与phpinfo 11 | --- 12 | 13 | php_info_*() 系列函数: 14 | 15 | ```c 16 | char *php_info_html_esc(char *str TSRMLS_DC) // 这个函数是 php_escape_html_entities() 的一个封装,htmlentites() 函数的底层实现。该函数返回的字符串通过 emalloc() 创建,并在使用后必须使用 efree() 函数释放掉。 17 | void php_info_print_table_start(void) // 输出表格开始标签。 18 | void php_info_print_table_end(void) // 输出表格结束标签。 19 | void php_info_print_table_header(int cols, ...) // 在可变参数列表中的 char * 元素外面的每一列都会输出一对 th 标签。 20 | void php_info_print_table_colspan_header(int cols, char *header) // 在指定列数外面输出一对 th 标签。 21 | void php_info_print_table_row(int cols, ...) // 在可变参数列表中的 char * 元素外面的每一行都会输出一对 td 标签。 22 | void php_info_print_table_row_ex(int cols, char *class, ...) // 在指定列数外面输出一对 td 标签。 23 | void php_info_print_hr(void) // 在HTML中输出一个br标签,或者一个表示行开始和结束的水平线。 24 | ``` 25 | 26 | 12.3 常量 27 | --- 28 | 29 | REGISTER_*_CONSTANT() 家族函数: 30 | 31 | ```c 32 | REGISTER_LONG_CONSTANT(char *name, long lval, int flags) 33 | REGISTER_DOUBLE_CONSTANT(char *name, double dval, int flags) 34 | REGISTER_STRING_CONSTANT(char *name, char *value, int flags) 35 | REGISTER_STRINGL_CONSTANT(char *name,char *value, int value_len, int flags) 36 | ``` 37 | 38 | 对应的底层函数: 39 | 40 | ```c 41 | void zend_register_long_constant(char *name, uint name_len, long lval, int flags, int module_number TSRMLS_DC) 42 | void zend_register_double_constant(char *name, uint name_len, double dval, int flags, int module_number TSRMLS_DC) 43 | void zend_register_string_constant(char *name, uint name_len, char *strval, int flags, int module_number TSRMLS_DC) 44 | void zend_register_stringl_constant(char *name, uint name_len, char *strval, uint strlen, int flags,int module_number TSRMLS_DC) 45 | ``` 46 | 47 | 12.4 PHP扩展中的全局变量 48 | --- 49 | 50 | 用 ZEND_BEGIN_MODULE_GLOBALS 和 ZEND_END_MODULE_GLOBALS 宏将定义的全局变量包起来。 51 | 在非线程安全的环境下,如:Apache1,Apache2-prefork, CGI,CLI... 会使用 zend_*_globals 结构来定义全局变量。 52 | 53 | + 常用 *G() 宏: 54 | 55 | ```c 56 | EG() // 用来访问符号表,函数,资源信息和常量。 57 | CG() // 用来访问核心全局变量。 58 | PG() // PHP全局变量。如:PG(register_globals), PG(safe_mode), PG(memory_limit) 59 | FG() // 文件全局变量。 60 | ``` 61 | 62 | 12.5 PHP语言中的超级全局变量 63 | --- 64 | 65 | zend_register_auto_global() 函数原型: 66 | 67 | ```c 68 | int zend_register_auto_global(char *name, uint name_len, zend_auto_global_callback auto_global_callback TSRMLS_DC) 69 | ``` 70 | 71 | + 全局变量的回调 72 | 73 | 在 ZE2 中,zend_register_auto_global() 函数的 auto_global_callback 参数接受一个自定义函数。 74 | 75 | links 76 | --- 77 | 78 | + [目录](00.目录.md) 79 | + 上一节: [11.PHP中的面向对象下篇](11.PHP中的面向对象下篇.md) 80 | + 下一节: [13.ini配置文件](13.ini配置文件.md) 81 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/13.ini配置文件.md: -------------------------------------------------------------------------------- 1 | ini 配置文件 2 | === 3 | 4 | 13.1 声明和访问 INI 设置 5 | --- 6 | 7 | INI 条目被定义在一个完整的独立的的块,位于 MINIT 方法的同一个源文件,并且用 PHP_INI_BEGIN() 和 PHP_INI_END() 这对宏来定义,并在这对宏之间放入一个或者多个条目。 8 | 9 | zend_ini_entry 10 | 11 | ```c 12 | static zend_ini_entry ini_entries[] = { 13 | {0,0,NULL,0,NULL,NULL,NULL,NULL,NULL,0,NULL,0,0,NULL} 14 | }; 15 | ``` 16 | 17 | + 简单 INI 设置 18 | 19 | + PHP_INI_ENTRY 宏: 20 | + 第一个参数 ini 设置的名称 21 | + 第二个参数 ini 设置的默认值 22 | + 第三个参数设置是否允许修改 23 | + 第四个参数是一个回调函数 24 | 25 | + PHP 总共有 4 个指令配置作用域: 26 | 27 | ```c 28 | PHP_INI_PERDIR // 指令可以在 php.ini、httpd.conf 或 .htaccess 文件中修改 29 | PHP_INI_SYSTEM // 指令可以在 php.ini 和 httpd.conf 文件中修改 30 | PHP_INI_USER // 指令可以在用户脚本中修改 31 | PHP_INI_ALL // 指令可以在任何地方修改 32 | ``` 33 | + 查询 ini 值的宏 34 | 35 | ```c 36 | const char *strval = INI_ORIG_STR("sample4.stringval"); 37 | long lval = INI_ORIG_INT("sample4.intval"); 38 | double dval = INI_ORIG_FLT("sample4.fltval"); 39 | zend_bool bval = INI_ORIG_BOOL("sample4.boolval"); 40 | ``` 41 | 42 | + 访问级别 43 | 44 | ```c 45 | SYSTEM // 设置放在 php.ini 或 Apache的 http.conf 配置文件中,它在 apache 启动时候生效,被认为是设置的全局变量。 46 | PERDIR // 设置放在 Apache 的 http.conf 或块中,或者 .htaccess 文件之中。 47 | USER // 一旦脚本开始执行,唯一的改变 INI 设置的方法就是利用用户方法 ini_set() 48 | ``` 49 | 50 | + 修改事件 51 | 52 | > 无论 INI 设置在什么时候被修改,无论是通过 ini_set() 方法来修改还是在一个 PERDIR 指令执行期间来修改,zend 引擎都会通过一个 OnModify 的回调来检查它。做修改的人可能会通过使用 ZEND_INI_MH 宏来定义,然后通 过OnModify 方法的参数来附加到 INI 设置里。 53 | 54 | + ini 设置回调参数修改 55 | 56 | ```c 57 | entry // 指向 zend 引擎实际存储的 INI 设置 58 | new_value // 关于设置的值 59 | mh_arg 1,2,3 // 这组指针(三个一组)提供了访问数据指针最初给出的 INI 设置的声明 60 | stage // ZEND_INI_STAGE_* 这种形式里面有五个值,这五个由 * 代表的值为 STARTUP、SHUTDOWN、ACTIVATE、DEACTIVATE、 RUNTIME,这些常量分别对应 MINIT、MSHUTDOWN、RINIT、RSHUTDOWN 还有处于活跃状态正在执行的脚本 61 | ``` 62 | 63 | + zend_ini_entry 结构体 64 | 65 | ```c 66 | struct _zend_ini_entry { 67 | int module_number; 68 | int modifiable; 69 | char *name; 70 | uint name_length; 71 | ZEND_INI_MH((*on_modify)); 72 | void *mh_arg1; 73 | void *mh_arg2; 74 | void *mh_arg3; 75 | char *value; 76 | uint value_length; 77 | char *orig_value; 78 | uint orig_value_length; 79 | int modified; 80 | void ZEND_INI_DISP(*displayer); 81 | }; 82 | ``` 83 | 84 | + 显示 INI 设置 85 | 86 | ```c 87 | PHP_MINFO_FUNCTION(sample4) 88 | { 89 | DISPLAY_INI_ENTRIES(); 90 | } 91 | ``` 92 | 93 | + 绑定到扩展的全局设置 94 | 95 | links 96 | --- 97 | 98 | + [目录](00.目录.md) 99 | + 上一节: [12.启动与终止的那点事](12.启动与终止的那点事.md) 100 | + 下一节: [14.流式访问](14.流式访问.md) 101 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/14.流式访问.md: -------------------------------------------------------------------------------- 1 | 流式访问 2 | === 3 | 4 | PHP 用户空间中所有的文件 I/O 处理都是通过 php 4.3 引入的 php 流包装层处理的。在内部扩展代码可以选择使用 stdio 或 posix 文件处理和本地文件系统或伯克利域套接字进行通信,或者也可以调用和用户空间流 I/O 相同的 API。 5 | 6 | 14.1 概览 7 | --- 8 | 9 | + 打开流 10 | 11 | ```php 12 | NOTE:无论打开的是什么类型的流, 它们都存储在一个公共的结构体 php_stream 中 37 | 38 | + fopen 包装 39 | 40 | ```c 41 | php_stream_open_wrapper() 42 | ``` 43 | 44 | + 传输层包装 45 | 46 | ```c 47 | php_stream *php_stream_xport_create(char *xport, int xport_len, 48 | int options, int flags, 49 | const char *persistent_id, 50 | struct timeval *timeout, 51 | php_stream_context *context, 52 | char **errstr, int *errcode); 53 | ``` 54 | 55 | + 目录访问 56 | 57 | + 特殊流 58 | 59 | ```c 60 | php_stream *php_stream_fopen_tmpfile(void); 61 | php_stream *php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path); 62 | 63 | // 这3个 API 方法接受已经打开的 FILE * 资源或文件描述符 ID, 使用流 API 的某种操作包装 64 | php_stream *php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id); 65 | php_stream *php_stream_fopen_from_file(FILE *file, const char *mode); 66 | php_stream *php_stream_fopen_from_pipe(FILE *file, const char *mode); 67 | ``` 68 | 69 | 14.2 访问流 70 | --- 71 | 72 | 开一个流之后, 就可以在它上面执行 I/O 操作了,使用哪种协议包装API创建了流并不重要, 它们都使用相同的访问 API。 73 | 74 | + 读 75 | 76 | + 流的读写可以使用下面的 API 函数组合完成, 它们多数都是遵循 POSIX I/O 中对应的 API 规范的: 77 | 78 | ```c 79 | // 从数据流中接收一个字符.如果流上没有数据, 则返回EOF. 80 | int php_stream_getc(php_stream *stream); 81 | // 从指定流中读取指定字节的数据。buf 必须预分配至少 count 字节的内存空间。这个函数将返回从数据流实际读到缓冲区中的数据字节数。 82 | size_t php_stream_read(php_stream *stream, char *buf, size_t count); 83 | // 从 stream 中读取最多 maxlen 个字符, 直到碰到换行符或流结束 84 | char *php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len); 85 | // 从 stream 中读取最多 maxlen 个字符, 直到碰到换行符或流结束 86 | char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen); 87 | // 和 php_stream_get_line() 类似,但允许指定任意的停止读取标记 88 | char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC); 89 | ``` 90 | 91 | + 读取目录项 92 | 93 | + php_stream_dirent 结构体 94 | 95 | ```c 96 | typedef struct _php_stream_dirent { 97 | char d_name[MAXPATHLEN]; 98 | } php_stream_dirent; 99 | ``` 100 | 101 | + php 流包装层暴露了一个 API, 它将记录大小的检查和类型转换处理封装到了一次调用中 102 | 103 | ```c 104 | php_stream_dirent *php_stream_readdir(php_stream *dirstream, php_stream_dirent *entry); 105 | ``` 106 | + 写 107 | 108 | + 和读类似, 向流中写数据只需要传递一个缓冲区和缓冲区长度给流 109 | 110 | ```c 111 | size_t php_stream_write(php_stream *stream, char *buf, size_t count); 112 | size_t php_stream_write_string(php_stream *stream, char *stf); 113 | ``` 114 | 115 | + 使用 php_stream_putc() 和 php_stream_puts() 写入一个字符或一个字符串到流中 116 | 117 | ```c 118 | int php_stream_putc(php_stream *stream, int c); 119 | int php_stream_puts(php_string *stream, char *buf); 120 | ``` 121 | 122 | + 功能和格式上都类似于 fprintf(), 这个 API 调用允许在写的同时构造字符串而不用去创建临时缓冲区构造数据,一个明显的不同是它需要 TSRMLS_CC 宏来保证线程安全 123 | 124 | ```c 125 | size_t php_stream_printf(php_stream *stream TSRMLS_DC, const char *format, ...); 126 | ``` 127 | 128 | + 随机访问, 查看文件偏移量以及缓存的 flush 129 | 130 | ```c 131 | // 将文件指针移动到任意位置 132 | int php_stream_seek(php_stream *stream, off_t offset, int whence); 133 | // 将文件指针移动到任意位置 134 | int php_stream_rewind(php_stream *stream); 135 | // 在目录流上随机访问 136 | int php_stream_rewinddir(php_stream *dirstream); 137 | // 返回当前的文件偏移量 138 | off_t php_stream_tell(php_stream *stream); 139 | // 强制将流过滤器此类内部缓冲区中的数据输出到最终的资源中 140 | int php_stream_flush(php_stream *stream); 141 | // 获取流实例的其他信息 142 | int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb); 143 | ``` 144 | 145 | + 关闭 146 | 147 | + 所有流的关闭都是通过 php_stream_free() 函数处理的 148 | 149 | ```c 150 | int php_stream_free(php_stream *stream, int options); 151 | ``` 152 | 153 | + 在关闭流时使用下面两个宏的某个替代 php_stream_free() 函数 154 | 155 | ```c 156 | #define php_stream_close(stream) \ 157 | php_stream_free((stream), PHP_STREAM_FREE_CLOSE) 158 | #define php_stream_pclose(stream) \ 159 | php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT) 160 | ``` 161 | 162 | + 通过 zval 交换流 163 | 164 | ```c 165 | #define php_stream_to_zval(stream, pzval) \ 166 | ZVAL_RESOURCE((pzval), (stream)->rsrc_id); 167 | ``` 168 | 169 | 14.3 静态资源操作 170 | --- 171 | 172 | ```c 173 | // 提供了对 POSIX 的 stat() 函数协议依赖的包装 174 | int php_stream_stat_path(char *path, php_stream_statbuf *ssb); 175 | int php_stream_stat_path_ex(char *path, int flags, 176 | php_stream_statbuf *ssb, php_stream_context *context); 177 | ``` 178 | 179 | ```c 180 | // 创建目录 181 | int php_stream_mkdir(char *path, int mode, int options, 182 | php_stream_context *context); 183 | // 删除目录 184 | int php_stream_rmdir(char *path, int options, 185 | php_stream_context *context); 186 | ``` 187 | 188 | links 189 | --- 190 | 191 | + [目录](00.目录.md) 192 | + 上一节: [13.ini配置文件](13.ini配置文件.md) 193 | + 下一节: [15.流的实现](15.流的实现.md) 194 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/15.流的实现.md: -------------------------------------------------------------------------------- 1 | 流的实现 2 | === 3 | 4 | php的流最强力的特性之一是它可以访问众多数据源: 普通文件, 压缩文件, 网络透明通道, 加密网络, 命名管道以及域套接字, 它们对于用户空间以及内部都是统⼀的API。 5 | 6 | 15.1 PHP 流的表象之下 7 | --- 8 | 9 | + php_stream_ops 结构体 10 | 11 | ```c 12 | typedef struct _php_stream_ops { 13 | size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC); 14 | size_t (*read)(php_stream *stream, char *buf, size_t count TSRMLS_DC); 15 | int (*close)(php_stream *stream, int close_handle TSRMLS_DC); 16 | int (*flush)(php_stream *stream TSRMLS_DC); 17 | const char *label; 18 | int (*seek)(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); 19 | int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC); 20 | int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); 21 | int (*set_option)(php_stream *stream, int option,int value, void *ptrparam TSRMLS_DC); 22 | } php_stream_ops; 23 | ``` 24 | 25 | 15.2 包装器操作 26 | --- 27 | 28 | 15.3 实现一个包装器 29 | --- 30 | 31 | 15.4 操纵 32 | --- 33 | 34 | 15.5 检查 35 | --- 36 | 37 | links 38 | --- 39 | 40 | + [目录](00.目录.md) 41 | + 上一节: [14.流式访问](14.流式访问.md) 42 | + 下一节: [16.有趣的流](16.有趣的流.md) 43 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/16.有趣的流.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuleying/PHP/7780ada3c3429168e69c85e3c4eeac2298a44ccc/PHP源码学习笔记/PHPBOOK/16.有趣的流.md -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/17.配置和链接.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuleying/PHP/7780ada3c3429168e69c85e3c4eeac2298a44ccc/PHP源码学习笔记/PHPBOOK/17.配置和链接.md -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/18.扩展生成器.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuleying/PHP/7780ada3c3429168e69c85e3c4eeac2298a44ccc/PHP源码学习笔记/PHPBOOK/18.扩展生成器.md -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/19.设置宿主环境.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuleying/PHP/7780ada3c3429168e69c85e3c4eeac2298a44ccc/PHP源码学习笔记/PHPBOOK/19.设置宿主环境.md -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/20.高级嵌入式.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wuleying/PHP/7780ada3c3429168e69c85e3c4eeac2298a44ccc/PHP源码学习笔记/PHPBOOK/20.高级嵌入式.md -------------------------------------------------------------------------------- /PHP源码学习笔记/PHPBOOK/README.md: -------------------------------------------------------------------------------- 1 | 《PHP扩展开发及内核应用-学习笔记》 2 | === 3 | 4 | PHP扩展开发及内核应用-学习笔记 5 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHP内核探索/00.常用宏解释.md: -------------------------------------------------------------------------------- 1 | 常用宏解释 2 | === 3 | 4 | 全局宏 5 | --- 6 | 7 | + CG 用于保存在编译时的数据 (compile_global) 8 | ``` 9 | Zend/zend_globals_macros.h: 10 | # define CG(v) TSRMG(compiler_globals_id, zend_compiler_globals *, v) 11 | ``` 12 | 13 | + EG 用于保存在运行时的数据 (excutor_global) 14 | ``` 15 | Zend/zend_globals_macros.h: 16 | # define EG(v) TSRMG(executor_globals_id, zend_executor_globals *, v) 17 | ``` 18 | 19 | + EX 用于获取 execute_data 中的值 20 | ``` 21 | Zend/zend_compile.h 22 | #define EX(element) execute_data->element 23 | ``` 24 | 25 | + Z_OBJ_P 将一个 zval 类型变量构造为 zend_object 类型 26 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHP内核探索/10.函数.md: -------------------------------------------------------------------------------- 1 | 函数 2 | === 3 | 4 | 函数返回值 5 | --- 6 | 7 | + zend_do_return() 给用户自定义的函数返回指定类型的变量 8 | 9 | + ZEND_RETURN 生成的中间代码 10 | + ZEND_RETURN_SPEC_CONST_HANDLER 11 | + ZEND_RETURN_SPEC_TMP_HANDLER 12 | + ZEND_RETURN_SPEC_TMP_HANDLER 13 | 14 | + zend_do_end_function_declaration() 每个函数解析时执行 15 | 16 | + return_value 内部函数的返回值都是通过此变量传递的 17 | 18 | + 从函数直接返回值的宏 19 | + RETURN_RESOURCE(resource) 返回一个资源 20 | + RETURN_BOOL(bool) 返回一个布尔值 21 | + RETURN_NULL() 返回一个空值 22 | + RETURN_LONG(long) 返回一个长整数 23 | + RETURN_DOUBLE(double) 返回一个双精度浮点数 24 | + RETURN_STRING(string, duplicate) 返回一个字符串。duplicate 表示这个字符是否使用 estrdup() 进行复制 25 | + RETURN_STRINGL(string, length, duplicate) 返回一个定长的字符串。其余跟 RETURN_STRING 相同。这个宏速度更快而且是二进制安全的 26 | + RETURN_EMPTY_STRING() 返回一个空字符串 27 | + RETURN_FALSE 返回一个布尔值假 28 | + RETURN_TRUE 返回一个布尔值真 29 | 30 | + 设置函数返回值的宏 31 | + RETVAL_RESOURCE(resource) 设定返回值为指定的一个资源 32 | + RETVAL_BOOL(bool) 设定返回值为指定的一个布尔值 33 | + RETVAL_NULL 设定返回值为空值 34 | + RETVAL_LONG(long) 设定返回值为指定的一个长整数 35 | + RETVAL_DOUBLE(double) 设定返回值为指定的一个双精度浮点数 36 | + RETVAL_STRING(string, duplicate) 设定返回值为指定的一个字符串,duplicate 含义同 RETURN_STRING 37 | + RETVAL_STRINGL(string, length, duplicate) 设定返回值为指定的一个定长的字符串。其余跟 RETVAL_STRING 相同。这个宏速度更快而且是二进制安全的 38 | + RETVAL_EMPTY_STRING 设定返回值为空字符串 39 | + RETVAL_FALSE 设定返回值为布尔值假 40 | + RETVAL_TRUE 设定返回值为布尔值真 41 | 42 | + array_init() 需要返回的是数组,需要先调用此函数 43 | 44 | + object_init() 需要返回的是对象,需要先调用此函数 45 | 46 | 形参return value 47 | --- 48 | 49 | + INTERNAL_FUNCTION_PARAMETERS 代表参数声明的宏 50 | + int ht 哈希表 51 | + zval *return_value 函数执行完成后,内核将把这个指针指向的 zval 返回给用户端的函数调用者 52 | + zval **return_value_ptr 指向函数的返回值的指针 53 | + zval *this_ptr 如果此函数是一个类的方法,那么这个指针的含义和PHP语言中 $this 变量差不多 54 | + int return_value_used 代表用户端在调用此函数时有没有使用到它的返回值 55 | 56 | 函数调用与执行 57 | --- 58 | 59 | + zend_execute_internal() 执行内部函数包含的 zend_op_array 60 | 61 | + zend_execute() 执行函数包含的 zend_op_array 62 | 63 | + zend_function_entry 结构体 64 | ``` 65 | typedef struct _zend_function_entry { 66 | const char *fname; 67 | void (*handler)(INTERNAL_FUNCTION_PARAMETERS); 68 | const struct _zend_arg_info *arg_info; 69 | zend_uint num_args; 70 | zend_uint flags; 71 | } zend_function_entry; 72 | ``` 73 | 74 | + zend_do_fcall_common_helper_SPEC() 75 | 76 | 引用与函数执行 77 | --- 78 | 79 | + zend_arg_info 结构体 80 | ``` 81 | typedef struct _zend_arg_info { 82 | const char *name; /* 参数的名称*/ 83 | zend_uint name_len; /* 参数名称的长度*/ 84 | const char *class_name; /* 类名 */ 85 | zend_uint class_name_len; /* 类名长度*/ 86 | zend_bool array_type_hint; /* 数组类型提示 */ 87 | zend_bool allow_null; /* 是否允许为NULL */ 88 | zend_bool pass_by_reference; /* 是否引用传递 */ 89 | zend_bool return_reference; /* 返回值是否为引用形式 */ 90 | int required_num_args; /* 必要参数的数量 */ 91 | } zend_arg_info; 92 | ``` 93 | 94 | + zend_arg_info 结构体生成相关宏 95 | + ZEND_BEGIN_ARG_INFO 生成头部 96 | + ZEND_BEGIN_ARG_INFO_EX 生成头部 97 | + ZEND_ARG_PASS_INFO 生成具体的数据 98 | + ZEND_END_ARG_INFO 生成尾部代码 99 | 100 | 匿名函数及闭包 101 | --- 102 | 103 | + zend_create_closure() 创建一个闭包对象 104 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHP内核探索/11.类与对象.md: -------------------------------------------------------------------------------- 1 | 类与对象 2 | === 3 | 4 | 开篇 5 | --- 6 | 7 | + 基本概念 8 | + 类。类是具体事物的抽象。通常类定义了事物的属性和所能完成的工作。有一点需要注意,并不是所有的面向对象编程语言的类都具有class这个明确的实体。例如Javascript就不是基于类的。 Javascript中的类(Function)也具有类定义的特性。这也印证了面向对象只是一种编程范式 9 | + 对象。对象是类的实例。对象是具体的 10 | + 方法。方法是类定义对象可以做的事情 11 | + 继承性。继承是类的具体化,子类是比具备更多特性和行为的类。面向对象是对现实世界的一个抽象。在很多时候的关系并不一定是继承关系。能在一定程序上实现代码的重用 12 | + 封装性、抽象性。封装性能实现的复杂性隐藏,减少出错的可能 13 | 14 | 类的结构和实现 15 | --- 16 | 17 | + _zend_class_entry 类的内部存储结构 18 | + name 类名 19 | + type 类别 (ZEND_INTERNAL_CLASS/ZEND_USER_CLASS) 20 | + parent 父类 21 | + refcount 引用计数 22 | + ce_flags 类的类型 23 | + function_table 函数列表 24 | + interfaces 接口列表 25 | + filename 存放文件绝对路径 26 | + line_start 类开始行数 27 | + line_end 类结束行数 28 | 29 | + 类的几种类型 30 | + 常规类 T_CLASS 31 | + 抽象类 T_ABSTRACT T_CLASS 32 | + final 类 T_FINAL T_CLASS 33 | + 没有加abstract关键字的抽象类 ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 34 | + 接口 ZEND_ACC_INTERFACE 35 | ``` 36 | /* 常规类为0,在这里没有定义 */ 37 | #define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10 38 | #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 39 | #define ZEND_ACC_FINAL_CLASS 0x40 40 | #define ZEND_ACC_INTERFACE 0x80 41 | ``` 42 | 43 | + zend_do_begin_class_declaration() 用来处理类名,类的类别和父类 44 | 45 | + zend_do_end_class_declaration() 用来处理接口和类的中间代码 46 | 47 | 类的成员变量 48 | --- 49 | 50 | + zend_do_declare_property 检查成员变量不允许的一些情况 51 | + 接口中不允许使用成员变量 52 | + 成员变量不能拥有抽象属性 53 | + 不能声明成员变量为final 54 | + 不能重复声明属性 55 | 56 | + default_properties 常规的成员变量最后都会注册到类结构的这个字段中 57 | 58 | + get_class_vars() 获取类的成员变量 59 | 60 | + default_static_members 类本身的静态变量存放在类结构的这个字段中 61 | 62 | 类的成员方法 63 | --- 64 | 65 | + zend_initialize_class_data 初始化了整个方法列表所在的哈希表 66 | 67 | + get_class_methods 获取类的成员方法 68 | 69 | + zend_fetch_class 通过类名在 EG(class_table) 中查找类 70 | 71 | + zend_std_get_static_method 查找 ce->function_table 列表,在查找到方法后检查方法的访问控制权限 72 | 73 | 类的定义 74 | --- 75 | 76 | + zend_declare_property* 系列函数为类定义属性 77 | 78 | + ZEND_METHOD 为类定义方法 79 | 80 | + 类方法声明时可以使用的掩码 81 | ``` 82 | /* fn_flags代表可以在定义方法时使用 */ 83 | /* zend_property_info.flags代表可以在定义属性时使用 */ 84 | /* ce_flags代表在定义zend_class_entry时候可用 */ 85 | #define ZEND_ACC_STATIC 0x01 /* fn_flags, zend_property_info.flags */ 86 | #define ZEND_ACC_ABSTRACT 0x02 /* fn_flags */ 87 | #define ZEND_ACC_FINAL 0x04 /* fn_flags */ 88 | #define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08 /* fn_flags */ 89 | #define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10 /* ce_flags */ 90 | #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 /* ce_flags */ 91 | #define ZEND_ACC_FINAL_CLASS 0x40 /* ce_flags */ 92 | #define ZEND_ACC_INTERFACE 0x80 /* ce_flags */ 93 | #define ZEND_ACC_INTERACTIVE 0x10 /* fn_flags */ 94 | #define ZEND_ACC_PUBLIC 0x100 /* fn_flags, zend_property_info.flags */ 95 | #define ZEND_ACC_PROTECTED 0x200 /* fn_flags, zend_property_info.flags */ 96 | #define ZEND_ACC_PRIVATE 0x400 /* fn_flags, zend_property_info.flags */ 97 | #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) 98 | #define ZEND_ACC_CHANGED 0x800 /* fn_flags, zend_property_info.flags */ 99 | #define ZEND_ACC_IMPLICIT_PUBLIC 0x1000 /* zend_property_info.flags; unused (1) */ 100 | #define ZEND_ACC_CTOR 0x2000 /* fn_flags */ 101 | #define ZEND_ACC_DTOR 0x4000 /* fn_flags */ 102 | #define ZEND_ACC_CLONE 0x8000 /* fn_flags */ 103 | #define ZEND_ACC_ALLOW_STATIC 0x10000 /* fn_flags */ 104 | #define ZEND_ACC_SHADOW 0x20000 /* fn_flags */ 105 | #define ZEND_ACC_DEPRECATED 0x40000 /* fn_flags */ 106 | #define ZEND_ACC_CLOSURE 0x100000 /* fn_flags */ 107 | #define ZEND_ACC_CALL_VIA_HANDLER 0x200000 /* fn_flags */ 108 | ``` 109 | 110 | 访问控制 111 | --- 112 | 113 | + 访问控制的相关常量 114 | ``` 115 | #define ZEND_ACC_PUBLIC 0x100 116 | #define ZEND_ACC_PROTECTED 0x200 117 | #define ZEND_ACC_PRIVATE 0x400 118 | #define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE) 119 | ``` 120 | 121 | 继承,多态与抽象类 122 | --- 123 | 124 | + zend_do_inheritance() 实现继承的编译函数 125 | 126 | + zend_do_inherit_interfaces() 遍历所有的接口列表 127 | 128 | + do_inherit_parent_constructor() 实现魔术方法继承 129 | 130 | + do_inherit_method_check() 实现继承中的访问控制 131 | 132 | + zend_verify_arg_type() 实现类型提示 133 | 134 | + zend_verify_arg_class_kind() 获取类的类型验证信息 135 | 136 | + instanceof_function() 判断是否为指定类的实例 137 | 138 | + zend_do_implement_interface() 合并接口中的常量列表和方法列表操作 139 | 140 | 魔术函数与延迟绑定 141 | --- 142 | 143 | + 魔术变量的存储在_zend_class_entry中的代码 144 | ``` 145 | struct _zend_class_entry { 146 | ... 147 | //构造方法 __construct 148 | union _zend_function *constructor; 149 | //析构方法 __destruct 150 | union _zend_function *destructor; 151 | //克隆方法 __clone 152 | union _zend_function *clone; 153 | union _zend_function *__get; 154 | union _zend_function *__set; 155 | union _zend_function *__unset; 156 | union _zend_function *__isset; 157 | union _zend_function *__call; 158 | union _zend_function *__callstatic; 159 | union _zend_function *__tostring; 160 | //序列化 161 | union _zend_function *serialize_func; 162 | //反序列化 163 | union _zend_function *unserialize_func; 164 | ... 165 | } 166 | ``` 167 | 168 | + zend_objects_destroy_object() 销毁对象池中的对象 169 | 170 | 保留类与特殊类 171 | --- 172 | 173 | + PHP 的保留类 174 | + Directory 175 | + stdClass 176 | + Exception 177 | 178 | + PHP 中的 static 关键字的含义 179 | + 在函数体内的修饰变量的 static 关键字用于定义静态局部变量 180 | + 用于修饰类成员函数和成员变量时用于声明静态成员 181 | + PHP5.3 在作用域解析符(::)前又表示静态延迟绑定的特殊类 182 | 183 | + zend_get_class_fetch_type() 获取类返回类型 184 | 185 | 对象 186 | --- 187 | 188 | + zend_object_value 结构体 189 | ``` 190 | typedef struct _zend_object_value { 191 | zend_object_handle handle; 192 | // unsigned int类型,EG(objects_store).object_buckets的索引 193 | zend_object_handlers *handlers; 194 | } zend_object_value; 195 | ``` 196 | 197 | + _zend_object 结构体 198 | ``` 199 | typedef struct _zend_object { 200 | zend_class_entry *ce; 201 | HashTable *properties; 202 | HashTable *guards; /* protects from __get/__set ... recursion */ 203 | } zend_object; 204 | ``` 205 | 206 | + zend_objects_new() 执行 zend_object 类型的对象的初始化工作 207 | 208 | + EG(objects_store) 对象池 209 | 210 | + 对象池的存储结构 211 | ``` 212 | typedef struct _zend_objects_store { 213 | zend_object_store_bucket *object_buckets; 214 | zend_uint top; 215 | zend_uint size; 216 | int free_list_head; 217 | } zend_objects_store; 218 | 219 | typedef struct _zend_object_store_bucket { 220 | zend_bool destructor_called; 221 | zend_bool valid; 222 | union _store_bucket { 223 | struct _store_object { 224 | void *object; 225 | zend_objects_store_dtor_t dtor; 226 | zend_objects_free_object_storage_t free_storage; 227 | zend_objects_store_clone_t clone; 228 | const zend_object_handlers *handlers; 229 | zend_uint refcount; 230 | gc_root_buffer *buffered; 231 | } obj; 232 | struct { 233 | int next; 234 | } free_list; 235 | } bucket; 236 | } zend_object_store_bucket; 237 | ``` 238 | 239 | + 对象操作相关 API 240 | + zend_objects_store_init 对象池初始化操作 241 | + zend_objects_store_destroy 销毁对象池,调用efree释放内存 242 | + zend_objects_store_mark_destructed 标记所有对象已经调用了析构函数 243 | + zend_objects_store_free_object_storage 释放存储的对象 244 | + zend_objects_store_put 对象的添加API,在此函数中,程序会执行单个bucket的初始化操作 245 | + zend_objects_store_get_refcount 获取对象池中对象的引用计数 246 | + zend_objects_store_add_ref 对象的引用计数加 1,传入值为对象 247 | + zend_objects_store_add_ref_by_handle 通过 handle 查找对象,并将其引用计数加 1 248 | + zend_objects_store_del_ref 对象的引用计数减 1,传入值为对象 249 | + zend_objects_store_del_ref_by_handle_ex 通过 handle 查找对象,并将其引用计数减 1,对于引用计数为 1 的对象有清除处理 250 | + zend_objects_store_clone_obj 对象克隆 API,构造一个新的 bucket,并将新的对象添加到对象池 251 | + zend_object_store_get_object 获取对象池中 bucket 中的对象,传入值为对象 252 | + zend_object_store_get_object_by_handle 获取对象池中 bucket 中的对象,传入值为索引值 253 | 254 | + zend_std_read_property() 获取对象的成员变量 255 | 256 | + zend_std_write_property() 设置对象的成员变量 257 | 258 | 对象属性读写 259 | --- 260 | 261 | + zend_update_property() 更新对象的属性 262 | 263 | + zend_update_static_property() 更新对象的静态属性 264 | 265 | + 其他对象操作快捷函数 266 | ``` 267 | ZEND_API void zend_update_property_null(zend_class_entry *scope, zval *object, char *name, int name_length TSRMLS_DC); 268 | ZEND_API void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 269 | ZEND_API void zend_update_property_long(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); 270 | ZEND_API void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC); 271 | ZEND_API void zend_update_property_string(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value TSRMLS_DC); 272 | ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value, int value_length TSRMLS_DC); 273 | ZEND_API int zend_update_static_property_null(zend_class_entry *scope, char *name, int name_length TSRMLS_DC); 274 | ZEND_API int zend_update_static_property_bool(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 275 | ZEND_API int zend_update_static_property_long(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); 276 | ZEND_API int zend_update_static_property_double(zend_class_entry *scope, char *name, int name_length, double value TSRMLS_DC); 277 | ZEND_API int zend_update_static_property_string(zend_class_entry *scope, char *name, int name_length, const char *value TSRMLS_DC); 278 | ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, char *name, int name_length, const char *value, int value_length TSRMLS_DC); 279 | ``` 280 | 281 | 命名空间 282 | --- 283 | 284 | + zend_do_begin_namespace() 开始处理命名空间函数 285 | 286 | + zend_do_build_namespace_name() 处理命名空间名称 287 | 288 | + zend_do_build_full_name() 实现命名空间与类名称合并 289 | 290 | 定义接口 291 | --- 292 | 293 | + ZEND_ABSTRACT_ME() 这个宏函数为接口添加函数,声明一个虚拟函数,不用实现 294 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHP内核探索/12.资源.md: -------------------------------------------------------------------------------- 1 | 资源 2 | === 3 | 4 | + 资源的结构体 5 | ``` 6 | typedef struct _zend_rsrc_list_entry 7 | { 8 | void *ptr; 9 | int type; 10 | int refcount; 11 | }zend_rsrc_list_entry; 12 | ``` 13 | 14 | + ZEND_REGISTER_RESOURCE() 注册一个资源到资源池 15 | 16 | + ZEND_FETCH_RESOURCE() 返回一个资源 17 | 18 | + ZEND_VERIFY_RESOURCE() 校验资源 19 | -------------------------------------------------------------------------------- /PHP源码学习笔记/PHP内核探索/13.Zend虚拟机.md: -------------------------------------------------------------------------------- 1 | Zend 虚拟机 2 | === 3 | 4 | 简介 5 | --- 6 | 7 | + 一个PHP文件在服务器端的执行过程包括以下两个大的过程 8 | + 递给 php 程序需要执行的文件, php程序完成基本的准备工作后启动 PHP 及 Zend 引擎,加载注册的扩展模块 9 | + 初始化完成后读取脚本文件,Zend 引擎对脚本文件进行词法分析,语法分析。然后编译成 opcode 执行。如过安装了 apc 之类的 opcode 缓存,编译环节可能会被跳过而直接从缓存中读取 opcode 执行 10 | 11 | + Zend 虚拟机体系结构 12 | + 解释层 13 | + 中间数据层 14 | + 执行引擎 15 | 16 | 虚拟机的词法解析 17 | --- 18 | 19 | + 语言转化的编译过程 20 | + 词法分析 21 | + 语法分析 22 | + 语义分析 23 | + 中间代码生成 24 | + 代码优化 25 | + 目标代码生成 26 | 27 | 虚拟机的语法分析 28 | --- 29 | 30 | + Bison是一种通用目的的分析器生成器。它将 LALR上下文无关文法的描述转化成分析该文法的C程序。使用它可以生成解释器,编译器,协议实现等多种程序。Bison向上兼容Yacc,所有书写正确的Yacc语法都应该可以不加修改地在 Bison 下工作。 它不但与 Yacc 兼容还具有许多 Yacc 不具备的特性 31 | 32 | 中间代码 opcode 的执行 33 | --- 34 | 35 | + 编译后生成中间代码的过程 36 | ``` 37 | EG(active_op_array) = zend_compile_file(file_handle, type TSRMLS_CC); 38 | ``` 39 | 40 | + PHP虚拟机将通过以下代码执行中间代码 41 | ``` 42 | zend_execute(EG(active_op_array) TSRMLS_CC); 43 | ``` 44 | 45 | + zend_execute_data 数据结构 46 | ``` 47 | typedef struct _zend_execute_data zend_execute_data; 48 | 49 | struct _zend_execute_data { 50 | struct _zend_op *opline; 51 | zend_function_state function_state; 52 | zend_function *fbc; /* Function Being Called */ 53 | zend_class_entry *called_scope; 54 | zend_op_array *op_array; /* 当前执行的中间代码 */ 55 | zval *object; 56 | union _temp_variable *Ts; 57 | zval ***CVs; 58 | HashTable *symbol_table; /* 符号表 */ 59 | struct _zend_execute_data *prev_execute_data; /* 前一条中间代码执行的环境*/ 60 | zval *old_error_reporting; 61 | zend_bool nested; 62 | zval **original_return_value; /* */ 63 | zend_class_entry *current_scope; 64 | zend_class_entry *current_called_scope; 65 | zval *current_this; 66 | zval *current_object; 67 | struct _zend_op *call_opline; 68 | }; 69 | ``` 70 | 71 | + zend_execute_data 结构体部分字段说明 72 | ``` 73 | opline struct _zend_op 类型,当前执行的中间代码 74 | op_array zend_op_array 类型,当前执行的中间代码队列 75 | fbc zend_function 类型,已调用的函数 76 | called_scope zend_class_entry 类型,当前调用对象作用域,常用操作是 EX(called_scope) = Z_OBJCE_P(EX(object)),即将刚刚调用的对象赋值给它 77 | symbol_table 符号表,存放局部变量。在 execute_data 初始时,EX(symbol_table) = EG(active_symbol_table); 78 | prev_execute_data 前一条中间代码执行的中间数据,用于函数调用等操作的运行环境恢复 79 | ``` 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP 2 | === 3 | 4 | PHP相关资料 5 | -------------------------------------------------------------------------------- /git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | read -p "请输入备注:" commit_content 4 | 5 | git pull 6 | echo "pull OK\n" 7 | git add -A * 8 | echo "add OK\n" 9 | git commit -m "$commit_content" 10 | echo "commit OK\n" 11 | git push origin master 12 | echo "push OK\n" 13 | exit 0 14 | -------------------------------------------------------------------------------- /redis源码学习笔记/01.内存管理.md: -------------------------------------------------------------------------------- 1 | # 01.内存管理 2 | 3 | Zmalloc 的接口定义在头文件 Zmalloc.h 中: 4 | 5 | ```c 6 | void *zmalloc(size_t size); 7 | void *zcalloc(size_t size); 8 | void *zrealloc(void *ptr, size_t size); 9 | void zfree(void *ptr); 10 | char *zstrdup(const char *s); 11 | size_t zmalloc_used_memory(void); /* 返回当前已使用内存 */ 12 | void zmalloc_enable_thread_safeness(void); 13 | float zmalloc_get_fragmentation_ratio(void); 14 | size_t zmalloc_get_rss(void); 15 | ``` 16 | 17 | zmalloc 函数: 18 | 19 | ```c 20 | void *zmalloc(size_t size) { 21 | void *ptr = malloc(size+PREFIX_SIZE); /* PREFIX_SIZE 为一个标准的 size_t 的大小 */ 22 | if (!ptr) zmalloc_oom(size); 23 | #ifdef HAVE_MALLOC_SIZE 24 | update_zmalloc_stat_alloc(zmalloc_size(ptr),size); /* 更新内存占用量统计 */ 25 | return ptr; 26 | #else 27 | *((size_t*)ptr) = size; 28 | update_zmalloc_stat_alloc(size+PREFIX_SIZE,size); /* 更新内存占用量统计 */ 29 | return (char*)ptr+PREFIX_SIZE; 30 | #endif 31 | } 32 | ``` 33 | update_zmalloc_stat_alloc 宏定义: 34 | 35 | ```c 36 | #define update_zmalloc_stat_alloc(__n,__size) do { \ 37 | size_t _n = (__n); \ 38 | if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 39 | if (zmalloc_thread_safe) { \ 40 | pthread_mutex_lock(&used_memory_mutex); \ 41 | used_memory += _n; \ 42 | pthread_mutex_unlock(&used_memory_mutex); \ 43 | } else { \ 44 | used_memory += _n; \ 45 | } \ 46 | } while(0) 47 | ``` 48 | --------------------------------------------------------------------------------