Data Storage
You've used PHP from the userspace side of things, so you're already familiar with the concept of an array. Any number of PHP variables (zvals) can be dropped into a single container (array) and be given names (labels) in the form of numbers or strings.
What's hopefully not surprising is that every single variable in a PHP script can be found in an array. When you create a variable, by assigning a value to it, Zend stores that value into an internal array known as a symbol table.
One symbol table, the one that defines the global scope, is initialized upon request startup just before extension RINIT methods are called, and then destroyed after script completion and subsequent RSHUTDOWN methods have executed.
When a userspace function or object method is called, a new symbol table is allocated for the life of that function or method and is defined as the active symbol table. If current script execution is not in a function or method, the global symbol table is considered active.
Taking a look at the execution globals structure (defined in Zend/zend_globals.h), you'll find the following two elements defined:
struct _zend_execution_globals { ... HashTable symbol_table; HashTable *active_symbol_table; ... };
The symbol_table, accessed as EG(symbol_table), is always the global variable scope much like the $GLOBALS variable in userspace always corresponds to the global scope for PHP scripts. In fact, the $GLOBALS variable is just a userspace wrapper around the EG(symbol_table) variable seen from the internals.
The other part of this pair, active_symbol_table, is similarly accessed as EG(active_symbol_table), and represents whatever variable scope is active at the time.
The key difference to notice here is that EG(symbol_table), unlike nearly every other HashTable you'll use and encounter while working with the PHP and Zend APIs, is a direct variable. Nearly all functions that operate on HashTables, however, expect an indirect HashTable* as their parameter. Therefore, you'll have to dereference EG(symbol_table) with an ampersand when using it.
Consider the following two code blocks, which are functionally identical:
In PHP:
In C:
{ zval *fooval; MAKE_STD_ZVAL(fooval); ZVAL_STRING(fooval, "bar", 1); ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval); }
First, a new zval was allocated using MAKE_STD_ZVAL() and its value was initialized to the string "bar". Then a new macro, which roughly equates with the assignment operator (=), combines that value with a label (foo), and adds it to the active symbol table. Because no userspace function is active at the time, EG(active_symbol_table) == &EG(symbol_table), which ultimately means that this variable is stored in the global scope.