Home > Article > Backend Development > 【PHP内核学习】变量和数据类型
<p class="sycode"> |=-----------------------------------------------------------------------=| </p> <p class="sycode"> |=---------------------=[ PHP内核中的变量和数据类型]=--------------------=| </p> <p class="sycode"> |=-----------------------------------------------------------------------=| </p> <p class="sycode"> |=--------------------------=[ by d4shman ]=-----------------------------=| </p> <p class="sycode"> |=-----------------------------------------------------------------------=| </p> <p class="sycode"> |=-------------------------=[ May 6, 2014 ]=---------------------------=| </p> <p class="sycode"> |=-----------------------------------------------------------------------=| </p> <p class="sycode"> <br> </p> <p class="sycode"> (_____ \| | | (_____ \ /\ / _____) | / ) </p> <p class="sycode"> _____) ) |__ | |_____) ) / \ | / | | / / </p> <p class="sycode"> | ____/| __)| (_____ ( / /\ \| | | | </p><p class="sycode"> | | | | | | | | |__| | \_____| | \ \ </p> <p class="sycode"> |_| |_| |_| |_|______|\______)_| \_) (向phrack致敬!) </p> <p class="sycode"> <br> </p> <p class="sycode"> </p> <p class="sycode"> <br> </p> <p class="sycode"> 0x01 变量的结构和类型 </p> <p class="sycode"> 0x02 哈希表--PHP的灵魂 </p> <p class="sycode"> 0x03 常量 </p> <p class="sycode"> 0x04 参考文献 </p> <p class="sycode"> </p> <p class="sycode"> <br> </p> <p class="sycode"> ///// </p> <p class="sycode"> 0x01 变量的结构和类型 </p> <p class="sycode"> ///// </p> <p class="sycode"> 1.数据类型 </p> <p class="sycode"> 1.1静态类型语言(C/Java),编译时确定 </p> <p class="sycode"> 1.2动态类型语言(php/python),运行时确定 </p> <p class="sycode"> 1.3无类型语言(汇编),操作的底层存储 </p> <p class="sycode"> <br> </p> <p class="sycode"> 2.php内核中所有的变量使用同一种数据结构zval来保存,而这个结构同时表示php中各种数据类型,它不仅仅包含变量的值,也包含变量的类型。这就是php弱类型的核心。 </p> <p class="sycode"> php中的8中数据类型: </p> <p class="sycode"> 2.1标量类型: boolean, integer, float, string </p> <p class="sycode"> 2.2复合类型: array, object </p> <p class="sycode"> 2.3特殊类型: resource, null </p> <p class="sycode"> </p> <p class="sycode"> 3.zval结构体(在php源码目录下Zend/zend.h中定义): </p> <p class="sycode"> struct _zval_struct{ </p> <p class="sycode"> /*Variable information*/ </p> <p class="sycode"> zvalue_value value /*value, 变量的值*/ </p> <p class="sycode"> zend_uint refcount__gc /*reference count, 引用计数器*/ </p> <p class="sycode"> zend_uchar type /*active type, 变量的类型*/ </p> <p class="sycode"> zend_uchar is_ref__gc; /*变量是否被引用*/ </p> <p class="sycode"> } </p> <p class="sycode"> <br> </p> <p class="sycode"> 4.变量类型: </p> <p class="sycode"> /*data types */ </p> <p class="sycode"> #define IS_NULL 0 </p> <p class="sycode"> #define IS_LONG 1 </p> <p class="sycode"> #define IS_DOUBLE 2 </p> <p class="sycode"> #define IS_BOOL 3 </p> <p class="sycode"> #define IS_ARRAY 4 </p> <p class="sycode"> #define IS_OBJECT 5 </p> <p class="sycode"> #define IS_STRING 6 </p> <p class="sycode"> #define IS_RESOURCE 7 </p> <p class="sycode"> #define IS_CONSTANT 8 </p> <p class="sycode"> #define IS_CONSTANT_ARRAY 9 </p> <p class="sycode"> #define IS_CALLABLE 10 </p> <p class="sycode"> </p> <p class="sycode"> 5.变量的值存储 </p> <p class="sycode"> typedef union _zvalue_value { </p> <p class="sycode"> long lval; /*long、bool、resource类型*/ </p> <p class="sycode"> double dval ; /*double 类型*/ </p> <p class="sycode"> struct { /*string 类型, len保存了字符串的长度*/ </p> <p class="sycode"> char *val; </p> <p class="sycode"> int len; </p> <p class="sycode"> } str; </p> <p class="sycode"> HashTable *ht; /*数组, 用HashTable实现*/ </p> <p class="sycode"> zend_object_value obj; /*object 类型*/ </p> <p class="sycode"> } zvalue_value; </p> <p class="sycode"> </p> <p class="sycode"> 这里之所以用共同体(union)是因为一个变量只可能有一种类型,符合共同体的特性,如果使用结构体则会浪费内存。 </p> <p class="sycode"> </p> <p class="sycode"> 实例:创建一个值为10的整型变量lvar,用php脚本的话很简单,就是:$lvar = 10 </p> <p class="sycode"> 而PHP内核中的实现可能就是类似下面这样: </p> <p class="sycode"> zval lval; </p> <p class="sycode"> Z_TYPE(lvar) = IS_LONG; </p> <p class="sycode"> Z_LVAL(lvar) = 10; </p> <p class="sycode"> </p> <p class="sycode"> ///// </p> <p class="sycode"> 0x02 哈希表--PHP的灵魂 </p> <p class="sycode"> ///// </p> <p class="sycode"> 1.为什么用哈希表 </p> <p class="sycode"> 哈希表通常提供CRUD(Create, Read, Update, Delete)操作,设计合理的哈希表中,这些操作时间复杂度为O(1),这也是它被钟爱的原因。 </p> <p class="sycode"> hash(key) -> index </p> <p class="sycode"> </p> <p class="sycode"> 2.哈希表的实现:结构体 bucket和_hashtable组成了完整的HashTable。 </p> <p class="sycode"> 首先看bucket结构体(定义在 Zend/zend_hash.h): </p> <p class="sycode"> typedef struct bucket { </p> <p class="sycode"> ulong h; /*hash值*/ </p> <p class="sycode"> uint nKeyLength; /*key的长度*/ </p> <p class="sycode"> void *pData; /*要保存的内存块地址,通常是malloc来的地址*/ </p> <p class="sycode"> void *pDataPtr; /*保存指针数据,不经过malloc的指针,防止产生内存碎片*/ </p> <p class="sycode"> struct bucket *pListNext; /*bucket中具有同一hash值的下一个元素*/ </p> <p class="sycode"> struct bucket *pListLast; /*bucket中具有同一hash值的上一个元素*/ </p> <p class="sycode"> struct bucket *pNext; /*双向链表的下一个元素*/ </p> <p class="sycode"> struct bucket *pLast; /*双向链表的上一个元素*/ </p> <p class="sycode"> const char *arKey; /*保存key*/ </p> <p class="sycode"> } Bucket; </p> <p class="sycode"> </p> <p class="sycode"> 可以看出bucket是一个双向链表,这是为了解决多个key冲突的问题(即算法导论中的链接法) </p> <p class="sycode"> </p> <p class="sycode"> </p> <p class="sycode"> 再看_hashtable结构体: </p> <p class="sycode"> typedef struct _hashtable { </p> <p class="sycode"> uint nTableSize; /*bucket数组的大小*/ </p> <p class="sycode"> uint nTableMask; </p> <p class="sycode"> uint nNumOfElements; /*HashTable中元素的个数*/ </p> <p class="sycode"> ulong nNextFreeElement; /*下一个可用的Bucket位置*/ </p> <p class="sycode"> Bucket *pInternalPointer /*遍历HashTable元素*/ </p> <p class="sycode"> Bucket *pListHead; /*双向链表表头*/ </p> <p class="sycode"> Bucket *pListTail; /*双向链表表尾*/ </p> <p class="sycode"> Bucket **arBuckets; /*Bucket数组*/ </p> <p class="sycode"> } HashTable; </p> <p class="sycode"> </p> <p class="sycode"> ======== </p> <p class="sycode"> 此处为HashTable的结构图 </p> <p class="sycode"> ======== </p> <p class="sycode"> <br> </p> <p class="sycode"> 3.神奇的数字--33 </p> <p class="sycode"> 见我原来的一篇博客:http://blog.csdn.net/wusuopubupt/article/details/11479869 </p> <p class="sycode"> 下面是PHP源码中的一段注释: </p> <p class="sycode"> /* </p> <p class="sycode"> * DJBX33A (Daniel J. Bernstein, Times 33 with Addition) </p> <p class="sycode"> * </p> <p class="sycode"> * This is Daniel J. Bernstein's popular `times 33' hash function as </p> <p class="sycode"> * posted by him years ago on comp.lang.c. It basically uses a function </p> <p class="sycode"> * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best </p> <p class="sycode"> * known hash functions for strings. Because it is both computed very </p> <p class="sycode"> * fast and distributes very well. </p> <p class="sycode"> * </p> <p class="sycode"> * The magic of number 33, i.e. why it works better than many other </p> <p class="sycode"> * constants, prime or not, has never been adequately explained by </p> <p class="sycode"> * anyone. So I try an explanation: if one experimentally tests all </p> <p class="sycode"> * multipliers between 1 and 256 (as RSE did now) one detects that even </p> <p class="sycode"> * numbers are not useable at all. The remaining 128 odd numbers </p> <p class="sycode"> * (except for the number 1) work more or less all equally well. They </p> <p class="sycode"> * all distribute in an acceptable way and this way fill a hash table </p> <p class="sycode"> * with an average percent of approx. 86%. </p> <p class="sycode"> * </p> <p class="sycode"> * If one compares the Chi^2 values of the variants, the number 33 not </p> <p class="sycode"> * even has the best value. But the number 33 and a few other equally </p> <p class="sycode"> * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great </p> <p class="sycode"> * advantage to the remaining numbers in the large set of possible </p> <p class="sycode"> * multipliers: their multiply operation can be replaced by a faster </p> <p class="sycode"> * operation based on just one shift plus either a single addition </p> <p class="sycode"> * or subtraction operation. And because a hash function has to both </p> <p class="sycode"> * distribute good _and_ has to be very fast to compute, those few </p> <p class="sycode"> * numbers should be preferred and seems to be the reason why Daniel J. </p> <p class="sycode"> * Bernstein also preferred it. </p> <p class="sycode"> * </p> <p class="sycode"> * </p> <p class="sycode"> * -- Ralf S. Engelschall <rse> </rse></p> <p class="sycode"> */ </p> <p class="sycode"> <br> </p> <p class="sycode"> </p> <p class="sycode"> 4.哈希表的操作接口(省略了部分参数) </p> <p class="sycode"> 初始化HashTable:int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction); </p> <p class="sycode"> 添加新hash值: int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData) </p> <p class="sycode"> 查找hash: int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData); </p> <p class="sycode"> <br> </p> <p class="sycode"> <br> </p> <p class="sycode"> ///// </p> <p class="sycode"> 0x03 常量 </p> <p class="sycode"> ///// </p> <p class="sycode"> 1.常量的内部结构 </p> <p class="sycode"> typedef struct _zend_constant { </p> <p class="sycode"> zval value; </p> <p class="sycode"> int flags; /*常量标记,如 CONST_PERSISTENT | CONST_CS */ </p> <p class="sycode"> char *name; </p> <p class="sycode"> uint name_len; </p> <p class="sycode"> int module_number; </p> <p class="sycode"> } zend_constant; </p> <p class="sycode"> <br> </p> <p class="sycode"> 2.define定义常量的过程 </p> <p class="sycode"> define的实现(定义在Zend/zend_builtin_functions.c),下面是部分核心代码: </p> <p class="sycode"> </p> <p class="sycode"> ZEND_FUNCTION(define) </p> <p class="sycode"> { </p> <p class="sycode"> /* 检查常量名是否存在 */ </p> <p class="sycode"> if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) { </p> <p class="sycode"> zend_error(E_WARNING, "Class constants cannot be defined or redefined"); </p> <p class="sycode"> RETURN_FALSE; </p> <p class="sycode"> } </p> <p class="sycode"> </p> <p class="sycode"> ... // 类常量定义 此处不做介绍 </p> <p class="sycode"> </p> <p class="sycode"> c.value = *val; </p> <p class="sycode"> zval_copy_ctor(&c.value); </p> <p class="sycode"> if (val_free) { </p> <p class="sycode"> zval_ptr_dtor(&val_free); </p> <p class="sycode"> } </p> <p class="sycode"> c.flags = case_sensitive; /* 大小写敏感 */ </p> <p class="sycode"> c.name = zend_strndup(name, name_len); </p> <p class="sycode"> c.name_len = name_len+1; </p> <p class="sycode"> c.module_number = PHP_USER_CONSTANT; </p> <p class="sycode"> if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) { /*注册常量*/ </p> <p class="sycode"> RETURN_TRUE; </p> <p class="sycode"> } else { </p> <p class="sycode"> RETURN_FALSE; </p> <p class="sycode"> } </p> <p class="sycode"> } </p> <p class="sycode"> </p> <p class="sycode"> 3.魔术常量 </p> <p class="sycode"> PHP中的魔术常量,虽然叫做常量,但它们的值实际上随它们在代码中的位置而变化的。 </p> <p class="sycode"> __LINE__ 文件中的当前行号。 </p> <p class="sycode"> __FILE__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。 </p> <p class="sycode"> __DIR__ 文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于 dirname(__FILE__)。 </p> <p class="sycode"> __FUNCTION__ 函数名称 </p> <p class="sycode"> __CLASS__ 类的名称。类名包括其被声明的作用区域(例如 Foo\Bar)。 </p> <p class="sycode"> __TRAIT__ Trait 的名字。Trait 名包括其被声明的作用区域(例如 Foo\Bar)。 </p> <p class="sycode"> __METHOD__ 类的方法名 </p> <p class="sycode"> __NAMESPACE__ 当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)。 </p> <p class="sycode"> </p> <p class="sycode"> PHP内核会在词法解析时将这些常量的内容赋值进行替换,而不是在运行时进行分析。 举个例子: </p> <p class="sycode"> <?php </p> </p><p class="sycode"> echo __LINE__; </p> <p class="sycode"> function demo() { </p> <p class="sycode"> echo __FUNCTION__; </p> <p class="sycode"> } </p> <p class="sycode"> demo(); </p> <p class="sycode"> ?> </p> <p class="sycode"> PHP已经在词法解析时将这些常量换成了对应的值,以上的代码可以看成如下的PHP代码: </p> <p class="sycode"> <?php </p> </p><p class="sycode"> echo 2; </p> <p class="sycode"> function demo() { </p> <p class="sycode"> echo "demo"; </p> <p class="sycode"> } </p> <p class="sycode"> demo(); </p> <p class="sycode"> ?> </p> <p class="sycode"> <br> </p> <p class="sycode"> =========== </p> <p class="sycode"> 此处涉及编译原理知识,需补充。 </p> <p class="sycode"> =========== </p> <p class="sycode"> </p> <p class="sycode"> ///// </p> <p class="sycode"> 0x04 参考文献 </p> <p class="sycode"> ///// </p> <p class="sycode"> <br> </p> <p class="sycode"> TIPI: http://www.php-internals.com/book/?p=chapt03/03-00-variable-and-data-types </p>