下面讨论的PHP都是5.1.5版本的。
1. PHP源码文件 Zend/zend_globals.h 中的_zend_executor_globals定义了执行时全局的hash表:
/* symbol table cache */
HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
HashTable **symtable_cache_limit;
HashTable **symtable_cache_ptr;
HashTable *active_symbol_table;
HashTable symbol_table; /* main symbol table */
HashTable included_files; /* files already included */
HashTable *function_table; /* function symbol table */
HashTable *class_table; /* class table */
HashTable *zend_constants; /* constants table */
HashTable *in_autoload;
HashTable regular_list;
HashTable persistent_list;
HashTable *ini_directives;
这里面只列了hash表类型的字段,从中可以看出: 函数、类、常量、头文件等都是存储为hash表的。
2. PHP源码文件 Zend/zend_globals.h 中的下列结构应该说明declare 关键字可以声明的内容都有哪些,似乎目前只有ticks
typedef struct _zend_declarables {
zval ticks;
} zend_declarables;
3. hash表数据结构,Zend/zend_hash.h 文件中:
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
4. PHP内部的Hash函数, Zend/zend_hash.h中:
static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)
{
register ulong hash = 5381;
/* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough… */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
EMPTY_SWITCH_DEFAULT_CASE()
}
return hash;
}
5. hash表查找函数:
/* Returns SUCCESS if found and FAILURE if not. The pointer to the
* data is returned in pData. The reason is that there’s no reason
* someone using the hash table might not want to have NULL data
*/
ZEND_API int zend_hash_find(HashTable *ht, 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;
}
这里面:
h = zend_inline_hash_func(arKey, nKeyLength);
nIndex = h & ht->nTableMask;
就可以定位到具体的Bucket了,特别是这个按位与的操作,就可以知道Bucket的数量了,怎么算呢?
6. 文件Zend/zend_globals_macros.h中定义了EG:
# define EG(v) (executor_globals.v)
EG 就是executor_globals的意思,比如EG(ini_directives) 就是:
executor_globals.ini_directives
从上面的结构体executor_globals就可以知道这是在做什么了
7. Zend/zend_ini.h 中定义了ini变量的结构体:
struct _zend_ini_entry {
int module_number;
int modifiable;
char *name;
uint name_length;
ZEND_INI_MH((*on_modify));
void *mh_arg1;
void *mh_arg2;
void *mh_arg3;
char *value;
uint value_length;
char *orig_value;
uint orig_value_length;
int modified;
void (*displayer)(zend_ini_entry *ini_entry, int type);
};
8. ini_set 函数是在源码中是通过函数zend_alter_ini_entry来实现的,其中的modifytype参数值为:PHP_INI_USER