Продолжая своё поверхностное изучение исходников PHP (7.0.7) и написания простейшего расширения к нему, хотел бы в этот раз немного углубится и описать приемы обхода массива через принятый аргумент функции, с которыми я познакомился при реализации простой PHP функции median(). Задача этой функции проста — вернуть средне-арифметическое значение. Возможна данная публикация будет полезной другим разработчикам PHP, таким же как и я, которые решили в свободное время немного изучить архитектуру любимого языка, на котором зарабатывают деньги. В предыдущей публикации я на “скорую руку” описал прием быстрого создания расширения в PHP с реализаций функции расчета факториала. Она проста в той степени, что принимает простой параметр целого типа и затем рекурсивно вызывается. Реализация функции median() усложнена тем, что принимаемый параметр — массив, по нему нужно пройтись, для суммирования общего значения, а также просчитать общее число элементов в массиве.
В данный момент я упростил задачу еще и тем, что заведо считаю, что все принятые элементы массива — числа. Исходники расширений PHP удивительны тем, что здесь “все пишется” через использование макросов. По крайней мере создается такое первоначальное мнение. Оказывается, для прохода по списку элементов в массиве тоже используются макросы. Для наглядности приведу сразу код функции с последующим небольшим описанием.
Функция описана все в том же файле — mathstat.c расширения mathstat. Ссылка на github.
Занесение в список функций расширения mathstat:
Само определение тела функции:
Если смотреть тело функции, то как и в прошлый раз, вызывается функция проверки параметра, где в качестве шаблона принимаемого типа аргумента задаем значение “a” (array)
Теперь самое интересное, проход по циклу реализован через макрос ZEND_HASH_FOREACH_VAL. Всего макросов которые проходят по массиву я нашел в справочках 7 штук. При этом, везде используется вместо массива термин HashTable. Для нашего случая я выбрал самый простой макрос. Первым аргументом он получает сам принятый массив через функцию, а вторым zval (базовая структура данных, которая хранить себе значение и тип данных — видео по этой части Дмитрия Стогова). В данном случае, я просто вызываю функцию zval_get_double, которая грубо говоря, мне и возвращает самое значение из массива. Если переписать это на обычный код PHP, то получится:
То есть, по сути ничего сложного, таже запись, только с использованием макроса. Если посмотреть на другой более расширенный макрос,
то без кода уже понятно, что это аналог php цикла:
Для наглядности приведу из справочника все макросы:
На этом все. Спасибо за отнятое время и потерянные деньги на мобильном трафике.
В данный момент я упростил задачу еще и тем, что заведо считаю, что все принятые элементы массива — числа. Исходники расширений PHP удивительны тем, что здесь “все пишется” через использование макросов. По крайней мере создается такое первоначальное мнение. Оказывается, для прохода по списку элементов в массиве тоже используются макросы. Для наглядности приведу сразу код функции с последующим небольшим описанием.
Функция описана все в том же файле — mathstat.c расширения mathstat. Ссылка на github.
Занесение в список функций расширения mathstat:
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[] */ };
Само определение тела функции:
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); }
Если смотреть тело функции, то как и в прошлый раз, вызывается функция проверки параметра, где в качестве шаблона принимаемого типа аргумента задаем значение “a” (array)
if (zend_parse_parameters(argc, "a", &array) == FAILURE) { RETURN_DOUBLE(number); }
Теперь самое интересное, проход по циклу реализован через макрос ZEND_HASH_FOREACH_VAL. Всего макросов которые проходят по массиву я нашел в справочках 7 штук. При этом, везде используется вместо массива термин HashTable. Для нашего случая я выбрал самый простой макрос. Первым аргументом он получает сам принятый массив через функцию, а вторым zval (базовая структура данных, которая хранить себе значение и тип данных — видео по этой части Дмитрия Стогова). В данном случае, я просто вызываю функцию zval_get_double, которая грубо говоря, мне и возвращает самое значение из массива. Если переписать это на обычный код PHP, то получится:
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 ?>
То есть, по сути ничего сложного, таже запись, только с использованием макроса. Если посмотреть на другой более расширенный макрос,
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val)
то без кода уже понятно, что это аналог php цикла:
foreach($array as $key => $value) { }
Для наглядности приведу из справочника все макросы:
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)
На этом все. Спасибо за отнятое время и потерянные деньги на мобильном трафике.
