In php, the underlying implementation of arrays is the hash table, which all appear in the form of key-value. In the Zend Engine of php, there are dedicated APIs for operations on hash tables for different hash table operations. The Creation method is the same for each hash table initialization,
In php, the underlying implementation of arrays is the hash table, which all appear in the form of key-value. In the Zend Engine of php, there are dedicated APIs for operations on hash tables for different hash table operations.
Creation
For a hash table, the initialization method is the same. the following function zend_hash_init is used to complete the initialization:
int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent)
Ht is a pointer to a hash table, which can be referenced by an existing hashtable variable. You can also apply for memory for the new hashtable. The general method is:
ALLOC_HASHTABLE (ht), equivalent to ht = emalloc (sizeof (HashTable ));.
NSize is the maximum number of elements in a hash table, so that you can apply for memory in advance. If it is not an exponential multiple of 2, it will increase according to the following formula nSize = pow (2, ceil (log (nSize, 2); for example, if 5 is given, it will grow to 8. this mechanism should be adopted to facilitate memory management.
PHashFunction is a zend eigine function of previous versions. it is always set to NULL in the new version.
PDestructor points to the entry of the method called when the elements in the hash table are deleted (zend_hash_del () zend_hash_update (), that is, a corresponding callback function. If the method_name function is given, when the function is implemented:
Void method_name (void * pElement)
PElement points to the deleted element
Persistent is a flag indicating whether it is a persistent hash table. the persistent data is independent from the request and will not be canceled during RSHUTDOWN. However, if it is set to 1, the ht must use pemalloc () when applying for memory ().
For example, zend_hash_init (& EG (symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0) is displayed during symbol_table initialization in every php request lifecycle );
When unset, zval * stored in the hash table is sent to zval_ptr_dtor () for destruction.
Population:
There are four main functions for inserting and updating data in the hash table:
int zend_hash_add(HashTable *ht, char *arKey, uint nKeyLen, void *pData, uint nDataSize, void **pDest);int zend_hash_update(HashTable *ht, char *arKey, uint nKeyLen, void *pData, uint nDataSize, void **pDest);int zend_hash_index_update(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest);int zend_hash_next_index_insert(HashTable *ht, void *pData, uint nDataSize, void **pDest);
The first two functions add data with string indexes to hashtable. for example, in php, $ foo ['bar'] = 'barvalue', then in extension:
Zend_hash_add (fooHashTbl, "bar", sizeof ("bar"), & barZval, sizeof (zval *), NULL );
Add the corresponding key value and table value to hashtable.
The only difference between add and update is that if the key already exists, add will fail.
The last two functions are the data that adds a digital index to ht.
The zend_hash_next_index_insert () function directly calculates the next numeric index value instead of the index value parameter.
If you want to obtain the numeric index value of the next element, you can also obtain the index through zend_hash_next_free_element.
Ulong nextid = zend_hash_next_free_element (ht );
Zend_hash_index_update (ht, nextid, & data, sizeof (data), NULL );
The above code is equivalent:
Zend_hash_next_index_insert (HashTable * ht, & data, sizeof (data), NULL ).
The pDest parameter can be used to store the address values of newly added elements.
Recall: Search
Generally, there are two ways to obtain data in a hash table:
int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData);int zend_hash_index_find(HashTable *ht, ulong h, void **pData);
In the following example, we can see more clearly:
Void hash_sample (HashTable * ht, sample_data * data1) {sample_data * data2; ulong targetID = histogram (ht); // Obtain the location of the next index if (zend_hash_index_update (ht, targetID, data1, sizeof (sample_data), NULL) = FAILURE) {// Insert data data1 to the next index location of the hash table/* shocould never happen */return ;} if (zend_hash_index_find (ht, targetID, (void **) & data2) = FAILURE) {// use id to find values in the hash table, if it is found, place the value in data2. /* Very unlikely since we just added this element */return;}/* data1! = Data2, however * data1 = * data2 */}
In addition to obtaining values in the hash table, it is more important to know the existence of some elements:
int zend_hash_exists(HashTable *ht, char *arKey, uint nKeyLen);int zend_hash_index_exists(HashTable *ht, ulong h);
Index of strings and numbers respectively. The returned values are 1 and 0.
If (zend_hash_exists (EG (active_symbol_table), "foo", sizeof ("foo "))) {// Determine whether the foo variable exists in the symbol table of the activity/* $ foo is set */} else {/* $ foo does not exist */}
Quick Population and RecallYou can use zend_get_hash_value to increase the speed when you need to perform many operations on the key of the same string, such as checking whether there is a key, and then inserting and modifying it. The return value of this function can be used with functions of the quick series to accelerate the process. Because you do not need to re-calculate the hash value of the string, instead, you can directly use the existing
Hash value.
ulong zend_get_hash_value(char *arKey, uint nKeyLen);
You can use this return value to pass the following quick functions to accelerate the process:
int zend_hash_quick_add(HashTable *ht, char *arKey, uint nKeyLen, ulong hashval, void *pData, uint nDataSize, void **pDest);int zend_hash_quick_update(HashTable *ht, char *arKey, uint nKeyLen, ulong hashval, void *pData, uint nDataSize, void **pDest);int zend_hash_quick_find(HashTable *ht, char *arKey, uint nKeyLen, ulong hashval, void **pData);int zend_hash_quick_exists(HashTable *ht, char *arKey, uint nKeyLen, ulong hashval);
The following example shows how to copy data between two hash tables:
Void php_sample_hash_copy (HashTable * hta, HashTable * htb, char * arKey, uint nKeyLen TSRMLS_DC) {ulong hashval = zend_get_hash_value (arKey, nKeyLen ); // Obtain the hash value hashval zval ** copyval for acceleration; if (zend_hash_quick_find (hta, arKey, nKeyLen, hashval, (void **)©Val) = FAILURE) {// first, find the corresponding elements in the hta table and store them in copyval. /* ArKey doesn't actually exist */return;}/* The zval * is about to be owned by another hash table */(* copyval) -> refcount _ gc ++; // The number of times the corresponding zval * variable is referenced + 1 zend_hash_quick_update (htb, arKey, nKeyLen, hashval, copyval, sizeof (zval *), NULL); // place the copyval from hta in htb .}
Note that there is no zend_hash_del function.
Copy and MergingThere are three ways to copy data. First, let's look at the first one:
typedef void (*copy_ctor_func_t)(void *pElement);void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size);
Each element in the source will be copied to the target. the processing of pCopyConstructor allows you to add one to the ref_count of these variables when copying them. The original elements of the target with the same index position as those of the source will be replaced, while other elements will be retained.
Tmp is NULL here, which is used in earlier versions.
Size indicates the size of each element, generally sizeof (zval *).
void zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite);
An overwrite parameter is added. if it is not 0, it is the same as copy. if it is 0, the existing elements will not be copied.
The following functions allow selective replication using a merge check:
typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, void *source_data, zend_hash_key *hash_key, void *pParam);void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam);
The pMergeSource callback function allows you to merge data in a selective manner, instead of merging all data. This gives you the feeling that the function entry left by the quick sort function in C language can determine the sorting method.
The following is an example of an application:
Zend_bool associative_only (HashTable * ht, void * pData, zend_hash_key * hash_key, void * pParam) {/* True if there's a key, false if there's not */return (hash_key-> arKey & hash_key-> nKeyLength); // string-type key, because nKeyLength} void merge_associative (HashTable * target, hashTable * source) {zend_hash_merge_ex (target, source, zval_add_ref, sizeof (zval *), associative_only, NULL );}