📜 ⬆️ ⬇️

Zend loop traversal macros (HashTable Iteration)

Continuing my superficial study of the PHP source code (7.0.7) and writing the simplest extension to it, I would like to go a little deeper this time and describe techniques for traversing the array through the accepted function argument, which I learned while implementing the simple PHP function median (). The task of this function is simple - to return the arithmetic mean value. It is possible this publication will be useful to other PHP developers, just like me, who decided in my spare time to study the architecture of my favorite language, which makes money. In a previous post, I was quick to describe how to quickly create an extension in PHP with implementations of the factorial calculation function. It is simple to the extent that it takes a simple integer parameter and then recursively called. The implementation of the median () function is complicated by the fact that the received parameter is an array, you need to walk through it to sum up the total value, and also to calculate the total number of elements in the array.

At the moment I have simplified the task also by the fact that I think that all the adopted elements of the array are numbers. The source code for PHP extensions is amazing because here “everything is written” through the use of macros. At the very least, this initial opinion is created. It turns out that macros are also used to traverse the list of elements in the array. For clarity, I will cite immediately the function code followed by a short description.

The function is described in the same file - mathstat.c of the mathstat extension. Link to github.

Listing mathstat extension features:
')
const zend_function_entry mathstat_functions[] = { PHP_FE(confirm_mathstat_compiled, NULL) /* For testing, remove later. */ PHP_FE(ms_factorial, arginfo_ms_factorial) PHP_FE(ms_median, NULL) PHP_FE_END /* Must be the last line in mathstat_functions[] */ }; 


The definition of the function body itself:

 PHP_FUNCTION(ms_median) { int argc = ZEND_NUM_ARGS(); double total = 0; int count = 0; zval *array, *value; if (zend_parse_parameters(argc, "a", &array) == FAILURE) { RETURN_DOUBLE(0); } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), value) { total = total + zval_get_double (value); count += 1; } ZEND_HASH_FOREACH_END(); if (count == 0 || total == 0) { RETURN_DOUBLE(0); } RETURN_DOUBLE(total/count); } 


If you look at the body of the function, then, just like last time, the function of checking the parameter is called, where you set the value “a” (array) as the template of the type of argument being received

  if (zend_parse_parameters(argc, "a", &array) == FAILURE) { RETURN_DOUBLE(number); } 


Now the most interesting thing is that the loop is implemented through the macro ZEND_HASH_FOREACH_VAL. I found 7 macros in total which pass through the array in the references. At the same time, the term HashTable is used instead of an array everywhere. For our case, I chose the simplest macro. The first argument is the received array itself via the function, and the second argument is zval (the basic data structure that stores the value and data type for itself is the video for this part of Dmitry Stogov). In this case, I simply call the zval_get_double function, which, roughly speaking, returns the value from the array itself. If you rewrite it into plain PHP code, you get:

  1 <?php 2 $array = [1,2,3]; 3 4 $number = 0; 5 $count = 0; 6 7 foreach($array as $val) { 8 $number += $val; 9 $count += 1; 10 } 11 12 echo "cnt: ".$count." total: ".$number."\n"; 13 ?> 


That is, in fact, nothing complicated, the same record, only using a macro. If you look at another more advanced macro,

 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) 


then without the code it is already clear that this is an analog of the php cycle:

 foreach($array as $key => $value) { } 


For clarity, I will cite all macros from the reference book:

 ZEND_HASH_FOREACH_VAL(ht, val) ZEND_HASH_FOREACH_KEY(ht, h, key) ZEND_HASH_FOREACH_PTR(ht, ptr) ZEND_HASH_FOREACH_NUM_KEY(ht, h) ZEND_HASH_FOREACH_STR_KEY(ht, key) ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val) ZEND_HASH_FOREACH_KEY_VAL(ht, h, key, val) 


That's all. Thanks for taking the time and losing money on mobile traffic.

Source: https://habr.com/ru/post/303678/


All Articles