• 技术文章 >后端开发 >php教程

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

    2016-06-23 13:57:36原创397
              

    |=-----------------------------------------------------------------------=|

    |=---------------------=[ PHP内核中的变量和数据类型]=--------------------=|

    |=-----------------------------------------------------------------------=|

    |=--------------------------=[ by d4shman ]=-----------------------------=|

    |=-----------------------------------------------------------------------=|

    |=-------------------------=[ May 6, 2014 ]=---------------------------=|

    |=-----------------------------------------------------------------------=|


    (_____ \| | | (_____ \ /\ / _____) | / )

    _____) ) |__ | |_____) ) / \ | / | | / /

    | ____/| __)| (_____ ( / /\ \| | | |< <

    | | | | | | | | |__| | \_____| | \ \

    |_| |_| |_| |_|______|\______)_| \_) (向phrack致敬!)


    <--------------------------( Table of Contents )-------------------------->


    0x01 变量的结构和类型

    0x02 哈希表--PHP的灵魂

    0x03 常量

    0x04 参考文献

    <------------------------------------------------------------------------->


    /////

    0x01 变量的结构和类型

    /////

    1.数据类型

    1.1静态类型语言(C/Java),编译时确定

    1.2动态类型语言(php/python),运行时确定

    1.3无类型语言(汇编),操作的底层存储


    2.php内核中所有的变量使用同一种数据结构zval来保存,而这个结构同时表示php中各种数据类型,它不仅仅包含变量的值,也包含变量的类型。这就是php弱类型的核心。

    php中的8中数据类型:

    2.1标量类型: boolean, integer, float, string

    2.2复合类型: array, object

    2.3特殊类型: resource, null

    3.zval结构体(在php源码目录下Zend/zend.h中定义):

    struct _zval_struct{

    /*Variable information*/

    zvalue_value value /*value, 变量的值*/

    zend_uint refcount__gc /*reference count, 引用计数器*/

    zend_uchar type /*active type, 变量的类型*/

    zend_uchar is_ref__gc; /*变量是否被引用*/


    4.变量类型:

    /*data types */

    #define IS_NULL 0

    #define IS_LONG 1

    #define IS_DOUBLE 2

    #define IS_BOOL 3

    #define IS_ARRAY 4

    #define IS_OBJECT 5

    #define IS_STRING 6

    #define IS_RESOURCE 7

    #define IS_CONSTANT 8

    #define IS_CONSTANT_ARRAY 9

    #define IS_CALLABLE 10

    5.变量的值存储

    typedef union _zvalue_value {

    long lval; /*long、bool、resource类型*/

    double dval ; /*double 类型*/

    struct { /*string 类型, len保存了字符串的长度*/

    char *val;

    int len;

    } str;

    HashTable *ht; /*数组, 用HashTable实现*/

    zend_object_value obj; /*object 类型*/

    } zvalue_value;

    这里之所以用共同体(union)是因为一个变量只可能有一种类型,符合共同体的特性,如果使用结构体则会浪费内存。

    实例:创建一个值为10的整型变量lvar,用php脚本的话很简单,就是:$lvar = 10

    而PHP内核中的实现可能就是类似下面这样:

    zval lval;

    Z_TYPE(lvar) = IS_LONG;

    Z_LVAL(lvar) = 10;

    /////

    0x02 哈希表--PHP的灵魂

    /////

    1.为什么用哈希表

    哈希表通常提供CRUD(Create, Read, Update, Delete)操作,设计合理的哈希表中,这些操作时间复杂度为O(1),这也是它被钟爱的原因。

    hash(key) -> index

    2.哈希表的实现:结构体 bucket和_hashtable组成了完整的HashTable。

    首先看bucket结构体(定义在 Zend/zend_hash.h):

    typedef struct bucket {

    ulong h; /*hash值*/

    uint nKeyLength; /*key的长度*/

    void *pData; /*要保存的内存块地址,通常是malloc来的地址*/

    void *pDataPtr; /*保存指针数据,不经过malloc的指针,防止产生内存碎片*/

    struct bucket *pListNext; /*bucket中具有同一hash值的下一个元素*/

    struct bucket *pListLast; /*bucket中具有同一hash值的上一个元素*/

    struct bucket *pNext; /*双向链表的下一个元素*/

    struct bucket *pLast; /*双向链表的上一个元素*/

    const char *arKey; /*保存key*/

    } Bucket;

    可以看出bucket是一个双向链表,这是为了解决多个key冲突的问题(即算法导论中的链接法)

    再看_hashtable结构体:

    typedef struct _hashtable {

    uint nTableSize; /*bucket数组的大小*/

    uint nTableMask;

    uint nNumOfElements; /*HashTable中元素的个数*/

    ulong nNextFreeElement; /*下一个可用的Bucket位置*/

    Bucket *pInternalPointer /*遍历HashTable元素*/

    Bucket *pListHead; /*双向链表表头*/

    Bucket *pListTail; /*双向链表表尾*/

    Bucket **arBuckets; /*Bucket数组*/

    } HashTable;

    ========

    此处为HashTable的结构图

    ========


    3.神奇的数字--33

    见我原来的一篇博客:http://blog.csdn.net/wusuopubupt/article/details/11479869

    下面是PHP源码中的一段注释:

    /*

    * DJBX33A (Daniel J. Bernstein, Times 33 with Addition)

    *

    * This is Daniel J. Bernstein's popular `times 33' hash function as

    * posted by him years ago on comp.lang.c. It basically uses a function

    * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best

    * known hash functions for strings. Because it is both computed very

    * fast and distributes very well.

    *

    * The magic of number 33, i.e. why it works better than many other

    * constants, prime or not, has never been adequately explained by

    * anyone. So I try an explanation: if one experimentally tests all

    * multipliers between 1 and 256 (as RSE did now) one detects that even

    * numbers are not useable at all. The remaining 128 odd numbers

    * (except for the number 1) work more or less all equally well. They

    * all distribute in an acceptable way and this way fill a hash table

    * with an average percent of approx. 86%.

    *

    * If one compares the Chi^2 values of the variants, the number 33 not

    * even has the best value. But the number 33 and a few other equally

    * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great

    * advantage to the remaining numbers in the large set of possible

    * multipliers: their multiply operation can be replaced by a faster

    * operation based on just one shift plus either a single addition

    * or subtraction operation. And because a hash function has to both

    * distribute good _and_ has to be very fast to compute, those few

    * numbers should be preferred and seems to be the reason why Daniel J.

    * Bernstein also preferred it.

    *

    *

    * -- Ralf S. Engelschall

    */


    4.哈希表的操作接口(省略了部分参数)

    初始化HashTable:int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction);

    添加新hash值: int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData)

    查找hash: int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData);



    /////

    0x03 常量

    /////

    1.常量的内部结构

    typedef struct _zend_constant {

    zval value;

    int flags; /*常量标记,如 CONST_PERSISTENT | CONST_CS */

    char *name;

    uint name_len;

    int module_number;

    } zend_constant;


    2.define定义常量的过程

    define的实现(定义在Zend/zend_builtin_functions.c),下面是部分核心代码:

    ZEND_FUNCTION(define)

    {

    /* 检查常量名是否存在 */

    if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {

    zend_error(E_WARNING, "Class constants cannot be defined or redefined");

    RETURN_FALSE;

    }

    ... // 类常量定义 此处不做介绍

    c.value = *val;

    zval_copy_ctor(&c.value);

    if (val_free) {

    zval_ptr_dtor(&val_free);

    }

    c.flags = case_sensitive; /* 大小写敏感 */

    c.name = zend_strndup(name, name_len);

    c.name_len = name_len+1;

    c.module_number = PHP_USER_CONSTANT;

    if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) { /*注册常量*/

    RETURN_TRUE;

    } else {

    RETURN_FALSE;

    }

    }

    3.魔术常量

    PHP中的魔术常量,虽然叫做常量,但它们的值实际上随它们在代码中的位置而变化的。

    __LINE__ 文件中的当前行号。

    __FILE__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。

    __DIR__ 文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于 dirname(__FILE__)。

    __FUNCTION__ 函数名称

    __CLASS__ 类的名称。类名包括其被声明的作用区域(例如 Foo\Bar)。

    __TRAIT__ Trait 的名字。Trait 名包括其被声明的作用区域(例如 Foo\Bar)。

    __METHOD__ 类的方法名

    __NAMESPACE__ 当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)。

    PHP内核会在词法解析时将这些常量的内容赋值进行替换,而不是在运行时进行分析。 举个例子:

    echo __LINE__;

    function demo() {

    echo __FUNCTION__;

    }

    demo();

    ?>

    PHP已经在词法解析时将这些常量换成了对应的值,以上的代码可以看成如下的PHP代码:

    echo 2;

    function demo() {

    echo "demo";

    }

    demo();

    ?>


    ===========

    此处涉及编译原理知识,需补充。

    ===========

    /////

    0x04 参考文献

    /////


    TIPI: http://www.php-internals.com/book/?p=chapt03/03-00-variable-and-data-types

    声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
    上一篇:想用PHP抓取某网站库存数据 下一篇:怎么把这两个函数相同的键值相加形成新的数组
    大前端线上培训班

    相关文章推荐

    • PHP中的命名空间定义与使用(实例详解)• 带你分清类中的构造函数与析构函数• PHP中clone关键字和__clone()方法的使用(实例详解)• 五分钟带你了解PHP中的魔术方法(实例详解)• 怎样去搞定PHP类的继承?(总结分享)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网