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 Questions8. Basic PHP StructuresIn this section, we will take a deeper look into the most important internal PHP data structures, which include: Values Strings Parameter parsing API Return Values And you can see an example of using the basic PHP structures in the sample extension we have been building in this book. PHP VALUES (ZVAL) Zval is the key PHP structure. It represents any PHP value (like a number, string, or array), following its simplified C definition. typedef struct _zval_struct { union { zend_long lval; double dval; zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; … } value; zend_uchar type; zend_uchar type_flags; uint16_t extra; uint32_t reserved; } zval;PHP is a dynamically-typed language, and the same zval may keep values of different types. The first field of zval is a union of all possible value types. It may keep integer, double number, or a pointer to some dependent structure. Zval also keeps “type,” “type_flags,” and two reserved fields. This space would be used for proper structure alignment anyway, but by reserving it, we may store some dependent data. In memory, zval is represented as two 64-bit words. The first word keeps the value — and the second word keeps the type, type_flags, extra, and reserved fields. Zvals are usually allocated in the PHP stack or inside other data structures. They are almost never allocated on heap. The single important zval flag is IS_TYPE_REFCOUNTED, which defines how to handle zval during copying and destruction. If it’s not set, zval is scalar. To copy zval, we copy the first word (with its value) and a half of the second word (with type, type_flags and extra fields). We don’t need any special actions to destroy them. There are few pure scalar PHP types that are completely represented by this zval structure: IS_UNDEF: Uninitialized PHP local variable. (You usually won’t get with this type in PHP extensions. PHP interpreter take cares about initialization, warnings and conversion to NULL.) IS_NULL: Null constant. (Value is not used.) IS_FALSE: False constant of boolean type. (Value is not used.) IS_TRUE: True constant of boolean type. (Value is not used.) IS_LONG: Long integer number. IS_DOUBLE: Long floating-point number.There is a special C macro API to retrieve fields of zvals. All the macros are defined in two forms: plain, for zvals and with “_P” suffix, for pointers to zvals (e.g. Z_TYPE(zval) and Z_TYPE_P(zval_ptr)). The following are the most important: Z_TYPE_FLAGS(zv): Returns type flags (set of bits: IS_TYPE_REFCOUNTED and few others). Z_REFCOUNTED(zv): Returns true if the IS_TYPE_REFCOUNTED is set. Z_TYPE(zv): Returns type of the zval (IS_NULL, IS_LONG, etc). Z_LVAL(zv): Returns long integer value of the zval (the type must be IS_LONG). Z_DVAL(zv): Returns double value of the zval (the type must be IS_DOUBLE). Another family of macros is used for zval initialization: ZVAL_UNDEF(zv): Initializes undefined zval ZVAL_NULL(zv): Initializes zval by null constant ZVAL_FALSE(zv): Initializes zval by false constant ZVAL_TRUE(zv): Initializes zval by true constant ZVAL_BOOL(zv, bval): Initializes zval by true constant if “bval” is true or by false otherwise ZVAL_LONG(zv, lval): Initializes a long integer number zval ZVAL_DOUBLE(zv, dval): Initializes a long floating point number zval The most zval-related declarations are done at Zend/zend_types.h. All non-scalar values — like strings, arrays, objects, resources, and references — are represented by structures specific to a certain type. Zval keeps just a pointer to this structure. In terms of object-oriented programming, all these specific structures have a common abstract parent class: zend_refcounted. It defines the format of the first 64-bit word of the structure. It contains the reference-counter, type, flags, and information used by a garbage collector. Specific structures for concrete types are built on top of this one and add some additional data after this first word. The following ref-counted types are possible: IS_STRING: PHP string. IS_ARRAY: PHP array. IS_REFERENCE: PHP reference. IS_OBJECT: PHP object. IS_RESOURCE: PHP resource. Looking ahead, PHP strings and arrays may be non-reference-counted (or immutable) and behave similar to scalar values. The following API macros are intended to work with reference-counted zvals (and there are also variants with “_P” suffix): Z_COUNTED(zv): Returns pointer to the dependent zend_refcounted structure. Z_REFCOUNT(zv): Returns reference-counter of the dependent zend_refcounted structure. Z_SET_REFCOUNT(zv, rc): Sets reference-counter of the dependent zend_refcounted structure. Z_ADDREF(zv): Increments reference-counter of the dependent zend_refcounted structure. Z_DELREF(zv): Decrements reference-counter of the dependent zend_refcounted structure. The following universal macros may be used with both reference-counted and non-reference-counted zvals: Z_TRY_ADDREF(zv): Checks IS_TYPE_REFCOUNTED flag and increment reference-counter of the dependent zend_refcounted structure, if it’s set. Z_TRY_DELREF(zv): Checks IS_TYPE_REFCOUNTED flag and decrements reference-counter of the dependent zend_refcounted structure, if it’s set. zval_ptr_dtor(zv): Release the zval value: checks IS_TYPE_REFCOUNTED flag; decrements reference-counter of the dependent zend_refcounted structure, if it’s set; call specific to zval type destruction function, if reference-counter became zero. ZVAL_COPY_VALUE(dst, src): Copies zval (value, type and type_flags) from “src” do “dst.” ZVAL_COPY(dst, src): Copies zval (value, type and type_flags) from “src” do “dst” and increments reference-counter of the dependent zend_refcounted structure, if IS_TYPE_REFCOUNTED flag is set. PHP STRINGS Strings are represented by the dependent “zend_string” structure. Its first word repeats the word defined by “zend_refcounted” structure. “zend_string” also keeps the pre-calculated value of a hash function, string length, and the actual embedded characters. Hash value doesn’t have to be pre-calculated. It’s initialized by zero, lazily calculated on demand, and then reused. PHP string copying doesn’t require duplication of the actual characters. Few zval structures may point to the same “zend_string” with the corresponding reference-counter value.Strings may be immutable (or interned) without IS_TYPE_REFCOUNTED flag set, and in this case, they behave similar to scalars. The PHP engine doesn’t need to perform any reference counting at all. Such strings are used only for reading and may be destroyed only at the end of request processing. They are never destroyed in the middle of request. The same “zend_string” representation is not only used for PHP values, but also for all other character data in the PHP engine, such as names of functions, classes, and methods. The different fields of zend_string may be accessed through the following API macros (and there are also variants with “_P” suffix for pointers to zvals): Z_STR(zv): Returns a pointer to corresponding zend_string structure. Z_STRVAL(zv): Returns a pointer to corresponding C string (char*). Z_STRLEN(zv): Returns length of the corresponding string. Z_STRHASH(zv): Returns hash value of the corresponding string. “zend_string.hash_value” is used as a cache to eliminate repeatable hash function calculation. PHP strings values may be constructed through the following macros: ZVAL_STRING(zv, cstr): Allocates zend_string structure, initializes it with the given C zero-terminated string, and initializes PHP string zval. ZVAL_STRINGL(zv, cstr, len): Allocates zend_string structure, initializes it with the given C string and length, and initializes PHP string zval. ZVAL_EMPTY_STRING(zv): Initializes empty PHP string zval. ZVAL_STR(zv, zstr): Initializes PHP string zval using given zend_string. ZVAL_STR_COPY(zv, zstr): Initializes PHP string zval using given zend_string. Reference-counter of “zend_string” is incremented, if necessary. Of course, it’s also possible to work with “zend_string” structures directly, without zval. The following are the most important and useful API macros and functions: ZSTR_VAL(zstr): Returns a pointer to corresponding C string (char*). ZSTR_LEN(zstr): Returns length of the string. ZSTR_IS_INTERNED(zstr): Checks if the string is interned (or immutable). ZSTR_HASH(zstr): Returns hash_value of the string using hash_value as cache. ZSTR_H(zstr): Returns value of hash_value field (it may be zero which means it has not been calculated yet). zend_string_hash_func(zstr): Calculates and returns hash value of the string. zend_hash_func(cstr, len): Calculates and returns hash value of the given C string (char*) and specified length. ZSTR_EMPTY_ALLOC(): Returns an empty “zend_string.” This macro doesn’t actually allocate anything, but rather returns a pointer to a single interned “zend_string” structure. zend_string_alloc(len, persistent): Allocates memory for the “zend_string” structure of the given string length. The “persistent” argument tells whether the created string should relive request boundary. (Usually it shouldn’t, and therefore “persistent” should be zero.) zend_string_safe_alloc(len, number, addition, persistent): Similar to zend_string_alloc(), but the final string size is calculated as (len * number + addition) and checked for possible overflow.zend_string_init(cstr, len, persistent): Allocates memory for the “zend_ string” structure (similar to zend_alloc) and initializes it with the given C string (char*) and length. zend_string_copy(zstr): Creates a copy of the given “zend_string” and returns a pointer to the same string and increments reference-counter, if necessary. zend_string_release(zstr): Releases a pointer to the given “zend_string” and checks if the given string is reference-counted (not interned), decrements reference-counter, and frees memory, if it reached zero. zend_string_equals(zstr1, zstr2): Checks equality of two “zend_string” structures. zend_string_equals_literal(zstr, cstr): Checks equality of “zend_string” with a given C string literal. zend_string_equals_literal_ci(zstr, cstr): The case insensitive variant of zend_string_equals_literal(). The compete zend_string API is defined in Zend/zend_string.h. PARAMETER PARSING API The parameter parsing API is a way to get the values of an actual PHP parameter in an internal PHP function. We already used some elements of this API in our “test” extension. These were the blocks between ZEND_PARSE_PARAMETERS_START and ZEND_PARSE_ PARAMETERS_END. Let’s review this API in more detail. First, there are two different parameters parsing APIs: the one we already used — the Fast Parameter Parsing API introduced in PHP 7 — and the old API that is compatible with PHP 5. There is no a single answer for which API is better for a particular function. The old API is slower, but its usage requires less machine code. If the function body is small and needs to be fast, it should use the Fast Parameter Parsing API, because the overhead of the old API may be bigger than the semantic part of the function itself. On the other hand, if the function is going to be slow, it doesn’t make sense to care about parameter parsing overhead and increase code size.FAST PARAMETER PARSING API This new API is implemented using C pre-processor macros that are converted to almost optimal C code to fetch values of actual parameters into C variables, especially for this function. ZEND_PARSE_PARAMETERS_NONE(): This macro should be used for functions that don’t expect any parameters. In case something is passed, the function will produce warning “expects exactly 0 parameters, %d given” and return NULL. ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args): This macro opens a block of parameter fetching code. The first argument is the minimal number of arguments that should be zero or more. The second argument is the maximum number of arguments. For functions with a variable number of arguments, its value should be -1. In case the number of passed parameters exceed the defined argument boundaries, the function will produce a warning about invalid number of arguments and return NULL. ZEND_PARSE_PARAMETERS_END(): This macro terminates the block of parameter fetching code. There are a few ZPP macros inside the block between START and END macros. There is one macro for each argument, except for functions with variable number of arguments, where the last argument may receive many values. Required arguments should be separated from optional using the Z_PARAM_OPTIONAL macro. The following are the most common macros for parameter parsing: Z_PARAM_BOOL(dest): Receives a boolean argument and stores the value of the actual parameter in a C variable of type zend_ bool. Here and below, “receive” means checking the type of actual parameter and its conversion to required type, if possible, or producing a corresponding type incompatibility warning and returning NULL. Z_PARAM_LONG(dest): Receives the integer number argument and stores the value of the actual parameter in a C variable of type zend_long. Z_PARAM_DOUBLE(dest): Receives a floating point number argument and stores the value of the actual parameter in a C variable of type double. Z_PARAM_STR(dest): Receives a string argument and stores the value of the actual parameter in a C variable of type zend_string*. Z_PARAM_STRING(dest, dest_len): Receives a string argument and stores a pointer to the C string and the length of passed string in the given C variables “dest” of type char* and “dest_len” of type size_t. Z_PARAM_ARRAY_HT(dest): Receives a PHP array argument and stores the value of the actual parameter in a C variable of type HashTable. (PHP arrays and HashTables are described in the next chapter.) Z_PARAM_ARRAY(dest): Receives a PHP array argument and stores the value of the actual parameter in a C variable of type zval*. Z_PARAM_OBJECT(dest): Receives a PHP object argument and stores the value of the actual parameter in a C variable of type zval*. Z_PARAM_RESOURCE(dest): Receives a PHP resource argument and stores the value of the actual parameter in a C variable of type zval*. Z_PARAM_ZVAL(dest): Receives any PHP zval as passed without any conversions, and stores its value in a C variable of type zval*. Z_PARAM_ZVAL_DEREF(dest): Receives any PHP zval and de-reference, and stores the referenced value in a C variable of type zval*. This macro is useful for receiving parameters passed by reference. (We speak about them in the next chapter.) Z_PARAM_VARIADIC(spec, dest, dest_num): Receives the rest arguments as an array of zvals. This macro must be the last one in the parameter passing block. The “spec” argument may be “*” (zero or more parameters) or “+” (one or more parameters). The address of the parameters array is stored in the C variable “dest” of type zval* and the number of parameters in the variable “dest_ num” of type “int.” Most of the above macros are available in extended variations with “_EX” and “_EX2” suffixes. Additional arguments of these macros allow control of nullability check, de-referencing, and separation. OLD PARAMETER PARSING API The old parameter parsing API was implemented as a C scanf() like function with a format string and following variable number of arguments, passed by address. zend_parse_parameters(int num_args, const char *type_spec, …);This function checks each letter of the “type_spec” string and performs parameter receiving and storing into the following variables accordingly. For example, we could use the following code in our test_scale() function. PHP_FUNCTION(test_scale) { double x; if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), “d”, &x) == FAILURE) { return; } RETURN_DOUBLE(x * TEST_G(scale)); }The “d” letter in “type_spec” assumes receiving of double argument and storing it in C variable of type double. Let’s review most “type_ spec” letters and their correlation with Fast Parameter Parsing API. ‘|’ - Z_PARAM_OPTIONAL ‘a’ - Z_PARAM_ARRAY(dest) ‘b’ - Z_PARAM_BOOL(dest) ‘d’ - Z_PARAM_DOBLE(dest) ‘h’ - Z_PARAM_ARRAY_HT(dest) ‘l’ - Z_PARAM_LONG(dest) ‘o’ - Z_PARAM_OBJECT(dest) ‘O’ - Z_PARAM_OBJECT_OF_CLASS(dest, ce) ‘r’ - Z_PARAM_RESOURCE(dest)‘s’ - Z_PARAM_STRING(dest, dest_len) ‘S’ - Z_PARAM_STR(dest) ‘z’ - Z_PARAM_ZVAL(dest) ‘*’ - Z_PARAM_VARIADIC(‘*’, dest, dest_num) ‘+’ - Z_PARAM_VARIADIC(‘+’, dest, dest_num) Each of the type specifiers may be followed by a modifier character: ‘/’ - separate zval, if necessary. This is useful when function receives value by reference and is going to be modified. Otherwise, if the value is referenced from several places (reference-counter is more than 1), then all the values are going to be incorrectly modified at once. ‘!’ - check if the actual parameter is null and set the corresponding pointer to NULL. For ‘b’, ‘l’ and ‘d’, an extra argument of type zend_bool* must be passed after the corresponding bool*, zend_long*, or double* argument. RETURN VALUE Each internal PHP function takes a “return_value” argument of type zval*. We may write into it using a family of ZVAL_...() macros described above, or use a special RETVAL_...() family of similar macros: #define RETVAL_NULL() ZVAL_NULL(return_value) #define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b) #define RETVAL_FALSE ZVAL_FALSE(return_value) #define RETVAL_TRUE ZVAL_TRUE(return_value) #define RETVAL_LONG(l) ZVAL_LONG(return_value, l) #define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d) #define RETVAL_STR(s) ZVAL_STR(return_value, s) #define RETVAL_STR_COPY(s) ZVAL_STR_COPY(return_value, s) #define RETVAL_STRING(s) ZVAL_STRING(return_value, s) #define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) #define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)It’s also possible to write value into “return_value” and perform actual return using RETURN_...() family of similar macros: #define RETURN_NULL() {RETVAL_NULL(); return;} #define RETURN_BOOL(b) {RETVAL_BOOL(b) return;} #define RETURN_FALSE {RETVAL_FALSE; return;} #define RETURN_TRUE {RETVAL_TRUE; return; #define RETURN_LONG(l) {RETVAL_LONG(l); return} #define RETURN_DOUBLE(d) {RETVAL_DOUBLE(d); return;} #define RETURN_STR(s) {RETVAL_STR(s); return;} #define RETURN_STR_COPY(s) {RETVAL_STR_COPY(s); return;} #define RETURN_STRING(s) {RETVAL_STRING(s); return;} #define RETURN_STRINGL(s, l) {RETVAL_STRINGL(s, l); return;} #define RETURN_EMPTY_STRING() {RETVAL_EMPTY_STRING(); return;}USING BASIC PHP INTERNALS IN OUR EXAMPLE EXTENSION Let’s extend our test_scale() example to allow passing of the scale factor as the optional second argument — and make it behave differently, depending on type of the first argument. Perform multiplication when the first parameter is number, but keep the type of result to be the same as type of the first argument. In case the first argument is string, it should be repeated few times. PHP_FUNCTION(test_scale) { 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(); if (Z_TYPE_P(x) == IS_LONG) { RETURN_LONG(Z_LVAL_P(x) * factor); } else if (Z_TYPE_P(x) == IS_DOUBLE) { RETURN_DOUBLE(Z_DVAL_P(x) * factor); } else if (Z_TYPE_P(x) == IS_STRING) { zend_string *ret = zend_string_safe_alloc(Z_STRLEN_P(x), factor, 0, 0); char *p = ZSTR_VAL(ret); while (factor-- > 0) { memcpy(p, Z_STRVAL_P(x), Z_STRLEN_P(x)); p += Z_STRLEN_P(x); } *p = ‘\000’; RETURN_STR(ret); } else { php_error_docref(NULL, E_WARNING, “unexpected argument type”); return; } } ZEND_BEGIN_ARG_INFO(arginfo_test_scale, 0) ZEND_ARG_INFO(0, x) ZEND_ARG_INFO(0, factor) ZEND_END_ARG_INFO()At this point you should know all the PHP internals and understand the details of this function implementation. Now it’s time to test our new implementation.$ php -r ‘var_dump(test_scale(2));’ int(2) $ php -r ‘var_dump(test_scale(2,3));’ int(6) $ php -r ‘var_dump(test_scale(2.0, 3));’ float(6) $ php -r ‘var_dump(test_scale(“2”, 3));’ string(3) “222”Request PDF VersionBook traversal links for 8. Basic PHP Structures‹ 7. Adding New FunctionalityWriting PHP Extensions9. PHP Arrays ›