작은 지식과 큰 지식을 위한 PHP 기능 등록
【관련 학습 추천: php 프로그래밍(동영상)】
PHP 함수 등록 및 사용
PHP 확장의 주요 목표는 사용자를 위해 새로운 PHP 함수를 등록하는 것입니다. , PHP 함수는 매우 복잡하고 Zend 엔진과 밀접하게 관련된 메커니즘을 완전히 이해하기 어렵지만 다행스럽게도 PHP 확장 메커니즘은 이러한 복잡성을 추상화하는 다양한 방법을 제공하기 때문에 이 장에서는 이러한 지식이 필요하지 않습니다.
확장 프로그램에 새로운 PHP 함수를 등록하고 사용하는 것은 간단한 단계이지만 전반적인 상황을 깊이 이해하는 것은 훨씬 더 복잡합니다. zend_function 장의 첫 번째 단계가 도움이 될 수 있습니다.
분명히 유형, 특히 zendValues 및 메모리 관리를 숙지해야 합니다.
zend_function_entry 구조체
zend_function과 혼동하지 마세요. struct, zend_function_entry
는 엔진에 대한 함수를 등록하기 위해 확장에서 사용됩니다. 여기를 보세요: 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)
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;
fname
. 추가할 내용은 별로 없습니다. 무엇을 위한 것인지 아시죠? const char *
유형이라는 점에 유의하세요. 이는 엔진에는 적용되지 않습니다. 이 fname
은 엔진이 내부 zend_string에서 생성할 모델입니다. 🎜🎜그럼 핸들러
를 살펴보겠습니다. 이는 함수의 본문이 될 C 코드에 대한 함수 포인터입니다. 여기서는 매크로를 사용하여 선언을 단순화하겠습니다(잠시 후에 살펴보겠지만). 이 함수를 입력하면 PHP 사용자 영역 함수와 마찬가지로 함수가 수신하는 매개변수를 구문 분석하고 반환 값을 생성할 수 있습니다. 이 반환 값은 핸들러에 매개변수로 전달됩니다. 🎜🎜인론. arg_info
변수는 함수가 수신할 API 인수를 선언하는 것입니다. 다시 말하지만, 이 부분은 깊이 이해하기 어려울 수 있지만 너무 깊게 이해할 필요는 없습니다. 매개변수 선언을 추상화하고 단순화하기 위해 다시 매크로를 사용합니다. 알아야 할 것은 함수를 사용하기 위해 여기에 매개변수를 선언할 필요는 없지만 그렇게 하는 것이 좋습니다. 우리는 여기로 다시 올 것이다. 인수는 arg_info
의 배열이므로 해당 크기는 num_args
로 전달됩니다. 🎜🎜그럼 플래그
하세요. 이 장에서는 이에 대해 자세히 설명하지 않습니다. 이는 내부적으로 사용되며 zend_function 섹션에서 이에 대해 자세히 알아볼 수 있습니다. 🎜🎜PHP 함수 등록🎜🎜확장 기능이 로드되면 PHP 함수가 ZEND 엔진에 등록됩니다. 확장은 확장 구조에서 함수 벡터를 선언할 수 있습니다. 확장에 의해 선언된 함수는 Core
함수라고 하며, User
함수(PHP 사용자가 선언하고 사용하는 함수)와 달리 현재 요청이 끝날 때 실행되지 않습니다. 로그아웃됩니다: 영원히 사용할 수 있습니다. 🎜🎜참고로 다음은 가독성을 위한 PHP 확장 구조의 약어입니다.🎜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 }, };
fahrenheit_to_celsius()
를 사용해 보겠습니다(이름에서 그 기능을 알 수 있습니다). 🎜🎜PHP_FUNCTION()
매크로를 사용하여 함수를 정의합니다. 후자는 인수를 전달하고 올바른 구조로 확장됩니다. 그런 다음 함수 기호를 집계하여 pib_functions
벡터에 추가합니다. 이는 zend_module_entry
기호를 통해 확장된 zend_function_entry *
유형입니다. 이 벡터에서는 PHP_FE
매크로를 통해 PHP 함수를 추가합니다. 후자에는 PHP 함수 이름이 필요하고 NULL 값을 전달하는 경우 인수 벡터가 필요합니다. 🎜🎜php_pib.h 헤더 파일에서 C와 마찬가지로 여기에 함수를 선언해야 합니다. 🎜/* 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)
PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius)
PHP_FUNCTION()
이 zif_로 끝나는 방식에 유의하세요. 코드 시작 부분>이 확장됩니다. C 기호로. <em>'zif'</em>가 함수 이름에 추가되어 PHP와 해당 모듈이 컴파일 중에 기호 이름 충돌을 일으키는 것을 방지합니다. 따라서 <code>fahrenheit_to_celsius()
PHP 함수는 zif_fahrenheit_to_celsius()
핸들러를 사용합니다. 거의 모든 PHP 함수와 같습니다. zif_var_dump
로 검색하시면 PHP var_dump()
등의 소스코드 함수를 보실 수 있습니다. 🎜🎜함수 매개변수 선언🎜🎜지금까지 확장 프로그램을 "컴파일"하여 PHP에 로드하면 함수가 렌더링하는 반사 메커니즘을 볼 수 있습니다. 🎜function fahrenheit_to_celsius($fahrenheit) { return 5/9 * ($fahrenheit - 32); }
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;
真的是一个简单的结构(如果你深入了解常量是如何管理到引擎中,那可能会是一场噩梦)。你声明了name
,value
,一些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", ¶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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP 함수를 통해 이미지의 지연 로딩 효과를 최적화하는 방법은 무엇입니까? 인터넷의 발달로 인해 웹 페이지의 이미지 수가 증가하고 있으며 이는 페이지 로딩 속도에 부담을 주고 있습니다. 사용자 경험을 개선하고 로딩 시간을 줄이기 위해 이미지 지연 로딩 기술을 사용할 수 있습니다. 이미지의 지연 로딩은 이미지 로딩을 지연시킬 수 있습니다. 사용자가 표시 영역으로 스크롤할 때만 이미지가 로드되므로 페이지 로딩 시간이 줄어들고 사용자 경험이 향상될 수 있습니다. PHP 웹 페이지를 작성할 때 일부 기능을 작성하여 이미지의 지연 로딩 효과를 최적화할 수 있습니다. 아래 세부정보

PHP 함수를 통해 메모리 사용량을 줄이는 방법 개발 시 메모리 사용량은 매우 중요한 고려 사항입니다. 프로그램에서 많은 양의 메모리를 사용하면 속도가 느려지거나 프로그램이 충돌할 수도 있습니다. 따라서 메모리 사용량을 합리적으로 관리하고 줄이는 것은 모든 PHP 개발자가 주목해야 할 문제입니다. 이 기사에서는 PHP 함수를 통해 메모리 사용량을 줄이는 몇 가지 방법을 소개하고 독자의 참고를 위한 구체적인 코드 예제를 제공합니다. PHP에서 변수를 해제하려면 unset() 함수를 사용하십시오. 변수가 더 이상 필요하지 않으면 다음을 사용하십시오.

PHP 기능 소개—curl_multi_getcontent(): cURL 세션의 콘텐츠를 가져옵니다. PHP 개발에서는 네트워크를 통해 다른 서버로부터 데이터를 요청해야 하는 경우가 많습니다. 그리고 cURL(ClientURL)은 PHP에서 네트워크 통신에 사용되는 강력한 PHP 확장 라이브러리입니다. cURL은 일련의 함수를 제공하며 그 중 하나는 cURL 세션의 콘텐츠를 얻는 데 사용되는 cur_multi_getcontent()입니다.

PHPDeprecated: Functionereg_replace()isdeprecated-Solution PHP에서 개발할 때 일부 함수가 더 이상 사용되지 않는다고 선언되는 문제에 자주 직면합니다. 이는 최신 PHP 버전에서는 이러한 기능이 제거되거나 대체될 수 있음을 의미합니다. 일반적인 예 중 하나는 ereg_replace() 함수입니다. ereg_replace

PHP 이미지 처리 기능은 이미지를 처리하고 편집하는 데 특별히 사용되는 기능 세트로 개발자에게 풍부한 이미지 처리 기능을 제공합니다. 이러한 기능을 통해 개발자는 자르기, 크기 조정, 회전, 워터마크 추가 등의 작업을 구현하여 다양한 이미지 처리 요구 사항을 충족할 수 있습니다. 먼저 PHP 이미지 처리 기능을 사용하여 이미지 자르기 기능을 구현하는 방법을 소개하겠습니다. PHP는 이미지를 자르는 데 사용할 수 있는 imagecrop() 함수를 제공합니다. 자르기 영역의 좌표와 크기를 전달하여 이미지를 자를 수 있습니다.

PHP 함수 소개: strtr() 함수 PHP 프로그래밍에서 strtr() 함수는 매우 유용한 문자열 대체 함수입니다. 문자열의 지정된 문자나 문자열을 다른 문자나 문자열로 바꾸는 데 사용됩니다. 이 기사에서는 strtr() 함수의 사용법을 소개하고 몇 가지 구체적인 코드 예제를 제공합니다. strtr() 함수의 기본 구문은 다음과 같습니다. strtr(string$str, array$replace) 여기서 $str은 대체할 원래 단어입니다.

다양한 PHP 기능의 성능은 애플리케이션 효율성에 매우 중요합니다. 성능이 더 좋은 함수에는 echo 및 print가 포함되는 반면 str_replace, array_merge 및 file_get_contents와 같은 함수는 성능이 느립니다. 예를 들어, str_replace 함수는 문자열을 바꾸는 데 사용되며 보통의 성능을 갖는 반면 sprintf 함수는 문자열 형식을 지정하는 데 사용됩니다. 성능 분석에 따르면 하나의 예제를 실행하는 데 0.05밀리초밖에 걸리지 않아 함수가 잘 수행된다는 것을 증명합니다. 따라서 기능을 현명하게 사용하면 더 빠르고 효율적인 응용 프로그램을 만들 수 있습니다.

PHP 함수는 다른 언어의 함수와 유사하지만 몇 가지 고유한 기능도 있습니다. 구문적으로 PHP 함수는 function으로 선언되고, JavaScript는 function으로 선언되며, Python은 def로 선언됩니다. 매개변수와 반환값 측면에서 PHP 함수는 매개변수를 받아들이고 값을 반환합니다. JavaScript와 Python에도 비슷한 기능이 있지만 구문이 다릅니다. 범위 측면에서 PHP, JavaScript 및 Python의 함수는 모두 전역 또는 로컬 범위를 갖습니다. 전역 함수는 어디에서나 액세스할 수 있으며 로컬 함수는 선언 범위 내에서만 액세스할 수 있습니다.
