I'm reading Sara Golemon's book, Extending and Embedding PHP so that I can try and write a PHP extension (long story...).
I've been looking through a lot of PHP code examples (JSON, serialize functions, etc.) and am baffled by the pointer usage. I coded in C at university years and years ago but never really grokked pointers. I'm hoping someone can help me wade through this section of the book and better understand what is going on. I'll italicize the parts I find confusing:
====
DATA RETRIEVAL
In order to retrieve a variable from userspace, you'll need to look in whatever symbol table it's stored in. The following code segment shows using the zend_hash_find() function for this purpose:
{
zval **fooval;
if (zend_hash_find(EG(active_symbol_table),
("foo", sizeof("foo"),
(void**)&fooval) == SUCCESS) {
php_printf("Got the value of $foo!");
} else {
php_printf("$foo is not defined.");
}
}
A few parts of this example should look a little funny. Why is fooval defined to two levels of indirection? Why is sizeof() used for determining the length of "foo" ? Why is &fooval, which would evaluate to a zval***, cast to a void**? If you asked yourself all three of these questions, pat yourself on the back.
First, it's worth knowing that HashTables aren't only used for userspace variables. The HashTable structure is so versatile that it's used all over the engine and in some cases it makes perfect sense to want to store a non-pointer value. A HashTable bucket is a fixed size, however, so in order to store data of any size, a HashTable will allocate a block of memory to wrap the data being stored. In the case of variables, it's a zval being stored, so the HashTable storage mechanism allocates a block of memory big enough to hold a pointer. The HashTable's bucket uses that new pointer to carry around the zval and you efffectively wind up with a zval** inside the HashTable. The reason for storing a zval* when HashTables are clearly capable of storing a full zval will be covered in the next chapter.
When trying to retrieve that data, the HashTable only knows that is has a pointer to something. In order to populate the pointer into a calling function's local storage, the calling function will naturally dereference the local pointer, resulting in a variable of indeterminate type with two levels of indirection (such as void**). Knowing that your "indeterminate type" in this case is zval*, you can see where the type being passed into zend_hash_find() will look different to the compiler, having three levels of direction rather than two. This is done on purpose here so a simple typecase is added to the function call to silence compiler warnings.
...
If zend_hash_find() locates the item you're looking for, it populates the dereferenced pointer provided with the address of the bucket pointer it allocated when the requested data was first added to the HashTable and returns an integer value matching the SUCCESS constant...
Any help coming to grips with this bit of info would be much appreciated. I find the double- and triple-indirection and casting of triple indirection to double indirection (void**) especially confusing.