Manipulating Zend Hash Table (1)

c9s
PHP Hacks
Published in
2 min readOct 20, 2015

Zend Hash is the core internal data structure for manipulating class property, array, globals, function names, resources … and so on.

There are two series of functions for manipulating zend hash:

  • zend_hash_
  • zend_hash_index_

When you have to look up / insert entries into the hash by string key (aka associative array), you can use the first series functions.

And the second series is pretty much faster than the first series. They are used for indexed array, therefore it’s real O(1) without calling the hash function to calculate keys.

Let’s start from PHP Array since it’s much simpler:

Before you manipulate the array, you need at least three things:

HashTable * hash; // the internal zend hash of PHP arrayHashPosition pos; // the entry position in the internal zend hash.zval ** data; // the entry value, it must be 2 level pointer of zval struct.

Then, to get the internal hash table from an zval array, you can do:

hash = Z_ARRVAL_P(array);  // get from zval *array;

The “Z_ARRVAL_P” macro works for 1 level zval pointer, if you used an array with second level zval pointer:

zval **array;

You should use:

hash = Z_ARRVAL_PP(array);

And here are the corresponding C code of PHP array manipulation:

The reset operation can be done by:

zend_hash_internal_pointer_reset_ex(hash, &pos);

And to move the current pointer to next position, simply call zend_hash_move_forward_ex like this:

zend_hash_move_forward_ex(hash, &pos));

And you can combine these functions to build up a PHP foreach statement, here is the C version foreach:

for(zend_hash_internal_pointer_reset_ex(hash, &pos);
zend_hash_get_current_data_ex(hash, (void**) &data, &pos) == SUCCESS;
zend_hash_move_forward_ex(hash, &pos))
{
}

And there is another form of foreach loop implemented with while statement:

zend_hash_internal_pointer_reset_ex(hash, &pos);
while (zend_hash_get_current_data_ex(hash, (void **)&data, &pos) == SUCCESS) {
zend_hash_move_forward_ex(arr_hash, &pos);
}

To get the current key in the loop, you can do:

// for string type key (associative array)
char *string_key;
// string length for string type key (associative array)
uint string_key_len;
// for indexed array
ulong num_index;
switch (zend_hash_get_current_key_ex(hash, &string_key, &string_key_len, &num_index, 0, &pos)) { case HASH_KEY_IS_STRING: break; case HASH_KEY_IS_LONG: break;}

There is also another way to get the current key in zval:

zval *key = NULL;
MAKE_STD_ZVAL(key);
zend_hash_get_current_key_zval_ex(hash, key, &pos);
// use the key
// release the zval
zval_ptr_dtor(&arr_key);

But remember to call zval_ptr_dtor at the end of loop, or you will have memory leaks in your extension.

--

--

c9s
PHP Hacks

Yo-an Lin, yet another programmer in 21 century