Maison  >  Article  >  développement back-end  >  Enregistrez la fonction PHP pour les petites connaissances et les grandes connaissances

Enregistrez la fonction PHP pour les petites connaissances et les grandes connaissances

coldplay.xixi
coldplay.xixiavant
2020-09-03 16:24:442132parcourir

Enregistrez la fonction PHP pour les petites connaissances et les grandes connaissances

[Recommandations d'apprentissage associées : programmation php (vidéo)]

Enregistrement et utilisation des fonctions PHP

L'objectif principal de l'extension PHP est d'enregistrer de nouvelles fonctions PHP pour les utilisateurs, les fonctions PHP sont très complexes et il est difficile de les comprendre complètement. comprenez-les avec les mécanismes Zend étroitement liés au moteur, mais heureusement nous n'avons pas besoin de ces connaissances dans ce chapitre car le mécanisme d'extension PHP fournit de nombreuses façons d'abstraire une telle complexité.

Enregistrer et utiliser une nouvelle fonction PHP dans une extension est une étape simple. Cependant, il est beaucoup plus complexe de comprendre en profondeur la situation globale. La première étape du chapitre zend_function peut être utile

Évidemment, vous devez maîtriser les types, en particulier les zendValues ​​​​et la gestion de la mémoire.

la structure zend_function_entry<.>

À ne pas confondre avec la structure zend_function,

est utilisé dans les extensions pour enregistrer des fonctions pour le moteur. Regardez ici : 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;
Vous verrez que la structure n'est pas compliquée, tout cela est nécessaire pour déclarer et enregistrer de nouvelles fonctions. Présentons-le ensemble en détail :

Le nom de la fonction :

. Rien à ajouter, vous savez à quoi ça sert, non ? Notez simplement le type fname. Cela ne s'applique pas aux moteurs. Ce const char * est un modèle que le moteur créera à partir du zend_string interne. fname

Alors venez voir

. Il s'agit d'un pointeur de fonction vers le code C qui sera le corps de la fonction. Ici, nous utiliserons des macros pour simplifier sa déclaration (comme nous le verrons dans un instant). En entrant dans cette fonction, nous pouvons analyser les paramètres que la fonction reçoit et générer une valeur de retour, comme n'importe quelle fonction PHP userland. Notez que cette valeur de retour est transmise en paramètre à notre gestionnaire. handler

Argumentation.

Les variables servent à déclarer les paramètres API que notre fonction recevra. Encore une fois, cette partie peut être difficile à comprendre en profondeur, mais nous n'avons pas besoin de la comprendre trop en profondeur, nous utilisons à nouveau des macros pour résumer et simplifier les déclarations de paramètres. Ce que vous devez savoir, c'est que vous n'avez pas besoin de déclarer de paramètres ici pour utiliser la fonction, mais nous vous recommandons fortement de le faire. Nous reviendrons ici. L'argument est un tableau de arg_info, donc sa taille est passée comme arg_info. num_args

Alors

. Nous ne le détaillerons pas dans ce chapitre. Ceux-ci sont utilisés en interne, vous pouvez en apprendre plus à leur sujet dans la section zend_function. flags

Enregistrer la fonction PHP

Lorsque l'extension est chargée, la fonction PHP sera enregistrée dans le moteur ZEND. Une extension peut déclarer un vecteur de fonction dans la structure d'extension. Les fonctions déclarées par les extensions sont appelées fonctions

Contrairement aux fonctions 核心 (fonctions déclarées et utilisées chez les utilisateurs PHP), elles ne sont pas désinscrites à la fin de la requête en cours : elles peuvent toujours être utilisées. 用户

Pour rappel, ce qui suit sont des raccourcis pour les structures d'extension PHP pour plus de lisibilité

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);
    /* ... */
};
Vous passerez un vecteur de fonction déclaré au vecteur de fonction. Regardons un exemple simple :

/* 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
};
Essayons une fonction simple

(le nom nous dit ce qu'elle fait) fahrenheit_to_celsius()

En utilisant la macro

Définir une fonction. Ce dernier transmettra ses arguments et se développera dans la structure correcte. Ensuite, nous résumons les symboles de fonction et les ajoutons au vecteur PHP_FUNCTION(). Il s'agit du type pib_functions étendu par le symbole zend_module_entry. Dans ce vecteur, nous ajoutons notre fonction PHP via la macro zend_function_entry *. Ce dernier nécessite le nom de la fonction PHP, et un vecteur d'arguments si l'on passe des valeurs NULL. PHP_FE

Dans le fichier d'en-tête

php_pib.h, nous devons déclarer nos fonctions ici comme en C :

/* pib.h 头文件*/
PHP_FUNCTION(fahrenheit_to_celsius);
Comme vous pouvez le voir, déclarer des fonctions est vraiment simple . Hong a fait tout le travail pour nous. Voici le même code que ci-dessus, mais avec les macros développées pour que vous puissiez voir comment elles fonctionnent :

/* 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 },
}
Notez comment

est développé en un symbole C commençant par PHP_FUNCTION() . zif_'zif' est ajouté à vos noms de fonctions pour empêcher PHP et ses modules de provoquer des conflits de noms de symboles lors de la compilation. Par conséquent, notre fonction PHP utilise le gestionnaire fahrenheit_to_celsius(). C'est comme presque toutes les fonctions PHP. Si vous recherchez zif_fahrenheit_to_celsius(), vous pouvez lire les fonctions du code source de PHP zif_var_dump, etc. var_dump()

Déclarez les paramètres de la fonction

Jusqu'à présent, si "vous compilez" l'extension et la chargez en PHP, vous pouvez voir le mécanisme de réflexion que la fonction restitue :

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

  - Functions {
    Function [ <internal:pib> function fahrenheit_to_celsius ] {
    }
}
Mais Il manque des paramètres. Si nous publions une signature de fonction

, un paramètre obligatoire est requis. 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培训栏目!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer