上一次修改时间:2018-06-14 01:47:28

PHP变量实现

  1. PHP为弱类型的语言,变量存储结构为:(zval结构体定义在Zend/zend.h文件)

  2. struct _zval_struct {    
        /* Variable information */    
        zvalue_value value;        //存储变量的值,联合体存储    
        zend_uint refcount__gc;    //表示引用计数    
        zend_uchar type;    /*变量具体的类型,type的值可以为:IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一*/    
        zend_uchar is_ref__gc;    //表示是否为引用
    };
    
    //存储PHP变量值的联合体
    typedef union _zvalue_value {     
        long lval;  //boolean,integer存储     
        double dval; //float存     
        struct {         
            char *val;         
            int len;     
        } str;    //字符串存     
        HashTable *ht; //数组存储     
        zend_object_value obj;  //对象存储     
        zend_ast *ast;    //5.6.6中有,5,3中没有
    } zvalue_value;
    
    //对象存储的结构体,PHP的对象只有在运行时才会被创建,EG宏是一个全局结构体,用于保存在运行时的数据。其中就包括了用来保存所有被创建的对象的对象池,EG(objects_store),而object对象值内容的zend_object_handle域就是当前对象在对象池中所在的索引
    typedef struct _zend_object_value {    
        zend_object_handle handle;  //unsigned int类型,EG(objects_store).object_buckets的索引     
        const zend_object_handlers *handlers; /*handlers字段则是将对象进行操作时的处理函数保存起来*/
    } zend_object_value;
  3. PHP中数组的实现采用的是哈希表;解决哈希冲突的方法则采用的是链接法;哈希表最根本的弱点是:开源算法和哈希实现的确定性以及可预测性, 攻击者可以利用特殊构造的key来进行攻击。要解决这个问题的方法则是让攻击者无法轻易构造 能够进行攻击的key序列。PHP采用的是一种 治标不治本的做法: 限制用户提交数据字段数量 这样可以避免大部分的攻击;

  4. PHP的哈希表定义为:(HashTable结构体用于保存整个哈希表需要的基本信息, 而Bucket结构体用于保存具体的数据内容)

  5. 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;       //指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。    
       unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)    
       zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
    #if ZEND_DEBUG    
       int inconsistent;
    #endif
    } HashTable;
    
    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;
  6. 哈希结构图

QQ图片20180614014520.png

  1. 1