【相关学习推荐:@L_502_0@(视频)】
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_valuetypedef 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 函数会被注册到 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 pibExtension [ <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 以及是否需要类型提示。@H_403_167@注意
如果你不知道怎样去命名参数向量符号,则一种做法是使用 ‘arginfo_[function name]’ 模式。
所以回到我们的 fahrenheit_to_celsius()
函数,我们这里申明一个简单的按值返回函数(非常经典的用例),其中一个参数称为 fahrenheit
,且未通过引用传递(又一次的传统用例)。
这就创建了类型 Zend_internal_arg_info[]
(一个向量, 或一个数组, 都相同) 的 arginfo_fahrenheit_to_celsius
符号,现在我们必须要使用该符号回到函数声明中来添加给它一些参数。
PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius)
至此我们完成了,现在反射可以看见参数了,并会告知引擎在引用不匹配的情况下该怎么做。太棒了!
C 语言的 PHP 函数结构和 API注意
还有其他宏。
ZenD_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX()
f.e. 你可以在 Zend/Zend_API.h 的源代码中找到所有这些文件。
好的。下面是一个 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
。但是现在我们不详细说明。@H_403_167@解析参数: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--<?PHPprintf("%.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;
真的是一个简单的结构(如果你深入了解常量是如何管理到引擎中,那可能会是一场噩梦)。你声明了name
,value
,一些flags
(不是很多),并且module_number
自动设置为你的扩展编号(不用注意它)。
要注册常量,同样的,这一点都不难,一堆宏可以帮你完成:
#define TEMP_CONVERTER_TO_FAHRENHEIT 2#define TEMP_CONVERTER_TO_CELSIUS 1PHP_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。
使用 Hashtable (PHP 数组)注意
strpprintf()
和它的朋友们在打印函数章节有解释过。
现在让我们来玩一下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", ¶m) == 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函数的注册和使用所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)