PHP函數(shù)的注冊和使用方法是什么?這個問題可能是我們?nèi)粘W(xué)習(xí)或工作經(jīng)常見到的。希望通過這個問題能讓你收獲頗深。下面是小編給大家?guī)淼膮⒖純?nèi)容,讓我們一起來看看吧!
創(chuàng)新互聯(lián)是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來公司不斷探索創(chuàng)新,始終堅持為客戶提供滿意周到的服務(wù),在本地打下了良好的口碑,在過去的10年時間我們累計服務(wù)了上千家以及全國政企客戶,如成都崗?fù)?/a>等企業(yè)單位,完善的項目管理流程,嚴(yán)格把控項目進度與質(zhì)量監(jiān)控加上過硬的技術(shù)實力獲得客戶的一致稱揚。
PHP擴展的主要目標(biāo)是為用戶注冊新的PHP函數(shù),PHP函數(shù)非常復(fù)雜,很難完全理解它們與Zend引擎密切相關(guān)的機制,但幸運的是, 我們在本章中不需要這些知識,因為PHP擴展機制提供了許多方法來抽象如此復(fù)雜的內(nèi)容。
在擴展中注冊并使用一個新的PHP函數(shù)是一個簡單的步驟. 然而,要深刻理解整體情況,則要復(fù)雜得多。zend_function章節(jié)的第一步 可能會有所幫助.
顯然,你需要掌握類型, 特別是 zendValues 和 內(nèi)存管理. 當(dāng)然, 了解你的鉤子.
不要和 zend_function 結(jié)構(gòu)混淆,zend_function_entry
是用在擴展中針對引擎注冊函數(shù)的??催@里:
#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;
你會發(fā)現(xiàn)該結(jié)構(gòu)并不復(fù)雜,這就是聲明和注冊新功能所需要的。讓我們一起詳細介紹:
函數(shù)的名字:fname
。沒什么好補充的,你知道它的用途對吧?只需注意是 const char *
類型。這不適用于引擎。此 fname
是一個模型,引擎將會從 內(nèi)部的 zend_string 創(chuàng)建。
然后來看 handler
。這是指向 C 代碼的函數(shù)指針,它將會是函數(shù)的主體。這里,我們將使用宏來簡化其聲明(等等會看到)。進入此函數(shù),我們能夠解析函數(shù)接收的參數(shù),并且生成一個返回值,就像任何 PHP 用戶區(qū)的函數(shù)。注意,這個返回值作為參數(shù)傳遞到我們的處理程序。
爭論。arg_info
變量是關(guān)于聲明我們的函數(shù)將接收的 API 參數(shù)。同樣,這部分可能很難深入理解,但我們不需要理解太深,我們再次使用宏進行抽象和簡化參數(shù)聲明。你要知道的是,在這里你不需要聲明任何參數(shù)即可使用該函數(shù),但是我們強烈建議你這么做。我們將回到這里。參數(shù)是一組 arg_info
,因此它的大小作為 num_args
傳遞。
然后是 flags
。在這章我們不詳細說明它。這些是內(nèi)部使用的,你可在 zend_function 章節(jié)了解詳細信息。
當(dāng)加載擴展時,PHP 函數(shù)會被注冊到 ZEND 引擎當(dāng)中。一個擴展可以在擴展結(jié)構(gòu)中聲明一個函數(shù)向量。被擴展聲明的函數(shù)被稱為 核心
函數(shù),與 用戶
函數(shù)(在PHP用戶中被聲明和使用的函數(shù))相反,它們在當(dāng)前的請求結(jié)束時不會被注銷:可以一直使用。
提醒一下,以下是為了方便可讀性對 PHP 擴展結(jié)構(gòu)的簡寫
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; /* 函數(shù)聲明向量 */ int (*module_startup_func)(INIT_FUNC_ARGS); int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* ... */ };
您將向函數(shù)向量傳遞一個已聲明的函數(shù)向量。讓我們一起來看一個簡單的例子:
/* 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 };
我們來試試一個簡單的函數(shù) fahrenheit_to_celsius()
(名字告訴了我們它的作用)
通過使用 PHP_FUNCTION()
宏來定義一個函數(shù)。后者將傳遞它的參數(shù)并擴展成正確的結(jié)構(gòu)。然后,我們把函數(shù)符號匯總并將其添加到 pib_functions
向量中。這就是通過 zend_module_entry
符號延伸的 zend_function_entry *
類型。在此向量中,我們通過 PHP_FE
宏添加我們的 PHP 函數(shù)。后者需要 PHP 函數(shù)名稱,以及我們傳遞 NULL 值時的一個參數(shù)向量。
在 php_pib.h 頭文件中,我們應(yīng)該像 C 語言一樣在這里聲明我們的函數(shù):
/* pib.h 頭文件*/ PHP_FUNCTION(fahrenheit_to_celsius);
如你所見,聲明函數(shù)確實很容易。宏為我們干完了所有難活。以下是和上文相同的代碼,但是卻擴展了宏,因此你可以看下它們是如何運行的:
/* 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’ 被添加到你的函數(shù)名稱中,以防止PHP 及其模塊在編譯中造成符號名稱沖突。因此,我們的 fahrenheit_to_celsius()
PHP 函數(shù)使用了 zif_fahrenheit_to_celsius()
的處理程序。它幾乎和每個 PHP 函數(shù)一樣。如果你搜索 zif_var_dump
,就可以閱讀PHP var_dump()
的源碼函數(shù)等。
到目前為止,如果 「你編譯」 擴展并將其加載到PHP中,你可以看見函數(shù)呈現(xiàn)的反射機制:
> ~/php/bin/php -dextension=pib.so --re pib Extension [ <persistent> extension #37 pib version 0.1 ] { - Functions { Function [ <internal:pib> function fahrenheit_to_celsius ] { } }
但是它缺少參數(shù)。如果我們發(fā)布一個 fahrenheit_to_celsius($fahrenheit)
函數(shù)簽名,則需要一個強制參數(shù)。
你必須了解,函數(shù)聲明和函數(shù)內(nèi)部的運行無關(guān)。這意味著即便沒有聲明參數(shù),我們現(xiàn)在編寫函數(shù)也可能會起作用。
注意
聲明參數(shù)雖然不是強制性的,但是我們強烈推薦使用。反射 API 可通過使用參數(shù)獲取函數(shù)的信息。Zend 引擎也用到參數(shù),尤其是當(dāng)我們談及引用傳參或者返回引用的函數(shù)時。
要聲明參數(shù),我們必須要熟悉 zend_internal_arg_info
結(jié)構(gòu):
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;
沒必要詳細說明每個字段,但是想要理解參數(shù)卻比這種單獨結(jié)構(gòu)復(fù)雜得多。幸運的是,我們再次為你提供了一些宏來抽象這艱巨的工作。
ZEND_BEGIN_ARG_INFO_EX(arginfo_fahrenheit_to_celsius, 0, 0, 1) ZEND_ARG_INFO(0, fahrenheit) ZEND_END_ARG_INFO()
上面的代碼詳細的說明了如何創(chuàng)建參數(shù),但當(dāng)我們擴展宏時,我們會感到有些困難:
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 }, };
正如我們所見,宏創(chuàng)建了一個 zend_internal_arg_info
結(jié)構(gòu)。如果你閱讀過這類宏的 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()
允許你聲明你的函數(shù)能接收多少個必要參數(shù)。它還允許你聲明一個 &return_by_ref() 函數(shù)。ZEND_ARG_***_INFO()
之一。使用它你可以判斷參數(shù)是否為 &$passed_by_ref 以及是否需要類型提示。注意
如果你不知道怎樣去命名參數(shù)向量符號,則一種做法是使用 ‘a(chǎn)rginfo_[function name]’ 模式。
所以回到我們的 fahrenheit_to_celsius()
函數(shù),我們這里申明一個簡單的按值返回函數(shù)(非常經(jīng)典的用例),其中一個參數(shù)稱為 fahrenheit
,且未通過引用傳遞(又一次的傳統(tǒng)用例)。
這就創(chuàng)建了類型 zend_internal_arg_info[]
(一個向量, 或一個數(shù)組, 都相同) 的 arginfo_fahrenheit_to_celsius
符號,現(xiàn)在我們必須要使用該符號回到函數(shù)聲明中來添加給它一些參數(shù)。
PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius)
至此我們完成了,現(xiàn)在反射可以看見參數(shù)了,并會告知引擎在引用不匹配的情況下該怎么做。太棒了!
注意
還有其他宏。
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX()
f.e. 你可以在 Zend/zend_api.h 的源代碼中找到所有這些文件。
好的。下面是一個 PHP 函數(shù)。你可以使用它,并用 PHP 語言聲明它(用戶區(qū)):
function fahrenheit_to_celsius($fahrenheit) { return 5/9 * ($fahrenheit - 32); }
這是一個簡單的函數(shù),以便你可以理解它。這是用 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 中,該函數(shù)不會返回任何東西。那是一個 void
聲明的函數(shù),你不可以在這里返回任何東西。但是我們注意到我們接收了一個 zval *
類型的return_value
參數(shù),看起來很不錯。用 C 編寫 PHP 函數(shù)時,你將得到一個指向 zval 的返回值 ,希望你們能玩一玩。這有更多關(guān)于 zval 的資源.
注意
在 C 擴展中編寫 PHP 函數(shù)時,你接收作為參數(shù)的返回值,并且你不會從 C 函數(shù)返回任何東西。
好的,第一點解釋了。第二點你可能已經(jīng)猜到了:PHP 函數(shù)的參數(shù)在哪里?$fahreinheit
在哪里?很難解釋完全,事實上,這很難。
但是我們不需要在這里了解細節(jié)。讓我們解釋下關(guān)鍵的概念:
zend_execute_data *execute_data
。但是現(xiàn)在我們不詳細說明。要讀取參數(shù),歡迎使用 zend_parse_parameters()
API (稱為 ‘zpp’).
注意
當(dāng)在 C 擴展中編寫 PHP 函數(shù)時,多虧了
zend_parse_parameters()
函數(shù)和它的朋友,你接收到 PHP 函數(shù)的參數(shù)。
zend_parse_parameters()
是一個函數(shù),它將為你到 Zend 引擎的堆棧中讀取參數(shù)。你要告訴它要讀取多少個參數(shù),以及想要它為你提供哪種類型。該函數(shù)將根據(jù) PHP 類型轉(zhuǎn)換規(guī)則(如果需要,并且有可能的話)將參數(shù)轉(zhuǎn)換為你要的類型。如果你需要一個整型,但給了一個浮點型,如果沒有嚴(yán)格的類型提示規(guī)則被阻塞,則引擎會將浮點型轉(zhuǎn)換為整型,然后給你。
讓我們來看看這個函數(shù):
PHP_FUNCTION(fahrenheit_to_celsius) { double f; if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &f) == FAILURE) { return; } /* continue */ }
我們希望在 f 變量上得到一個 double 類型。然后我們調(diào)用zend_parse_parameters()
。
第一個參數(shù)是運行時已給定的參數(shù)數(shù)目。ZEND_NUM_ARGS()
是一個宏,它會告訴我們,然后我們用它去告知 zpp() 需要讀取多少個參數(shù)。
然后我們傳遞一個const char *
類型的 “d” 字符串。在這里,要求你為每一個接收的參數(shù)寫一個字母,除了一些未在這里講述的特殊情況。一個簡單的 “d” 表示 “如果需要的話,我想要第一個接收的參數(shù)轉(zhuǎn)換為 float (double)”。
然后,在該字符串之后傳遞 C 真正需要的參數(shù),以滿足第二個參數(shù)。一個 “d” 表示 “一個 double”,然后你現(xiàn)在傳遞 double 的 地址,引擎將會填充其值。
注意
你總是將一個指針傳遞給要填充的數(shù)據(jù)。
你可以在 PHP 源代碼的 README.PARAMETER_PARSING_API文件中找到關(guān)于 zpp() 的字符串格式的最新幫助。仔細閱讀,因為這是你可能搞錯并造成程序崩潰的一步。始終檢查你的參數(shù),始終根據(jù)你提供的格式字符串傳遞相同數(shù)量的參數(shù)變量,以及你要求的類型相同。要合乎邏輯。
同樣注意一下參數(shù)解析的正常過程。zend_parse_parameters()
函數(shù)在成功時應(yīng)返回 SUCCESS
或者在失敗時應(yīng)返回FAILURE
。失敗可能表示你沒有使用ZEND_NUM_ARGS()
值,而是手動提供一個值(壞主意)?;蛘咴趨?shù)解析時做錯了什么。如果是這樣,那么是時候 return 了,終止當(dāng)前函數(shù)(你應(yīng)該從 C 函數(shù)中返回 void
,所以只要 return
)。
到目前為止,我們接收了一個 double。讓我們執(zhí)行數(shù)學(xué)運算并返回結(jié)果:
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 的工作原理,返回值對你來說應(yīng)該很容易。你必須填寫 return_value
。
一些 RETURN_***()
宏以及一些RETVAL_***()
宏都是專門用來這么做的。這兩個僅設(shè)置return_value
zval 的類型和值,但是RETURN_***()
宏后面會跟著一個從當(dāng)前函數(shù)返回的 Creturn
。
或者,API 提供了一系列去處理和解析參數(shù)的宏。如果你對 python 樣式說明符困惑的話,那么它更具有可讀性。
你需要使用以下宏來開始和結(jié)束函數(shù)參數(shù)解析:
ZEND_PARSE_PARAMETERS_START(min_argument_count, max_argument_count) /* 需要兩個參數(shù) */ /* 這里我們將使用參數(shù)列表 */ ZEND_PARSE_PARAMETERS_END();
可用的參數(shù)宏可以列出如下:
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 "*" */
為了添加一個參數(shù)作為可選參數(shù),我們使用以下宏:
Z_PARAM_OPTIONAL /* old "|" */
這是基于宏的參數(shù)解析樣式的示例:
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)); }
如果你已閱讀有關(guān)測試的章節(jié)(看使用 .phpt 文件測試),現(xiàn)在你應(yīng)該編寫一個簡單的例子:
--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
讓我們來看一個高級的例子。我們來添加相反的函數(shù):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 };
現(xiàn)在是一個更復(fù)雜的用例,在將它作為 C 擴展實現(xiàn)之前,在 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; } }
這個例子有助于我們介紹常量。
常量在擴展中很容易管理,就像它們在用戶區(qū)一樣。常量通常是持久性的,意味著它們應(yīng)該在請求之間保持其值不變。如果你知道 PHP 的生命周期,則應(yīng)該猜到 MINIT()
是向引擎注冊常量的正確階段。
在內(nèi)部,這有個常量,一個zend_constant
結(jié)構(gòu):
typedef struct _zend_constant { zval value; zend_string *name; int flags; int module_number; } zend_constant;
真的是一個簡單的結(jié)構(gòu)(如果你深入了解常量是如何管理到引擎中,那可能會是一場噩夢)。你聲明了name
,value
,一些flags
(不是很多),并且module_number
自動設(shè)置為你的擴展編號(不用注意它)。
要注冊常量,同樣的,這一點都不難,一堆宏可以幫你完成:
#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 常量值是一個很好的實踐。事情變得容易了,這就是我們做的。
根據(jù)你的常量類型,你將使用 REGISTER_LONG_CONSTANT()
、 REGISTER_DOUBLE_CONSTANT()
等等。API 和宏位于 Zend/zend_constants.h中。
flag 在CONST_CS
(case-sensitive constant 大小寫敏感常量,我們想要的)和CONST_PERSISTENT
(持久性常量,在請求中也是我們想要的)之間是混合的 OR 操作。
現(xiàn)在在 C 中的temperature_converter($temp, $type = TEMP_CONVERTER_TO_CELSIUS)
函數(shù):
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();
我們得到了一個必須的參數(shù),兩個中的一個。那就是我們聲明的。其默認(rèn)值不是一個參數(shù)聲明可以解決的,那將在一秒鐘內(nèi)完成。
然后我們將我們的新函數(shù)添加到函數(shù)注冊向量:
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 */ }
函數(shù)主體:
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()
的參數(shù)。一個 double、或(管道“|”)、一個 long。注意,如果在運行時不提供可選參數(shù)(提醒一下,ZEND_NUM_ARGS()
是什么),則 &mode
不會被 zpp() 觸及。這就是為什么我們提供了一個TEMP_CONVERTER_TO_CELSIUS
默認(rèn)值給該變量。
然后我們使用 strpprintf()
去構(gòu)建一個 zend_string,并且使用 RETURN_STR()
返回它到 return_value
zval。
注意
strpprintf()
和它的朋友們在打印函數(shù)章節(jié)有解釋過。
現(xiàn)在讓我們來玩一下PHP 數(shù)組并設(shè)計:
function multiple_fahrenheit_to_celsius(array $temperatures) { foreach ($temperatures as $temp) { $return[] = fahreinheit_to_celsius($temp); } return $return; }
所以在 C 語言實現(xiàn)的時候,我們需要zend_parse_parameters()
并請求一個數(shù)組,遍歷它,進行數(shù)學(xué)運算,并將結(jié)果作為數(shù)組添加到 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 章節(jié)
在這里,C 語言那部分將更快,因為不需要在 C 循環(huán)中調(diào)用 PHP 函數(shù),但是一個靜態(tài)(可能由編輯器內(nèi)聯(lián)的)函數(shù),它的運行速度快了幾個數(shù)量級,并且運行低級 CPU 指令所需的時間也更少。這并不是說這個小小的演示函數(shù)在代碼性能方面需要如此多的關(guān)注,只要記住為什么我們有時會使用 C 語言代替 PHP。
現(xiàn)在讓我們開始玩 PHP 引用。您已經(jīng)從 zval 章節(jié) 了解到引用是在引擎中使用的一種特殊技巧。作為提醒,引用(我們指的是&$php_reference
)是分配給 zval
的,存儲在 zval
的容器中。
所以,只要記住引用是什么以及它們的設(shè)計目的,就不難將它們處理成 PHP 函數(shù)。
如果你的函數(shù)接受一個參數(shù)作為引用,你必須在參數(shù)簽名中聲明,并從你的 zend_parse_parameter()
調(diào)用中傳遞一個引用。
讓我們像往常一樣,首先使用 PHP 示例:因此,現(xiàn)在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()
宏告訴引擎必須通過引用傳遞參數(shù)。
然后,當(dāng)我們接收到參數(shù)時,我們使用 z
參數(shù)類型,以告訴我們希望將它作為一個 zval
給出。當(dāng)我們向引擎提示它應(yīng)該向我們傳遞一個引用這一事實時,我們將獲得對該 zval
的引用,也就是它的類型為is_reference
時,我們只需要解引用它(即獲取存儲到 zval
中的 zval
),并按原樣修改它,因為引用的預(yù)期行為是您必須修改引用所攜帶的值:
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))); }
完成。
注意
默認(rèn)
return_value
值為NULL
。如果我們不碰它,函數(shù)將返回PHP的NULL
。
感謝各位的閱讀!看完上述內(nèi)容,你們對PHP函數(shù)的注冊和使用方法是什么大概了解了嗎?希望文章內(nèi)容對大家有所幫助。如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁題目:PHP函數(shù)的注冊和使用方法是什么
文章來源:http://jinyejixie.com/article22/poeojc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、移動網(wǎng)站建設(shè)、網(wǎng)站收錄、動態(tài)網(wǎng)站、品牌網(wǎng)站制作、網(wǎng)站設(shè)計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)