Writing PHP Extensions1. Setting up Your PHP Build Environment on Linux2. Generating a PHP Extension Skeleton3. Building and Installing a PHP Extension4. Rebuilding Extensions for Production5. Extension Skeleton File Content6. Running PHP Extension Tests7. Adding New Functionality8. Basic PHP Structures9. PHP Arrays10. Catching Memory Leaks11. PHP Memory Management12. PHP References13. Copy on Write14. PHP Classes and Objects15. Using OOP in our Example Extension16. Embedding C Data into PHP Objects17. Overriding Object Handlers18. Answers to Common Extension Questions12. PHP ReferencesUsually, when you pass a parameter to function, you do it by value, and it means, the called function cannot modify it. In general, in a PHP extension you may modify a parameter passed by value, but most probably, this will lead to memory errors and crashes. In case you need to modify the argument of function (like sort() function does), it must be passed by reference. Passing by reference is the main use-case for php references, but they also may be used everywhere inside other data structures (e.g. element of array). Internally, they are represented by zval with IS_REFERENCE as type and pointer to zend_reference structure as value. As all reference-counted types, it’s inherited from the zend_refcounted structure, which defines the format of the first 64-bit word. The rest of the structure is another embedded zval.There are few C macros to check or retrieve fields of reference zvals (and the same macros with “_P” suffix take pointers to zvals): Z_ISREF(zv) – checks if the value is a PHP reference (has type IS_REFERENCE). Z_REF(zv) – returns dependent zend_reference structure (type must be IS_REFERENCE). Z_REFVAL(zv) – returns a pointer to the referenced value (zval). There are also few macros for constructing references and de-referencing: ZVAL_REF(zv, ref) – initializes zval by IS_REFERENCE type and give zend_reference pointer. ZVAL_NEW_EMPTY_REF(zv) – initializes zval by IS_REFERENCE type and a new zend_reference structure. Z_REFVAL_P(zv) needs to be initialized after this call. ZVAL_NEW_REF(zv, value) – initializes zval by IS_REFERENCE type and a new zend_reference structure with a given value. ZVAL_MAKE_REF_EX(zv, refcount) – converts “zv” to PHP reference with the given reference-counter. ZVAL_DEREF(zv) – if “zv” is a reference, it’s de-referenced (a pointer to the referenced value is assigned to “zv”). USING PHP REFERENCES IN OUR EXAMPLE EXTENSIONS Let’s try to pass a reference to our test_scale() function. $ php -r ‘$a = 5; var_dump(test_scale([&$a], 2));’ Warning: test_scale(): unexpected argument type in Command line code on line 1 NULLReferences are not supported. To fix this, we should just add de-referencing. static int do_scale(zval *return_value, zval *x, zend_long factor) { ZVAL_DEREF(x); if (Z_TYPE_P(x) == IS_LONG) { RETVAL_LONG(Z_LVAL_P(x) * factor);Now everything is fine: $ php -r ‘$a = 5; var_dump(test_scale([&$a], 2));’ array(1) { [0]=> int(10) }Let’s also convert our test_scale() to a function test_scale_ref() that won’t return any value, but will receive argument by reference and multiply the passed value in-place.static int do_scale_ref(zval *x, zend_long factor) { ZVAL_DEREF(x); if (Z_TYPE_P(x) == IS_LONG) { Z_LVAL_P(x) *= factor; } else if (Z_TYPE_P(x) == IS_DOUBLE) { Z_DVAL_P(x) *= factor; } else if (Z_TYPE_P(x) == IS_STRING) { size_t len = Z_STRLEN_P(x); char *p; ZVAL_STR(x, zend_string_safe_realloc(Z_STR_P(x), len, factor, 0, 0)); p = Z_STRVAL_P(x) + len; while (--factor > 0) { memcpy(p, Z_STRVAL_P(x), len); p += len; } *p = ‘\000’; } else if (Z_TYPE_P(x) == IS_ARRAY) { zval *val; ZEND_HASH_FOREACH_VAL(Z_ARR_P(x), val) { if (do_scale_ref(val, factor) != SUCCESS) { return FAILURE; } } ZEND_HASH_FOREACH_END(); } else { php_error_docref(NULL, E_WARNING, “unexpected argument type”); return FAILURE; } return SUCCESS; } PHP_FUNCTION(test_scale_ref) { zval * x; zend_long factor = TEST_G(scale); // default value ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(x) Z_PARAM_OPTIONAL Z_PARAM_LONG(factor) ZEND_PARSE_PARAMETERS_END(); do_scale_ref(x, factor); } ZEND_BEGIN_ARG_INFO(arginfo_test_scale_ref, 1) ZEND_ARG_INFO(1, x) // pass by reference ZEND_ARG_INFO(0, factor) ZEND_END_ARG_INFO() static const zend_function_entry test_functions[] = { PHP_FE(test_test1, arginfo_test_test1) PHP_FE(test_test2, arginfo_test_test2) PHP_FE(test_scale_ref, arginfo_test_scale_ref) PHP_FE_END };Testing: $ php -r ‘$x=5; test_scale_ref($x, 2); var_dump($x);’ int(10) $ php -r ‘$x=5.0; test_scale_ref($x, 2); var_dump($x);’ float(10) $ php -r ‘$x=”5”; test_scale_ref($x, 2); var_dump($x);’ string(2) “55” $ php -r ‘$x=[[5]]; test_scale_ref($x, 2); var_dump($x);’ array(1) { [0]=> array(1) { [0]=> int(10) } }Everything looks correct, but in some cases our function is going to behave incorrectly. See the next section for more details about how to avoid issues by using Copy on Write. Request PDF VersionBook traversal links for 12. PHP References‹ 11. PHP Memory ManagementWriting PHP Extensions13. Copy on Write ›