当前位置:Gxlcms > PHP教程 > 【PHP内核学习】变量跟数据类型

【PHP内核学习】变量跟数据类型

时间:2021-07-01 10:21:17 帮助过:15人阅读

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

人气教程排行