目次
PHP 関数の登録と使用
zend_function_entry 構造
拡張機能がロードされると、PHP 関数が ZEND エンジンに登録されます。拡張機能は、拡張構造体内で関数ベクトルを宣言できます。拡張機能によって宣言された関数は、
これまでのところ、拡張機能を「コンパイル」して PHP にロードすると、関数レンダリングのリフレクション メカニズムがわかります。パラメータが欠落しています。
C 语言的 PHP 函数结构和 API
解析参数:zend_parse_parameters()
添加测试
玩转常量
使用 Hashtable (PHP 数组)
管理引用
ホームページ バックエンド開発 PHPチュートリアル 小さな知識と大きな知識のPHP関数を登録する

小さな知識と大きな知識のPHP関数を登録する

Sep 03, 2020 pm 04:24 PM
PHP関数

小さな知識と大きな知識のPHP関数を登録する

[関連する学習の推奨事項: 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 として渡されます。 次に、

フラグ

。この章では詳しく説明しません。これらは内部で使用されます。詳細については、zend_function セクションを参照してください。 PHP 関数の登録

拡張機能がロードされると、PHP 関数が ZEND エンジンに登録されます。拡張機能は、拡張構造体内で関数ベクトルを宣言できます。拡張機能によって宣言された関数は、

User

関数 (PHP ユーザーで宣言および使用される関数) とは対照的に、Core 関数と呼ばれ、現在のリクエストの最後には呼び出されません。 :いつでもご利用いただけます。 念のため、以下は読みやすさを目的とした 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 にロードすると、関数レンダリングのリフレクション メカニズムがわかります。パラメータが欠落しています。

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 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP関数を通じて画像の遅延読み込み効果を最適化するにはどうすればよいですか? PHP関数を通じて画像の遅延読み込み効果を最適化するにはどうすればよいですか? Oct 05, 2023 pm 12:13 PM

PHP 関数を通じて画像の遅延読み込み効果を最適化するにはどうすればよいですか?インターネットの発展に伴い、Web ページ内の画像の数が増加し、ページの読み込み速度に圧力がかかっています。ユーザーエクスペリエンスを向上させ、読み込み時間を短縮するために、画像の遅延読み込みテクノロジーを使用できます。画像の遅延読み込みにより、画像の読み込みが遅れる可能性があります。画像は、ユーザーが表示領域までスクロールしたときにのみ読み込まれるため、ページの読み込み時間が短縮され、ユーザー エクスペリエンスが向上します。 PHP Web ページを作成する場合、いくつかの関数を作成することで画像の遅延読み込み効果を最適化できます。詳細は以下

PHP関数を使用してメモリ使用量を削減するにはどうすればよいですか? PHP関数を使用してメモリ使用量を削減するにはどうすればよいですか? Oct 05, 2023 pm 01:45 PM

PHP 関数を使用してメモリ使用量を削減する方法 開発において、メモリ使用量は非常に重要な考慮事項です。プログラムで大量のメモリが使用されると、速度が低下したり、プログラムがクラッシュしたりする可能性があります。したがって、メモリ使用量を合理的に管理し削減することは、すべての PHP 開発者が注意を払うべき問題です。この記事では、PHP 関数を通じてメモリ使用量を削減するいくつかの方法を紹介し、読者の参考となる具体的なコード例を示します。 PHP で変数を解放するには、unset() 関数を使用します。変数が不要になった場合は、次を使用します。

PHP 関数の紹介—curl_multi_getcontent(): cURL セッションのコンテンツを取得する PHP 関数の紹介—curl_multi_getcontent(): cURL セッションのコンテンツを取得する Jul 26, 2023 pm 03:01 PM

PHP 関数の紹介—curl_multi_getcontent(): cURL セッションのコンテンツを取得する PHP 開発では、ネットワークを介して他のサーバーにデータを要求することがよくあります。 cURL (ClientURL) は、PHP でのネットワーク通信に使用される強力な PHP 拡張ライブラリです。 cURL は一連の関数を提供します。そのうちの 1 つは、cURL セッションのコンテンツを取得するために使用されるcurl_multi_getcontent()です。

PHP の非推奨: 関数 ereg_replace() は非推奨です - 解決策 PHP の非推奨: 関数 ereg_replace() は非推奨です - 解決策 Aug 18, 2023 am 10:48 AM

PHPDeprecated: Functionereg_replace()isdeprecated-Solution PHP で開発する場合、一部の関数が非推奨と宣言されるという問題によく遭遇します。これは、最新の PHP バージョンでは、これらの関数が削除または置き換えられる可能性があることを意味します。一般的な例の 1 つは ereg_replace() 関数です。 ereg_replace

PHPの画像処理機能を利用した画像編集・加工機能の実装方法まとめ PHPの画像処理機能を利用した画像編集・加工機能の実装方法まとめ Nov 20, 2023 pm 12:31 PM

PHPの画像処理関数は、画像の加工・編集に特化した関数群であり、開発者に豊富な画像処理機能を提供します。これらの機能を通じて、開発者は画像のトリミング、拡大縮小、回転、透かしの追加などの操作を実装して、さまざまな画像処理のニーズを満たすことができます。まずはPHPの画像処理関数を使って画像の切り抜き機能を実現する方法を紹介します。 PHP には、画像のトリミングに使用できる imagecrop() 関数が用意されています。トリミング領域の座標とサイズを渡すことで、画像をトリミングできます

PHP 関数と他の言語の関数の比較 PHP 関数と他の言語の関数の比較 Apr 10, 2024 am 10:03 AM

PHP 関数には他の言語の関数との類似点がありますが、いくつかの独自の機能もあります。構文的には、PHP 関数は function で宣言され、JavaScript は function で宣言され、Python は def で宣言されます。パラメータと戻り値の点では、PHP 関数はパラメータを受け入れて値を返します。JavaScript と Python にも同様の関数がありますが、構文が異なります。スコープに関しては、PHP、JavaScript、Python の関数はすべてグローバル スコープまたはローカル スコープを持ち、グローバル関数にはどこからでもアクセスできますが、ローカル関数には宣言スコープ内でのみアクセスできます。

PHP 関数の概要: strtr() 関数 PHP 関数の概要: strtr() 関数 Nov 03, 2023 pm 12:15 PM

PHP 関数の紹介: strtr() 関数 PHP プログラミングにおいて、strtr() 関数は非常に便利な文字列置換関数です。文字列内の指定した文字または文字列を他の文字または文字列に置き換えるのに使用されます。この記事では、strtr() 関数の使用法を紹介し、具体的なコード例をいくつか示します。 strtr() 関数の基本構文は次のとおりです。 strtr(string$str, array$replace) ここで、$str は置換される元の単語です。

PHP 関数のパフォーマンスはどの程度ですか? PHP 関数のパフォーマンスはどの程度ですか? Apr 18, 2024 pm 06:45 PM

さまざまな PHP 関数のパフォーマンスは、アプリケーションの効率にとって非常に重要です。パフォーマンスの良い関数には echo や print などがありますが、str_replace、array_merge、file_get_contents などの関数のパフォーマンスは低くなります。たとえば、str_replace 関数は文字列の置換に使用され、中程度のパフォーマンスを発揮しますが、sprintf 関数は文字列の書式設定に使用されます。パフォーマンス分析によると、1 つの例の実行にかかる時間はわずか 0.05 ミリ秒であり、関数が適切に実行されることが証明されています。したがって、関数を賢く使用すると、アプリケーションをより高速かつ効率的に実行できます。

See all articles