Introduction
. Accepting Values
. The ZVAL
. Creating ZVALs
. Arrays
. Symbol Tables as Arrays
. Reference Counting
Copies versus References
Sanity Check
What’s Next?
Copies versus References
There are two ways to reference a zval
. The first, demonstrated above, is known as copy-on-write referencing. The second form, full referencing, is what a userspace script writer is more familiar with in relationship to the word “reference” and occurs with userspace code like: $a = &$b;
.
In a zval
, these two types are differentiated with the member value is_ref,
which will be 0 for copy references, and non-zero for full references. Note that it’s not possible for a zval
to be both a copy style reference and a full reference. So if a variable starts off being is_ref
, and is then assigned to a new variable as a copy, a full copy must be performed. Consider the following userspace code:
$a = 1; |
In this block of code, a zval
is created for $a
, initially with is_ref
set to 0 and refcount__gc
set to 1. When $a
is referenced to $b
, is_ref
gets changed to 1, and refcount__gc
increases to 2. When a copy is made into $c
, the Zend Engine can’t just simply increase the refcount__gc
to 3 since $c
would then be seen as a full reference to $a
. Turning off is_ref
wouldn’t work either since $b
would now be seen as a copy of $a
, rather than a reference. So at this point a new zval
is allocated, and the value of the original is copied into it withzval_copy_ctor()
. The original zval
is left with is_ref==1
and refcount__gc==2
, while the new zval
has is_ref=0
and refcount__gc=1
. Now look at that same code block done in a slightly different order:
$a = 1; |
The end result is the same, with $b
being a full reference to $a
, and $c
being a copy of $a
. This time, however, the internal actions are slightly different. As before, a new zval
is created for $a
at the beginning withis_ref==0
and refcount__gc=1
. The $c = $a;
statement then assigns the same zval
to the $c
variable while incrementing the refcount__gc
to 2, and leaving is_ref
as 0. When the Zend Engine encounters $b = &$a;
it wants to just set is_ref
to 1, but of course can’t since that would impact $c
. Instead it creates a new zval
and copies the contents of the original into it withzval_copy_ctor()
, then decrements refcount__gc
in the original zval
to signify that $a
is no longer using that zval
. Instead, it sets the new zval
‘s is_ref
to 1, its refcount__gc
to 2, and updates the $a
and $b
variables to refer to it.
Sanity Check
As before, the complete code listing for our three primary files is provided whole and intact below:
config.m4
|
php_hello.h
#ifdef ZTS ZEND_BEGIN_MODULE_GLOBALS(hello) #ifdef ZTS #define PHP_HELLO_WORLD_VERSION "1.0" PHP_MINIT_FUNCTION(hello); PHP_FUNCTION(hello_world); extern zend_module_entry hello_module_entry; #endif |
hello.c
#include "php.h" ZEND_DECLARE_MODULE_GLOBALS(hello) static zend_function_entry hello_functions[] = { zend_module_entry hello_module_entry = { #ifdef COMPILE_DL_HELLO PHP_INI_BEGIN() static void php_hello_init_globals(zend_hello_globals *hello_globals) PHP_RINIT_FUNCTION(hello) return SUCCESS; PHP_MINIT_FUNCTION(hello) REGISTER_INI_ENTRIES(); return SUCCESS; PHP_MSHUTDOWN_FUNCTION(hello) return SUCCESS; PHP_FUNCTION(hello_world) PHP_FUNCTION(hello_long) RETURN_LONG(HELLO_G(counter)); PHP_FUNCTION(hello_double) PHP_FUNCTION(hello_bool) PHP_FUNCTION(hello_null) PHP_FUNCTION(hello_greetme) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zname) == FAILURE) { convert_to_string(zname); php_printf("Hello "); RETURN_TRUE; PHP_FUNCTION(hello_add) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ld|b", &a, &b, &return_long) == FAILURE) { if (return_long) { PHP_FUNCTION(hello_dump) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &uservar) == FAILURE) { switch (Z_TYPE_P(uservar)) { RETURN_TRUE; PHP_FUNCTION(hello_array) array_init(return_value); add_index_long(return_value, 42, 123); add_next_index_string(return_value, "I should now be found at index 43", 1); add_next_index_stringl(return_value, "I'm at 44!", 10, 1); mystr = estrdup("Forty Five"); add_assoc_double(return_value, "pi", 3.1415926535); ALLOC_INIT_ZVAL(mysubarray); php_printf("mysubarray->refcount__gc = %d PHP_FUNCTION(hello_array_strings) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { arr_hash = Z_ARRVAL_P(arr); php_printf("The array passed contains %d elements for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) { zval temp; if (zend_hash_get_current_key_ex(arr_hash, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) { php_printf(" => "); temp = **data; RETURN_TRUE; static int php_hello_array_walk(zval **element TSRMLS_DC) temp = **element; return ZEND_HASH_APPLY_KEEP; static int php_hello_array_walk_arg(zval **element, char *greeting TSRMLS_DC) return ZEND_HASH_APPLY_KEEP; static int php_hello_array_walk_args(zval **element, int num_args, va_list args, zend_hash_key *hash_key) php_printf("%s", prefix); return ZEND_HASH_APPLY_KEEP; PHP_FUNCTION(hello_array_walk) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zarray) == FAILURE) { zend_hash_apply(Z_ARRVAL_P(zarray), (apply_func_t)php_hello_array_walk TSRMLS_CC); RETURN_TRUE; PHP_FUNCTION(hello_array_value) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &zarray, &zoffset) == FAILURE) { switch (Z_TYPE_P(zoffset)) { if (key && zend_hash_find(Z_ARRVAL_P(zarray), key, key_len + 1, (void**)&zvalue) == FAILURE) { *return_value = **zvalue; PHP_FUNCTION(hello_get_global_var) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) { if (zend_hash_find(&EG(symbol_table), varname, varname_len + 1, (void**)&varvalue) == FAILURE) { *return_value = **varvalue; PHP_FUNCTION(hello_set_local_var) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &varname, &varname_len, &value) == FAILURE) { ALLOC_INIT_ZVAL(newvar); zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, &newvar, sizeof(zval*), NULL); |
What’s Next?
In this tutorial, Part Two of the Extension Writing series, you learned how to accept
function parameters, you created and used arrays, and, most importantly, you took a
look at the inner workings of the zval
. In Part Three, you’ll take a look
at the resource
data type and start working with more complex data structures.