主讲人 王华呈 提纲 1.源码阅读准备 2.PHP生命周期和SAPI 3.Zend Engine 4.变量和数组的实现原理 5.PHP扩展 源码阅读准备 工具: 1.Vim ctags ctags -R . set tags+=phpsrc/tags taglist 2.Eclipse +CDT 3.VisualStudio 源码编译php cd php-src ./buildconf ./configure –-help ./configure –-disable-all make ./sapi/cli/php -v php目录结构 ext 扩展目录 main php主目录 Zend Zend引擎的实现目录 sapi sapi接口实现目录 TSRM PHP的线程安全是构建在TSRM库之上的, TSRM(Thread Safe Resource Manager)线程安全资源 管理器 win32 win32特有,socket处理 pear PHP 扩展与应用仓库,包含PEAR的核心文件 tests php tests目录 build php生成可执行文件 二 PHP生命周期和SAPI PHP生命周期主要有四个阶段: MINIT 模块初始化 RINIT 请求初始化,模块激活 RSHUTDOWN 请求关闭 MSHUTDOWN 模块关闭,销毁资源 SAPI SAPI全称是Server Application Programming Interface, 也就是服务端应用编程接口,sapi通过一系列钩子函数,使得 php可以和外围交互数据,这是php非常优雅和成功的一个 设计,通过sapi成功的将php本身和上层应用解耦隔离, php可以不再考虑如何针对不同应用进行兼容,而应用本身 也可以针对自己的特点实现不同的处理方式 单线程SAPI Life Cycle(cli) 多线程SAPI Life Cycle(apache) 1 三 Zend Engine Zend Engine 是一个开源脚本引擎 (一个虚拟机),因作为PHP语言的重要核 心而闻名。它原由仍在以色列技术学院的学生 Andi Gutmans 与 Zeev Suraski 所开发。他们之后在以色列的 Ramat Gan 创立了 Zend技术公司。 Zend 一名为他们名字 Zeev 和 Andi 所组成的新字。 目前版本为Zend引擎 II. Zeng Engine就是把 PHP 的边解释边执行的运行方式改为先进行预编译 (Compile),然后再执行(Execute) 编译:zend_compile php-src→Token → parse→ OPCODE 执行:zend_execute zend engine 根据OPCODE 找到对应Handler处理 Zend VM PHP 脚本执行 $ php ./hello.php 1. 传递给php程序需要执行的文件, php程序完成基本的准备工作后启动PHP及Zend引擎, 加载注册的扩展模块。 2.初始化完成后读取脚本文件,Zend引擎对脚本文件 进行词法分析,语法分析。然后编译成opcode执行。 如过安装了apc之类的opcode缓存, 编译环节可能会 被跳过而直接从缓存中读取opcode 执行。 词法分析和语法分析 编程语言的编译器(compiler)或解释器(interpreter)一般包括两大部分: 读取源程序,并处理语言结构。 处理语言结构并生成目标程序。 Lex和Yacc可以解决第一个问题。 第一个部分也可以分为两个部分: 将代码切分为一个个的标记(token)。 处理程序的层级结构(hierarchical structure)。 Lex和Yacc是Unix下的两个文本处理工具, 主要用于编写编译器, 也可以做其他用途。 Lex(词法分析生成器:A Lexical Analyzer Generator)。 Yacc(Yet Another Compiler-Compiler) php使用的是re2c(词法分析) & bison(BNF描述语法,语法分析 ,兼容yacc) zend_language_scanner.l re2c -> zend_language_scanner.c zend_language_parser.y bison -> zend_language_parser.c BNF巴科斯范式 巴科斯范式(BNF: Backus-Naur Form)一种形式化符号来描 述 给定语言的语法 双引号中的字("word")代表着这些字符本身。 而double_quote用来代表双引号。 在双引号外的字(有可能有下划线)代表着语法部分。 尖括号( < > )内包含的为必选项。 方括号( [ ] )内包含的为可选项。 大括号( { } )内包含的为可重复0至无数次的项。 竖线( | )表示在其左右两边任选一项,相当于"OR"的意 思。 BNF example 这是用BNF来定义的Java语言中的For语句的实例: FOR_STATEMENT ::= "for" "(" ( variable_declaration | ( expression ";" ) | ";" ) [ expression ] ";" [ expression ] ")" statement BNF in php bison statement: unticked_statement { DO_TICKS(); } //无标记 | T_STRING':' { zend_do_label(&$1 TSRMLS_CC); } //有标记 用于goto ; unticked_statement: .... | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC ); } | T_ECHO echo_expr_list';' echo_expr_list: echo_expr_list',' expr { zend_do_echo(&$3 TSRMLS_CC); } | expr { zend_do_echo(&$1 TSRMLS_CC); } ; zend_do_echo void zend_do_echo(const znode *arg TSRMLS_DC) /* {{{ */ { zend_op *opline = get_next_op(CG(active_Q_array) TSRMLS_CC); //获取下个opline指针 opline->opcode = ZEND_ECHO; //设置opcode opline->op1 = *arg; SET_UNUSED(opline->op2); } OPCODE opcode是计算机指令中的一部分,用于指定要执行的操 作, 指令的格式和规范由处理器的指令规范指定。 除了指令本身以外通常还有指令所需要的操作数, 可能有的指令不需要显式的操作数。 这些操作数可能是寄 存器中的值,堆栈中的值,某块内存的值或者IO端口中的 值等等。 opcode还有另一种称谓: 字节码(byte codes)。 例如Java 虚拟机(JVM),.NET的通用中间语言(CIL: Common Intermeditate Language)等等。 PHP的opcodePHP中的opcode则属于前面介绍中的后着, PHP是构建在Zend虚拟机(Zend VM)之上的。 PHP的opcode就是Zend虚拟机中的指令。 四 变量和数组实现原理 PHP是弱类型语言,这并不表示PHP没有类型,在PHP 中,存在8种变量类型,可以分为三类 标量类型: boolean、integer、float(double)、string 复合类型: array、object 特殊类型: resource、NULL 变量 变量在PHP内核中以zval来保存. zval是zend中另一个非常重要的数据结构,用来标识并实现php变量 typedef struct _zval_struct zval; struct _zval_struct { zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct {char *val; int len;} str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; refcount & copy on write refcount 引用计数在内存回收、字符串操作等地方使用非常广泛。 Php中的变量就是引用计数的典型应用 Zval的引用计数通过成员变量 is_ref和ref_count实现,通过引用计数,多个变量可以共享同一份数据。 避免频繁拷贝带来的大量消耗,在进行赋值操作时,zend将变量 指向相同的zval同时ref_count++,在unset操作时,对应的 ref_count-1。只有ref_count减为0时才会真正执行销毁操作,如果是引用赋 值, 则zend会修改is_ref为1 copy on write 写时复制 当试图写入一个变量时,Zend若发现该变量指向的zval被多个变量共 享, 则为其复制一份ref_count为1的zval,并递减原zval的refcount, 这个过程称为“zval分离”。可见,只有在有写操作发生时zend才进行拷贝 操作, 因此也叫copy-on-write(写时拷贝)对于引用型变量,其要求和非引用型相 反, 引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。 数组 数组是PHP中最常用,也是最强大变量类型, 它可以存储其他类型的数据,而且提供各种内置操作函 数。 数组的存储相对于其他变量要复杂一些, 数组的值存储在zvalue_value.ht字段中, 它是一个HashTable类型的数据。 PHP的数组使用哈希表来存储关联数据。 哈希表是一种高效的键值对存储结构。 PHP的哈希表实现中使用了两个数据结构HashTable和 Bucket。 PHP所有的工作都由哈希表实现, 在下节HashTable中将进行哈希表基本概念的介绍以及PHP 的 哈希表实现。 Zend Hash 键(key):用于操作数据的标示,例如PHP数组中的索引, 或者字符串键等等。 槽(slot/bucket):哈希表中用于保存数据的一个单元, 也就是数据真正存放的容器。 哈希函数(hash function):将key映射(map)到数据应该存 放的slot所在位置的函数。 哈希冲突(hash collision):哈希函数将两个不同的key映射 到同一个索引的情况。 Zend Hash typedef struct _hashtable { uint nTableSize; // hash Bucket的大小,最小为8, 以2x增长。 uint nTableMask; // nTableSize-1 , 索引取值的优化 uint nNumOfElements; // hash Bucket中当前存在的元素个 数,count()函数会直接返回此值 ulong nNextFreeElement; // 下一个数字索引的位置 Bucket *pInternalPointer; // 当前遍历的指针 (foreach比for快的原因之一) Bucket *pListHead; // 存储数组头元素指针 Bucket *pListTail; // 存储数组尾元素指针 Bucket **arBuckets; // 存储hash数组 dtor_func_t pDestructor; zend_bool persistent; unsigned char nApplyCount; // 标记当前hash Bucket被 递归访问的次数(防止多次递归) zend_bool bApplyProtection;// 标记当前hash桶允许不 允许多次访问,不允许时,最多只能递归3次 #if ZEND_DEBUG int inconsistent; #endif } HashTable Bucket typedef struct bucket { ulong h; // 对char *key进行hash后的值, 或者是用户指定的数字索引值 uint nKeyLength; // hash关键字的长度,如果 数组索引为数字,此值为0 void *pData; // 指向value,一般是用户 数据的副本,如果是指针数据, 则指向pDataPtr void *pDataPtr; //如果是指针数据,此值会指向 真正的value, 同时上面pData会指向此值 struct bucket *pListNext; // 整个hash表的下一元素 struct bucket *pListLast; // 整个哈希表该元素的上一个元素 struct bucket *pNext; // 存放在同一个hash Bucket内的 下一个元素 struct bucket *pLast; // 同一个哈希bucket的上一个元素 // 保存当前值所对于的key字符串,这个字段只能定义在最后 ,实现变长结构体 char arKey[1]; } Bucket init hash ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; //... if (nSize >= 0x80000000) { /* prevent overflow */ ht->nTableSize = 0x80000000; } else { while ((1U << i) < nSize) { i++; } ht->nTableSize = 1<< i; } // ... ht->nTableMask = ht->nTableSize - 1; /* Uses ecalloc() so that Bucket* == NULL*/ if (persistent) { tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *)); if (!tmp) { return FAILURE; } ht->arBuckets = tmp; } else { tmp = (Bucket **) ecalloc_rel(ht->nTableSize,sizeof(Bucket *)); if (tmp) { ht->arBuckets = tmp; } } return SUCCESS; } hash find ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData) { ulong h; uint nIndex; Bucket *p; IS_CONSISTENT(ht); h = zend_inline_hash_func(arKey,nKeyLength); nIndex = h & ht->nTableMask; p = ht->arBuckets[nIndex]; while (p != NULL) { if ((p->h == h) && (p->nKeyLength == nKeyLength)) { if (!memcmp(p->arKey, arKey, nKeyLength)) { *pData = p->pData; return SUCCESS; } } p = p->pNext; } return FAILURE; } Zend Hash 图示 五 PHP扩展编写 ext_skel —extname=EXTNAME vim config.m4 phpize configure make php.ini PHP_FUNCTION #define PHP_FUNCTION ZEND_FUNCTION #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_FN(name) zif_##name #define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS) #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC TSRMLS_XX宏 定义(线程安全资源管理器),这些宏提供给扩展拥有独自的全局变量 Thread Safe Resource Manager local storage D declaration C Call C comma(逗号) 参数获取 zend_parse_parameters int zend_get_parameters(int ht, int param_count, char * typestr,...); b Boolean l Integer d Floating point s String r Resource a Array o Object instance O Object instance of a specified type z Non-specific zval Z Dereferenced non-specific zval 函数返回 zend_parse从函数直接返回值的宏: RETURN_RESOURCE(resource) 返回一个资源。 RETURN_BOOL(bool) 返回一个布尔值。 RETURN_NULL() 返回一个空值。 RETURN_LONG(long) 返回一个长整数。 RETURN_DOUBLE(double) 返回一个双精度浮点数。 RETURN_STRING(string,duplicate) 返回一个字符串。duplicate 表示这个字符是否使用 estrdup() 进行复制。 RETURN_STRINGL(string,length,duplicate) 返回一个定长的字符串。其余跟 RETURN_STRING相同。这个宏速 度更快 而且是二进制安全的。 RETURN_EMPTY_STRING() 返回一个空字符串。 RETURN_FALSE 返回一个布尔值假。 RETURN_TRUE 返回一个布尔值真。 设置函数返回值的宏: RETVAL_RESOURCE(resource) 设定返回值为指定的一个资源。 RETVAL_BOOL(bool) 设定返回值为指定的一个布尔值。 RETVAL_NULL 设定返回值为空值 RETVAL_LONG(long) 设定返回值为指定的一个长整数。 RETVAL_DOUBLE(double) 设定返回值为指定的一个双精度浮点数。 RETVAL_STRING(string, duplicate) 设定返回值为指定的一个字符串,duplicate 含义同 RETURN_STRING。 RETVAL_STRINGL(string, length, duplicate) 设定返回值为指定的一个定长的字符串。其余跟 RETVAL_STRING 相同。 这个宏速度更快而且是二进制安全的。 RETVAL_EMPTY_STRING设定返回值为空字符串。 RETVAL_FALSE 设定返回值为布尔值假。 RETVAL_TRUE 设定返回值为布尔值真. PHP internal reference Extending and Embedding PHP TIPI深入理解PHP内核 wiki.php.net lurance blog Thank You!
还剩33页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

离开的123

贡献于2013-10-20

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf