小知识大学问的注册 PHP 函数-php教程

资源魔 33 0

【相干学习保举:php编程(视频)】

PHP函数的注册以及应用

PHP扩大的次要指标是为用户注册新的PHP函数,PHP函数十分复杂,很难齐全了解它们与Zend引擎亲密相干的机制,但侥幸的是, 咱们正在本章中没有需求这些常识,由于PHP扩大机制提供了许多办法来形象如斯复杂的内容。

正在扩大中注册并应用一个新的PHP函数是一个简略的步骤. 但是,要粗浅了解全体状况,则要复杂患上多。zend_function章节的第一步 可能会有所协助.

显然,你需求把握类型, 特地是 zendValues 以及 内存治理. 当然, 理解你的钩子.

zend_function_entry 构造

没有要以及 zend_function 构造混杂,zend_function_entry 是用正在扩大中针对引擎注册函数的。看这里:

#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value

typedef struct _zend_function_entry {
        const char *fname;
        void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
        const struct _zend_internal_arg_info *arg_info;
        uint32_t num_args;
        uint32_t flags;
} zend_function_entry;

你会发现该构造其实不复杂,这就是申明以及注册新性能所需求的。让咱们一同具体引见:

函数的名字:fname。没甚么好增补的,你晓得它的用处对吧?只要留意是 const char * 类型。这没有实用于引擎。此 fname是一个模子,引擎将会从 外部的 zend_string 创立。

而后来看 handler。这是指向 C 代码的函数指针,它将会是函数的主体。这里,咱们将应用宏来简化其申明(等等会看到)。进入此函数,咱们可以解析函数接纳的参数,而且天生一个前往值,就像任何 PHP 用户区的函数。留意,这个前往值作为参数通报到咱们的解决顺序。

争执。arg_info 变量是对于申明咱们的函数将接纳的 API 参数。一样,这局部可能很难深化了解,但咱们没有需求了解太深,咱们再次应用宏进行形象以及简化参数申明。你要晓得的是,正在这里你没有需求申明任何参数便可应用该函数,然而咱们激烈倡议你这么做。咱们将回到这里。参数是一组 arg_info,因而它的巨细作为 num_args 通报。

而后是 flags。正在这章咱们没有具体阐明它。这些是外部应用的,你可正在 zend_function 章节理解具体信息。

注册 PHP 函数

当加载扩大时,PHP 函数会被注册到 ZEND 引擎傍边。一个扩大能够正在扩大构造中申明一个函数向量。被扩大申明的函数被称为 外围 函数,与 用户 函数(正在PHP用户中被申明以及应用的函数)相同,它们正在以后的申请完结时没有会被登记:能够不断应用。

提示一下,如下是为了不便可读性对 PHP 扩大构造的简写

struct _zend_module_entry {
        unsigned short size;
        unsigned int zend_api;
        unsigned char zend_debug;
        unsigned char zts;
        const struct _zend_ini_entry *ini_entry;
        const struct _zend_module_dep *deps;
        const char *name;
        const struct _zend_function_entry *functions;     /* 函数申明向量 */
        int (*module_startup_func)(INIT_FUNC_ARGS);
        int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    /* ... */
};

您将向函数向量通报一个已申明的函数向量。让咱们一同来看一个简略的例子:

/* pib.c 头文件*/
PHP_FUNCTION(fahrenheit_to_celsius)
{

}

static const zend_function_entry pib_functions[] =
{
    PHP_FE(fahrenheit_to_celsius, NULL)
};

zend_module_entry pib_module_entry = {
    STANDARD_MODULE_HEADER,
    "pib",
    pib_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    "0.1",
    STANDARD_MODULE_PROPERTIES
};

咱们来尝尝一个简略的函数 fahrenheit_to_celsius() (名字通知了咱们它的作用)

经过应用 PHP_FUNCTION() 宏来界说一个函数。后者将通报它的参数并扩大成正确的构造。而后,咱们把函数符号汇总并将其增加到 pib_functions 向量中。这就是经过 zend_module_entry 符号延长的 zend_function_entry * 类型。正在此向量中,咱们经过 PHP_FE 宏增加咱们的 PHP 函数。后者需求 PHP 函数称号,和咱们通报 NULL 值时的一个参数向量。

正在 php_pib.h 头文件中,咱们应该像 C 言语同样正在这里申明咱们的函数:

/* pib.h 头文件*/
PHP_FUNCTION(fahrenheit_to_celsius);

如你所见,申明函数的确很容易。宏为咱们干完了一切难活。如下是以及上文相反的代码,然而却扩大了宏,因而你能够看下它们是若何运转的:

/* pib.c */
void zif_fahrenheit_to_celsius(zend_execute_data *execute_data, zval *return_value)
{

}

static const zend_function_entry pib_functions[] =
{
    { "fahrenheit_to_celsius", zif_fahrenheit_to_celsius, ((void *)0),
        (uint32_t) (sizeof(((void *)0))/sizeof(struct _zend_internal_arg_info)-1), 0 },
}

请留意 PHP_FUNCTION() 是若何以 zif_ 扫尾扩大为 C 符号的。‘zif’ 被增加到你的函数称号中,以避免PHP 及其模块正在编译中造成符号称号抵触。因而,咱们的 fahrenheit_to_celsius() PHP 函数应用了 zif_fahrenheit_to_celsius() 的解决顺序。它简直以及每一个 PHP 函数同样。假如你搜寻 zif_var_dump,就能够浏览PHP var_dump() 的源码函数等。

申明函数参数

到今朝为止,假如 「你编译」 扩大并将其加载到PHP中,你能够瞥见函数出现的反射机制:

> ~/php/bin/php -dextension=pib.so --re pib
Extension [ <persistent> extension #37 pib version 0.1 ] {

  - Functions {
    Function [ <internal:pib> function fahrenheit_to_celsius ] {
    }
}

然而它短少参数。假如咱们公布一个 fahrenheit_to_celsius($fahrenheit) 函数署名,则需求一个强迫参数。

你必需理解,函数申明以及函数外部的运转有关。这象征着即使不申明参数,咱们如今编写函数也可能会起作用。

留意

申明参数尽管没有是强迫性的,然而咱们激烈保举应用。反射 API 可经过应用参数猎取函数的信息。Zend 引擎也用到参数,尤为是当咱们谈及援用传参或许前往援用的函数时。

要申明参数,咱们必需要相熟 zend_internal_arg_info 构造:

typedef struct _zend_internal_arg_info {
        const char *name;
        const char *class_name;
        zend_uchar type_hint;
        zend_uchar pass_by_reference;
        zend_bool allow_null;
        zend_bool is_variadic;
} zend_internal_arg_info;

没须要具体阐明每一个字段,然而想要了解参数却比这类独自构造复杂患上多。侥幸的是,咱们再次为你提供了一些宏来形象这艰巨的工作。

ZEND_BEGIN_ARG_INFO_EX(arginfo_fahrenheit_to_celsius, 0, 0, 1)
    ZEND_ARG_INFO(0, fahrenheit)
ZEND_END_ARG_INFO()

下面的代码具体的阐明了若何创立参数,但当咱们扩大宏时,咱们会感应有些艰难:

static const zend_internal_arg_info arginfo_fahrenheit_to_celsius[] = {
            { (const char*)(zend_uintptr_t)(1), ((void *)0), 0, 0, 0, 0 },
            { "fahrenheit", ((void *)0), 0, 0, 0, 0 },
    };

正如咱们所见,宏创立了一个 zend_internal_arg_info 构造。假如你浏览过这种宏的 API,那末对咱们来讲所有都变患上分明了:

/* API only */
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)
#define ZEND_ARG_INFO(pass_by_ref, name)
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null)
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null)
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name)

这一系列的宏能够让你解决每一个用例。
This bunch of macros allow you to deal with every use-case.

  • ZEND_BEGIN_ARG_INFO_EX() 容许你申明你的函数能接纳几何个须要参数。它还容许你申明一个 &return_by_ref() 函数。
  • 那末你每一个参数都需求 ZEND_ARG_***_INFO() 之一。应用它你能够判别参数能否为 &$passed_by_ref 和能否需求类型提醒。

留意

假如你没有晓得怎么去定名参数向量符号,则一种做法是应用 ‘arginfo_[function name]’ 模式。

以是回到咱们的 fahrenheit_to_celsius() 函数,咱们这里声明一个简略的按值前往函数(十分经典的用例),此中一个参数称为 fahrenheit ,且未经过援用通报(又一次的传统用例)。

这就创立了类型 zend_internal_arg_info[] (一个向量, 或一个数组, 都相反) 的 arginfo_fahrenheit_to_celsius 符号,如今咱们必需要应用该符号回到函数申明中来增加给它一些参数。

PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius)

至此咱们实现了,如今反射能够瞥见参数了,并会奉告引擎正在援用没有婚配的状况下该怎样做。太棒了!

留意

另有其余宏。ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX() f.e. 你能够正在 Zend/zend_api.h 的源代码中找到一切这些文件。

C 言语的 PHP 函数构造以及 API

好的。上面是一个 PHP 函数。你能够应用它,并用 PHP 言语申明它(用户区):

function fahrenheit_to_celsius($fahrenheit)
{
    return 5/9 * ($fahrenheit - 32);
}

这是一个简略的函数,以便你能够了解它。这是用 C 编程时的样子:

PHP_FUNCTION(fahrenheit_to_celsius)
{
    /* code to go here */
}

宏开展后,将失去:

void zif_fahrenheit_to_celsius(zend_execute_data *execute_data, zval *return_value)
{
    /* code to go here */
}

劳动一下,思考一下次要差别。

起首希奇的是,正在 C 中,该函数没有会前往任何货色。那是一个 void 申明的函数,你不成以正在这里前往任何货色。然而咱们留意到咱们接纳了一个 zval *类型的return_value参数,看起来很没有错。用 C 编写 PHP 函数时,你将失去一个指向 zval 的前往值 ,心愿你们能玩一玩。这有更多对于 zval 的资本.

留意

正在 C 扩大中编写 PHP 函数时,你接纳作为参数的前往值,而且你没有会从 C 函数前往任何货色。

好的,第一点诠释了。第二点你可能曾经猜到了:PHP 函数的参数正在那里?$fahreinheit正在那里?很难诠释齐全,现实上,这很难。

然而咱们没有需求正在这里理解细节。让咱们诠释下要害的概念:

  • 参数曾经经过引擎推入货仓旅馆中。它们都正在内存的某个中央挨着堆放。
  • 假如你的函数被挪用,这象征着不梗阻谬误,因而你能够阅读参数货仓旅馆,并读取运转时通报的参数。不只是你申明的那些,还包罗那些正在挪用函数时通报给函数的。引擎会为你解决所有。
  • 为了读取参数,你需求一个函数或许宏,而且需求晓得有几何参数曾经推入货仓旅馆中,以便晓得何时应该中止读取它们。
  • 所有都依照你接纳的作为参数的zend_execute_data *execute_data。然而如今咱们没有具体阐明。

解析参数:zend_parse_parameters()

要读取参数,欢送应用 zend_parse_parameters() API (称为 ‘zpp’).

留意

当正在 C 扩大中编写 PHP 函数时,多亏了zend_parse_parameters() 函数以及它的冤家,你接纳到 PHP 函数的参数。

zend_parse_parameters() 是一个函数,它将为你到 Zend 引擎的货仓旅馆中读取参数。你要通知它要读取几何个参数,和想要它为你提供哪一种类型。该函数将依据 PHP 类型转换规定(假如需求,而且有可能的话)将参数转换为你要的类型。假如你需求一个整型,但给了一个浮点型,假如不严格的类型提醒规定被梗阻,则引擎会将浮点型转换为整型,而后给你。

让咱们来看看这个函数:

PHP_FUNCTION(fahrenheit_to_celsius)
{
    double f;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &f) == FAILURE) {
        return;
    }

    /* continue */
}

咱们心愿正在 f 变量上失去一个 double 类型。而后咱们挪用zend_parse_parameters()

第一个参数是运转时已给定的参数数量。ZEND_NUM_ARGS() 是一个宏,它会通知咱们,而后咱们用它去奉告 zpp() 需求读取几何个参数。

而后咱们通报一个const char *类型的 “d” 字符串。正在这里,要求你为每个接纳的参数写一个字母,除了了一些未正在这里讲述的非凡状况。一个简略的 “d” 示意 “假如需求的话,我想要第一个接纳的参数转换为 float (double)”

而后,正在该字符串之后通报 C 真正需求的参数,以餍足第二个参数。一个 “d” 示意 “一个 double”,而后你如今通报 double 的 地点,引擎将会填充其值。

留意

你老是将一个指针通报给要填充的数据。

你能够正在 PHP 源代码的 README.PARAMETER_PARSING_API文件中找到对于 zpp() 的字符串格局的最新协助。细心浏览,由于这是你可能搞错并造成顺序解体的一步。始终反省你的参数,始终依据你提供的格局字符串通报相反数目的参数变量,和你要求的类型相反。要符合逻辑。

一样留意一下参数解析的失常进程。zend_parse_parameters()函数正在胜利时应前往 SUCCESS或许正在失败时应前往FAILURE。失败可能示意你不应用ZEND_NUM_ARGS()值,而是手动提供一个值(坏主见)。或许正在参数解析时做错了甚么。假如是这样,那末是时分 return 了,终止以后函数(你应该从 C 函数中前往 void,以是只需 return)。

到今朝为止,咱们接纳了一个 double。让咱们执行数学运算并前往后果:

static double php_fahrenheit_to_celsius(double f)
{
    return ((double)5/9) * (double)(f - 32);
}

PHP_FUNCTION(fahrenheit_to_celsius)
{
    double f;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &f) == FAILURE) {
        return;
    }

    RETURN_DOUBLE(php_fahrenheit_to_celsius(f));
}

如你所知的zval 的工作原理,前往值对你来讲应该很容易。你必需填写 return_value

一些 RETURN_***() 宏和一些RETVAL_***()宏都是专门用来这么做的。这两个仅设置return_value zval 的类型以及值,然而RETURN_***()宏前面会随着一个从以后函数前往的 Creturn

或许,API 提供了一系列行止理息争析参数的宏。假如你对 python 款式阐明符困惑的话,那末它更具备可读性。

你需求应用如下宏来开端以及完结函数参数解析:

ZEND_PARSE_PARAMETERS_START(min_argument_count, max_argument_count) /* 需求两个参数 */
/* 这里咱们将应用参数列表 */
ZEND_PARSE_PARAMETERS_END();

可用的参数宏能够列出以下:

Z_PARAM_ARRAY()                /* old "a" */
Z_PARAM_ARRAY_OR_OBJECT()      /* old "A" */
Z_PARAM_BOOL()                 /* old "b" */
Z_PARAM_CLASS()                /* old "C" */
Z_PARAM_DOUBLE()               /* old "d" */
Z_PARAM_FUNC()                 /* old "f" */
Z_PARAM_ARRAY_HT()             /* old "h" */
Z_PARAM_ARRAY_OR_OBJECT_HT()   /* old "H" */
Z_PARAM_LONG()                 /* old "l" */
Z_PARAM_STRICT_LONG()          /* old "L" */
Z_PARAM_OBJECT()               /* old "o" */
Z_PARAM_OBJECT_OF_CLASS()      /* old "O" */
Z_PARAM_PATH()                 /* old "p" */
Z_PARAM_PATH_STR()             /* old "P" */
Z_PARAM_RESOURCE()             /* old "r" */
Z_PARAM_STRING()               /* old "s" */
Z_PARAM_STR()                  /* old "S" */
Z_PARAM_ZVAL()                 /* old "z" */
Z_PARAM_VARIADIC()             /* old "+" and "*" */

为了增加一个参数作为可选参数,咱们应用如下宏:

Z_PARAM_OPTIONAL              /* old "|" */

这是基于宏的参数解析款式的示例:

PHP_FUNCTION(fahrenheit_to_celsius)
{
    double f;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_DOUBLE(f);
    ZEND_PARSE_PARAMETERS_END();

    RETURN_DOUBLE(php_fahrenheit_to_celsius(f));
}

增加测试

假如你已浏览无关测试的章节(看应用 .phpt 文件测试),如今你应该编写一个简略的例子:

--TEST--
Test fahrenheit_to_celsius
--SKIPIF--
<?php if (!extension_loaded("pib")) print "skip"; ?>
--FILE--
<?php
printf("%.2f", fahrenheit_to_celsius(70));
?>
--EXPECTF--
21.11

并启动make test

玩转常量

让咱们来看一个初级的例子。咱们来增加相同的函数:celsius_to_fahrenheit($celsius):

ZEND_BEGIN_ARG_INFO_EX(arginfo_celsius_to_fahrenheit, 0, 0, 1)
    ZEND_ARG_INFO(0, celsius)
ZEND_END_ARG_INFO();

static double php_celsius_to_fahrenheit(double c)
{
    return (((double)9/5) * c) + 32 ;
}

PHP_FUNCTION(celsius_to_fahrenheit)
{
    double c;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &c) == FAILURE) {
        return;
    }

    RETURN_DOUBLE(php_celsius_to_fahrenheit(c));
}

static const zend_function_entry pib_functions[] =
{
    PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius) /* Done above */
    PHP_FE(celsius_to_fahrenheit,arginfo_celsius_to_fahrenheit) /* just added */
    PHP_FE_END
};

如今是一个更复杂的用例,正在将它作为 C 扩大完成以前,正在 PHP 中展现它:

const TEMP_CONVERTER_TO_CELSIUS     = 1;
const TEMP_CONVERTER_TO_FAHREINHEIT = 2;

function temperature_converter($temp, $type = TEMP_CONVERTER_TO_CELSIUS)
{
    switch ($type) {
        case TEMP_CONVERTER_TO_CELSIUS:
            return sprintf("%.2f degrees fahrenheit gives %.2f degrees celsius", $temp,
                            fahrenheit_to_celsius($temp));
        case TEMP_CONVERTER_TO_FAHREINHEIT:
            return sprintf("%.2f degrees celsius gives %.2f degrees fahrenheit, $temp,
                            celsius_to_fahrenheit($temp));
        default:
            trigger_error("Invalid mode provided, accepted values are 1 or 2", E_USER_WARNING);
        break;
    }
}

这个例子有助于咱们引见常量

常量正在扩大中很容易治理,就像它们正在用户区同样。常量一般为耐久性的,象征着它们应该正在申请之间放弃其值没有变。假如你晓得 PHP 的生命周期,则应该猜到 MINIT()是向引擎注册常量的正确阶段。

正在外部,这有个常量,一个zend_constant 构造:

typedef struct _zend_constant {
    zval value;
    zend_string *name;
    int flags;
    int module_number;
} zend_constant;

真的是一个简略的构造(假如你深化理解常量是若何治理到引擎中,那可能会是一场恶梦)。你申明了namevalue,一些flags(没有是不少),而且module_number主动设置为你的扩大编号(不必留意它)。

要注册常量,一样的,这一点都没有难,一堆宏能够帮你实现:

#define TEMP_CONVERTER_TO_FAHRENHEIT 2
#define TEMP_CONVERTER_TO_CELSIUS 1

PHP_MINIT_FUNCTION(pib)
{
    REGISTER_LONG_CONSTANT("TEMP_CONVERTER_TO_CELSIUS", TEMP_CONVERTER_TO_CELSIUS, CONST_CS|CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("TEMP_CONVERTER_TO_FAHRENHEIT", TEMP_CONVERTER_TO_FAHRENHEIT, CONST_CS|CONST_PERSISTENT);

    return SUCCESS;
}

留意

给出 C 宏的 PHP 常量值是一个很好的理论。事件变患上容易了,这就是咱们做的。

依据你的常量类型,你将应用 REGISTER_LONG_CONSTANT()REGISTER_DOUBLE_CONSTANT()等等。API 以及宏位于 Zend/zend_constants.h中。

flag 正在CONST_CS (case-sensitive constant 巨细写敏感常量,咱们想要的)以及CONST_PERSISTENT(耐久性常量,正在申请中也是咱们想要的)之间是夹杂的 OR 操作。

如今正在 C 中的temperature_converter($temp, $type = TEMP_CONVERTER_TO_CELSIUS)函数:

ZEND_BEGIN_ARG_INFO_EX(arginfo_temperature_converter, 0, 0, 1)
    ZEND_ARG_INFO(0, temperature)
    ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO();

咱们失去了一个必需的参数,两个中的一个。那就是咱们申明的。其默许值没有是一个参数申明能够处理的,那将正在一秒钟内实现。

而后咱们将咱们的新函数增加到函数注册向量:

static const zend_function_entry pib_functions[] =
{
    PHP_FE(fahrenheit_to_celsius,arginfo_fahrenheit_to_celsius) /* seen above */
    PHP_FE(celsius_to_fahrenheit,arginfo_celsius_to_fahrenheit) /* seen above */
    PHP_FE(temperature_converter, arginfo_temperature_converter) /* our new function */
}

函数主体:

PHP_FUNCTION(temperature_converter)
{
    double t;
    zend_long mode = TEMP_CONVERTER_TO_CELSIUS;
    zend_string *result;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d|l", &t, &mode) == FAILURE) {
        return;
    }

    switch (mode)
    {
        case TEMP_CONVERTER_TO_CELSIUS:
            result = strpprintf(0, "%.2f degrees fahrenheit gives %.2f degrees celsius", t, php_fahrenheit_to_celsius(t));
            RETURN_STR(result);
        case TEMP_CONVERTER_TO_FAHRENHEIT:
            result = strpprintf(0, "%.2f degrees celsius gives %.2f degrees fahrenheit", t, php_celsius_to_fahrenheit(t));
            RETURN_STR(result);
        default:
            php_error(E_WARNING, "Invalid mode provided, accepted values are 1 or 2");
    }
}

记患上好难看 README.PARAMETER_PARSING_API。它没有是一个很难的 API,你必需相熟它。

咱们应用 “d|l” 作为 zend_parse_parameters()的参数。一个 double、或(管道“|”)、一个 long。留意,假如正在运转时没有提供可选参数(提示一下,ZEND_NUM_ARGS()是甚么),则 &mode没有会被 zpp() 涉及。这就是为何咱们提供了一个TEMP_CONVERTER_TO_CELSIUS默许值给该变量。

而后咱们应用 strpprintf() 去构建一个 zend_string,而且应用 RETURN_STR() 前往它到 return_value zval。

留意

strpprintf() 以及它的冤家们正在打印函数章节有诠释过。

应用 Hashtable (PHP 数组)

如今让咱们来玩一下PHP 数组并设计:

function multiple_fahrenheit_to_celsius(array $temperatures)
{
    foreach ($temperatures as $temp) {
        $return[] = fahreinheit_to_celsius($temp);
    }

    return $return;
}

以是正在 C 言语完成的时分,咱们需求zend_parse_parameters()并申请一个数组,遍历它,进行数学运算,并将后果作为数组增加到 return_value

ZEND_BEGIN_ARG_INFO_EX(arginfo_multiple_fahrenheit_to_celsius, 0, 0, 1)
    ZEND_ARG_ARRAY_INFO(0, temperatures, 0)
ZEND_END_ARG_INFO();

static const zend_function_entry pib_functions[] =
{
        /* ... */
    PHP_FE(multiple_fahrenheit_to_celsius, arginfo_multiple_fahrenheit_to_celsius)
    PHP_FE_END
};

PHP_FUNCTION(multiple_fahrenheit_to_celsius)
{
    HashTable *temperatures;
    zval *data;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &temperatures) == FAILURE) {
        return;
    }
    if (zend_hash_num_elements(temperatures) == 0) {
        return;
    }

    array_init_size(return_value, zend_hash_num_elements(temperatures));

    ZEND_HASH_FOREACH_VAL(temperatures, data)
        zval dup;
        ZVAL_COPY_VALUE(&dup, data);
        convert_to_double(&dup);
    add_next_index_double(return_value, php_fahrenheit_to_celsius(Z_DVAL(dup)));
    ZEND_HASH_FOREACH_END();
}

留意

你需求晓得 Hashtable 的工作原理,而且必读 zval 章节

正在这里,C 言语那局部将更快,由于没有需求正在 C 轮回中挪用 PHP 函数,然而一个动态(可能由编纂器内联的)函数,它的运转速率快了几个数目级,而且运转低级 CPU 指令所需的工夫也更少。这并非说这个小小的演示函数正在代码功能方面需求如斯多的存眷,只需记住为何咱们有时会应用 C 言语替代 PHP。

治理援用

如今让咱们开端玩 PHP 援用。您曾经从 zval 章节 理解到援用是正在引擎中应用的一种非凡技术。作为提示,援用(咱们指的是&$php_reference)是调配给 zval的,存储正在 zval 的容器中。

以是,只需记住援用是甚么和它们的设计目的,就没有难将它们解决成 PHP 函数。

假如你的函数承受一个参数作为援用,你必需正在参数署名中申明,并从你的 zend_parse_parameter() 挪用中通报一个援用。

让咱们像往常同样,起首应用 PHP 示例:因而,如今C中,起首咱们必需更改 arg_info

ZEND_BEGIN_ARG_INFO_EX(arginfo_fahrenheit_to_celsius, 0, 0, 1)
    ZEND_ARG_INFO(1, fahrenheit)
ZEND_END_ARG_INFO();

" 1 ",中通报的 ZEND_ARG_INFO() 宏通知引擎必需经过援用通报参数。

而后,当咱们接纳到参数时,咱们应用 z 参数类型,以通知咱们心愿将它作为一个 zval 给出。当咱们向引擎提醒它应该向咱们通报一个援用这一现实时,咱们将取得对该 zval 的援用,也就是它的类型为is_reference时,咱们只要要解援用它(即猎取存储到 zval中的 zval),并按原样修正它,由于援用的预期行为是您必需修正援用所携带的值:

PHP_FUNCTION(fahrenheit_to_celsius)
{
    double result;
    zval *param;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &param) == FAILURE) {
        return;
    }

    ZVAL_DEREF(param);
    convert_to_double(param);

    ZVAL_DOUBLE(param, php_fahrenheit_to_celsius(Z_DVAL_P(param)));
}

实现。

留意

默许 return_value 值为 NULL。假如咱们没有碰它,函数将前往PHP的 NULL


想理解更多编程学习,敬请存眷php培训栏目!

以上就是小常识年夜学识的注册 PHP 函数的具体内容,更多请存眷资源魔其它相干文章!

标签: php开发教程 php开发资料 php开发自学 PHP函数

抱歉,评论功能暂时关闭!