Что выполняет данный участок кода

Смотрю исходники PHP и возник вопрос понимания, что делает данная конструкция и что она в себя включает вообще. файл zend_ast.h, строка 300

ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);

Поскольку это заголовочный файл, смотрим файл zend_ast.c начиная с 508:

ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)
{
    zval op1, op2;
    zend_result ret = SUCCESS;

    switch (ast->kind) {
        case ZEND_AST_BINARY_OP:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
            } else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
                zval_ptr_dtor_nogc(&op1);
                ret = FAILURE;
            } else {
                binary_op_type op = get_binary_op(ast->attr);
                ret = op(result, &op1, &op2);
                zval_ptr_dtor_nogc(&op1);
                zval_ptr_dtor_nogc(&op2);
            }
            break;
        case ZEND_AST_GREATER:
        case ZEND_AST_GREATER_EQUAL:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
            } else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
                zval_ptr_dtor_nogc(&op1);
                ret = FAILURE;
            } else {
                /* op1 > op2 is the same as op2 < op1 */
                binary_op_type op = ast->kind == ZEND_AST_GREATER
                    ? is_smaller_function : is_smaller_or_equal_function;
                ret = op(result, &op2, &op1);
                zval_ptr_dtor_nogc(&op1);
                zval_ptr_dtor_nogc(&op2);
            }
            break;
        case ZEND_AST_UNARY_OP:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
            } else {
                unary_op_type op = get_unary_op(ast->attr);
                ret = op(result, &op1);
                zval_ptr_dtor_nogc(&op1);
            }
            break;
        case ZEND_AST_ZVAL:
        {
            zval *zv = zend_ast_get_zval(ast);

            ZVAL_COPY(result, zv);
            break;
        }
        case ZEND_AST_CONSTANT:
        {
            zend_string *name = zend_ast_get_constant_name(ast);
            zval *zv = zend_get_constant_ex(name, scope, ast->attr);

            if (UNEXPECTED(zv == NULL)) {
                ZVAL_UNDEF(result);
                return FAILURE;
            }
            ZVAL_COPY_OR_DUP(result, zv);
            break;
        }
        case ZEND_AST_CONSTANT_CLASS:
            if (scope) {
                ZVAL_STR_COPY(result, scope->name);
            } else {
                ZVAL_EMPTY_STRING(result);
            }
            break;
        case ZEND_AST_CLASS_NAME:
            if (!scope) {
                zend_throw_error(NULL, "Cannot use \"self\" when no class scope is active");
                return FAILURE;
            }
            if (ast->attr == ZEND_FETCH_CLASS_SELF) {
                ZVAL_STR_COPY(result, scope->name);
            } else if (ast->attr == ZEND_FETCH_CLASS_PARENT) {
                if (!scope->parent) {
                    zend_throw_error(NULL,
                        "Cannot use \"parent\" when current class scope has no parent");
                    return FAILURE;
                }
                ZVAL_STR_COPY(result, scope->parent->name);
            } else {
                ZEND_ASSERT(0 && "Should have errored during compilation");
            }
            break;
        case ZEND_AST_AND:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
                break;
            }
            if (zend_is_true(&op1)) {
                if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
                    zval_ptr_dtor_nogc(&op1);
                    ret = FAILURE;
                    break;
                }
                ZVAL_BOOL(result, zend_is_true(&op2));
                zval_ptr_dtor_nogc(&op2);
            } else {
                ZVAL_FALSE(result);
            }
            zval_ptr_dtor_nogc(&op1);
            break;
        case ZEND_AST_OR:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
                break;
            }
            if (zend_is_true(&op1)) {
                ZVAL_TRUE(result);
            } else {
                if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
                    zval_ptr_dtor_nogc(&op1);
                    ret = FAILURE;
                    break;
                }
                ZVAL_BOOL(result, zend_is_true(&op2));
                zval_ptr_dtor_nogc(&op2);
            }
            zval_ptr_dtor_nogc(&op1);
            break;
        case ZEND_AST_CONDITIONAL:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
                break;
            }
            if (zend_is_true(&op1)) {
                if (!ast->child[1]) {
                    *result = op1;
                } else {
                    if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
                        zval_ptr_dtor_nogc(&op1);
                        ret = FAILURE;
                        break;
                    }
                    zval_ptr_dtor_nogc(&op1);
                }
            } else {
                if (UNEXPECTED(zend_ast_evaluate(result, ast->child[2], scope) != SUCCESS)) {
                    zval_ptr_dtor_nogc(&op1);
                    ret = FAILURE;
                    break;
                }
                zval_ptr_dtor_nogc(&op1);
            }
            break;
        case ZEND_AST_COALESCE:
            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
                break;
            }
            if (Z_TYPE(op1) > IS_NULL) {
                *result = op1;
            } else {
                if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
                    zval_ptr_dtor_nogc(&op1);
                    ret = FAILURE;
                    break;
                }
                zval_ptr_dtor_nogc(&op1);
            }
            break;
        case ZEND_AST_UNARY_PLUS:
            if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
            } else {
                ZVAL_LONG(&op1, 0);
                ret = add_function(result, &op1, &op2);
                zval_ptr_dtor_nogc(&op2);
            }
            break;
        case ZEND_AST_UNARY_MINUS:
            if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
            } else {
                ZVAL_LONG(&op1, 0);
                ret = sub_function(result, &op1, &op2);
                zval_ptr_dtor_nogc(&op2);
            }
            break;
        case ZEND_AST_ARRAY:
            {
                uint32_t i;
                zend_ast_list *list = zend_ast_get_list(ast);

                if (!list->children) {
                    ZVAL_EMPTY_ARRAY(result);
                    break;
                }
                array_init(result);
                for (i = 0; i < list->children; i++) {
                    zend_ast *elem = list->child[i];
                    if (elem->kind == ZEND_AST_UNPACK) {
                        if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[0], scope) != SUCCESS)) {
                            zval_ptr_dtor_nogc(result);
                            return FAILURE;
                        }
                        if (UNEXPECTED(zend_ast_add_unpacked_element(result, &op1) != SUCCESS)) {
                            zval_ptr_dtor_nogc(&op1);
                            zval_ptr_dtor_nogc(result);
                            return FAILURE;
                        }
                        zval_ptr_dtor_nogc(&op1);
                        continue;
                    }
                    if (elem->child[1]) {
                        if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[1], scope) != SUCCESS)) {
                            zval_ptr_dtor_nogc(result);
                            return FAILURE;
                        }
                    } else {
                        ZVAL_UNDEF(&op1);
                    }
                    if (UNEXPECTED(zend_ast_evaluate(&op2, elem->child[0], scope) != SUCCESS)) {
                        zval_ptr_dtor_nogc(&op1);
                        zval_ptr_dtor_nogc(result);
                        return FAILURE;
                    }
                    if (UNEXPECTED(zend_ast_add_array_element(result, &op1, &op2) != SUCCESS)) {
                        zval_ptr_dtor_nogc(&op1);
                        zval_ptr_dtor_nogc(&op2);
                        zval_ptr_dtor_nogc(result);
                        return FAILURE;
                    }
                }
            }
            break;
        case ZEND_AST_DIM:
            if (ast->child[1] == NULL) {
                zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
            }

            if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
                ret = FAILURE;
                break;
            }

            // DIM on objects is disallowed because it allows executing arbitrary expressions
            if (Z_TYPE(op1) == IS_OBJECT) {
                zval_ptr_dtor_nogc(&op1);
                zend_throw_error(NULL, "Cannot use [] on objects in constant expression");
                ret = FAILURE;
                break;
            }

            if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
                zval_ptr_dtor_nogc(&op1);
                ret = FAILURE;
                break;
            }

            zend_fetch_dimension_const(result, &op1, &op2, (ast->attr & ZEND_DIM_IS) ? BP_VAR_IS : BP_VAR_R);

            zval_ptr_dtor_nogc(&op1);
            zval_ptr_dtor_nogc(&op2);
            if (UNEXPECTED(EG(exception))) {
                return FAILURE;
            }

            break;
        case ZEND_AST_CONST_ENUM_INIT:
        {
            zend_ast *class_name_ast = ast->child[0];
            zend_string *class_name = zend_ast_get_str(class_name_ast);

            zend_ast *case_name_ast = ast->child[1];
            zend_string *case_name = zend_ast_get_str(case_name_ast);

            zend_ast *case_value_ast = ast->child[2];
            zval *case_value_zv = case_value_ast != NULL
                ? zend_ast_get_zval(case_value_ast)
                : NULL;

            zend_class_entry *ce = zend_lookup_class(class_name);
            if (!ce) {
                /* Class may not be available when resolving constants on a dynamically
                 * declared enum during preloading. */
                ZEND_ASSERT(CG(compiler_options) & ZEND_COMPILE_PRELOAD);
                return FAILURE;
            }

            zend_enum_new(result, ce, case_name, case_value_zv);
            break;
        }
        case ZEND_AST_CLASS_CONST:
        {
            zend_string *class_name = zend_ast_get_str(ast->child[0]);
            zend_string *const_name = zend_ast_get_str(ast->child[1]);
            zval *zv = zend_get_class_constant_ex(class_name, const_name, scope, ast->attr);

            if (UNEXPECTED(zv == NULL)) {
                ZVAL_UNDEF(result);
                return FAILURE;
            }
            ZVAL_COPY_OR_DUP(result, zv);
            break;
        }
        case ZEND_AST_NEW:
        {
            zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
            if (!ce) {
                return FAILURE;
            }

            if (object_init_ex(result, ce) != SUCCESS) {
                return FAILURE;
            }

            zend_ast_list *args_ast = zend_ast_get_list(ast->child[1]);
            if (args_ast->attr) {
                /* Has named arguments. */
                HashTable *args = zend_new_array(args_ast->children);
                for (uint32_t i = 0; i < args_ast->children; i++) {
                    zend_ast *arg_ast = args_ast->child[i];
                    zend_string *name = NULL;
                    zval arg;
                    if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
                        name = zend_ast_get_str(arg_ast->child[0]);
                        arg_ast = arg_ast->child[1];
                    }
                    if (zend_ast_evaluate(&arg, arg_ast, scope) == FAILURE) {
                        zend_array_destroy(args);
                        zval_ptr_dtor(result);
                        return FAILURE;
                    }
                    if (name) {
                        if (!zend_hash_add(args, name, &arg)) {
                            zend_throw_error(NULL,
                                "Named parameter $%s overwrites previous argument",
                                ZSTR_VAL(name));
                            zend_array_destroy(args);
                            zval_ptr_dtor(result);
                            return FAILURE;
                        }
                    } else {
                        zend_hash_next_index_insert(args, &arg);
                    }
                }

                zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
                if (ctor) {
                    zend_call_known_function(
                        ctor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
                }

                zend_array_destroy(args);
            } else {
                ALLOCA_FLAG(use_heap)
                zval *args = do_alloca(sizeof(zval) * args_ast->children, use_heap);
                for (uint32_t i = 0; i < args_ast->children; i++) {
                    if (zend_ast_evaluate(&args[i], args_ast->child[i], scope) == FAILURE) {
                        for (uint32_t j = 0; j < i; j++) {
                            zval_ptr_dtor(&args[j]);
                        }
                        free_alloca(args, use_heap);
                        zval_ptr_dtor(result);
                        return FAILURE;
                    }
                }

                zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
                if (ctor) {
                    zend_call_known_instance_method(
                        ctor, Z_OBJ_P(result), NULL, args_ast->children, args);
                }

                for (uint32_t i = 0; i < args_ast->children; i++) {
                    zval_ptr_dtor(&args[i]);
                }
                free_alloca(args, use_heap);
            }

            if (EG(exception)) {
                zend_object_store_ctor_failed(Z_OBJ_P(result));
                zval_ptr_dtor(result);
                return FAILURE;
            }
            return SUCCESS;
        }
        default:
            zend_throw_error(NULL, "Unsupported constant expression");
            ret = FAILURE;
    }
    return ret;
}

и видим, что это полноценная функция. Но! где ключевое слово function, что обозначает аж три слова перед именем функции(ZEND_API, zend_result, ZEND_FASTCALL) Макросы это, структуры, объединения? Где возвращаемый тип? И почему в других языках функции задаются проще, например:

final static public function get_name(int|float $var): int|float
{
    return $var;
}

Где сразу понятно, что возвращает функция и есть ключевое слово function


Ответы (1 шт):

Автор решения: Fat-Zer

где ключевое слово function

В Си нет такого ключевого слова.

что обозначает аж три слова перед именем функции(ZEND_API, zend_result, ZEND_FASTCALL) Макросы это, структуры, объединения

ZEND_API и ZEND_FASTCALL — это макросы; по болей-менее общепринятым соглашениям все идентификаторы, которые состоят из заглавных букв являются макросами. Оба, вероятнее всего, раскрываются в компиляторо/платформо-зависимые атрибуты. Определяются такие вещи обычно где-то в недрах системах сборки.

  • ZEND_API, исходя из названия, скорей всего, говорит о том, что функция является частью публичного API разделяемой библиотеки. В зависимости от платформы и компилятора, стоит ожидать, что оно раскроется во что-то вроде __declspec(dllexport) на win и __attribute__ ((visibility ("default"))) или пустую строку в *nix'ах.

  • ZEND_FASTCALL, опять же, исходя из названия, говорит, что для этой функции нужно использовать fastcall ABI. Что это значит конкретно зависит от платформы. Может раскрыться во что-то вроде __attribute__ ((fastcall)).

Где возвращаемый тип?

zend_result — это возвращаемый тип. Вероятнее всего, является просто int'ом.

И почему в других языках функции задаются проще

На вкус и цвет все фломастеры разные.


Дополнение

Тогда вопросы. Это все-таки функция да?

да

Немного про то, что макросы вообще такое

И сколько таких макросов можно записать у функции? Например ZEND_API ANOTHER_API zend_result ZEND_SLOWCALL ZEND_FASTCALL zend_ast_evaluate(){} прокатит ли?

Макросы обрабатываются препроцессором до компиляции. Вместо них просто подставляется заданная строка (возможно пустая). т.е. в каком-то файле, который это включает задано

#define ZEND_API __attribute__ ((visibility ("default"))
#define ZEND_FASTCALL __attribute__ ((fastcall))

т.е. вместо строки

ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);

компилятор получит на вход строку вида:

__attribute__ ((visibility ("default")) zend_result __attribute__ ((fastcall)) zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);

А если они определены как пустые строки:

#define ZEND_API
#define ZEND_FASTCALL

то компилятор получит уже строку вида:

zend_result zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);

т.е., вообще говоря, макросов может быть сколько угодно и в любом месте, но будет ли код корректен после препроцессирования — зависит от того, как они определены.

«Атрибуты» из примеров выше в свою очередь не определены стандартом языка и являются расширениями компилятора. Для разных компиляторов они зачастую разные. Их возможные значения и семантику стоит смотреть в документации своего компилятора. Поэтому (а также потому что они обычно выглядят моструозно) их обычно и прячут внутрь макроопределений — так, изменяя их, можно относительно легко сконфигурировать один и тот же исходный код, чтобы он компилировался разными компиляторами и под разные платформы.

→ Ссылка