免费观看又色又爽又黄的小说免费_美女福利视频国产片_亚洲欧美精品_美国一级大黄大色毛片

小知識(shí)大學(xué)問(wèn)的注冊(cè)PHP函數(shù)

【相關(guān)學(xué)習(xí)推薦:php編程(視頻)】

創(chuàng)新互聯(lián)公司專(zhuān)業(yè)為企業(yè)提供扶溝網(wǎng)站建設(shè)、扶溝做網(wǎng)站、扶溝網(wǎng)站設(shè)計(jì)、扶溝網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、扶溝企業(yè)網(wǎng)站模板建站服務(wù),十載扶溝做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。PHP函數(shù)的注冊(cè)和使用

PHP擴(kuò)展的主要目標(biāo)是為用戶(hù)注冊(cè)新的PHP函數(shù),PHP函數(shù)非常復(fù)雜,很難完全理解它們與Zend引擎密切相關(guān)的機(jī)制,但幸運(yùn)的是, 我們?cè)诒菊轮胁恍枰@些知識(shí),因?yàn)镻HP擴(kuò)展機(jī)制提供了許多方法來(lái)抽象如此復(fù)雜的內(nèi)容。

在擴(kuò)展中注冊(cè)并使用一個(gè)新的PHP函數(shù)是一個(gè)簡(jiǎn)單的步驟. 然而,要深刻理解整體情況,則要復(fù)雜得多。zend_function章節(jié)的第一步 可能會(huì)有所幫助.

顯然,你需要掌握類(lèi)型, 特別是 zendValues 和 內(nèi)存管理. 當(dāng)然, 了解你的鉤子.

zend_function_entry 結(jié)構(gòu)

不要和 zend_function 結(jié)構(gòu)混淆,zend_function_entry是用在擴(kuò)展中針對(duì)引擎注冊(cè)函數(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;

你會(huì)發(fā)現(xiàn)該結(jié)構(gòu)并不復(fù)雜,這就是聲明和注冊(cè)新功能所需要的。讓我們一起詳細(xì)介紹:

函數(shù)的名字:fname。沒(méi)什么好補(bǔ)充的,你知道它的用途對(duì)吧?只需注意是 const char *類(lèi)型。這不適用于引擎。此 fname是一個(gè)模型,引擎將會(huì)從 內(nèi)部的 zend_string 創(chuàng)建。

然后來(lái)看 handler。這是指向 C 代碼的函數(shù)指針,它將會(huì)是函數(shù)的主體。這里,我們將使用宏來(lái)簡(jiǎn)化其聲明(等等會(huì)看到)。進(jìn)入此函數(shù),我們能夠解析函數(shù)接收的參數(shù),并且生成一個(gè)返回值,就像任何 PHP 用戶(hù)區(qū)的函數(shù)。注意,這個(gè)返回值作為參數(shù)傳遞到我們的處理程序。

爭(zhēng)論。arg_info變量是關(guān)于聲明我們的函數(shù)將接收的 API 參數(shù)。同樣,這部分可能很難深入理解,但我們不需要理解太深,我們?cè)俅问褂煤赀M(jìn)行抽象和簡(jiǎn)化參數(shù)聲明。你要知道的是,在這里你不需要聲明任何參數(shù)即可使用該函數(shù),但是我們強(qiáng)烈建議你這么做。我們將回到這里。參數(shù)是一組 arg_info,因此它的大小作為 num_args傳遞。

然后是 flags。在這章我們不詳細(xì)說(shuō)明它。這些是內(nèi)部使用的,你可在 zend_function 章節(jié)了解詳細(xì)信息。

注冊(cè) PHP 函數(shù)

當(dāng)加載擴(kuò)展時(shí),PHP 函數(shù)會(huì)被注冊(cè)到 ZEND 引擎當(dāng)中。一個(gè)擴(kuò)展可以在擴(kuò)展結(jié)構(gòu)中聲明一個(gè)函數(shù)向量。被擴(kuò)展聲明的函數(shù)被稱(chēng)為 核心函數(shù),與 用戶(hù)函數(shù)(在PHP用戶(hù)中被聲明和使用的函數(shù))相反,它們?cè)诋?dāng)前的請(qǐng)求結(jié)束時(shí)不會(huì)被注銷(xiāo):可以一直使用。

提醒一下,以下是為了方便可讀性對(duì) PHP 擴(kuò)展結(jié)構(gòu)的簡(jiǎn)寫(xiě)

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ù)向量傳遞一個(gè)已聲明的函數(shù)向量。讓我們一起來(lái)看一個(gè)簡(jiǎn)單的例子:

/* 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
};

我們來(lái)試試一個(gè)簡(jiǎn)單的函數(shù) fahrenheit_to_celsius()(名字告訴了我們它的作用)

通過(guò)使用 PHP_FUNCTION()宏來(lái)定義一個(gè)函數(shù)。后者將傳遞它的參數(shù)并擴(kuò)展成正確的結(jié)構(gòu)。然后,我們把函數(shù)符號(hào)匯總并將其添加到 pib_functions向量中。這就是通過(guò) zend_module_entry符號(hào)延伸的 zend_function_entry *類(lèi)型。在此向量中,我們通過(guò) PHP_FE宏添加我們的 PHP 函數(shù)。后者需要 PHP 函數(shù)名稱(chēng),以及我們傳遞 NULL 值時(shí)的一個(gè)參數(shù)向量。

php_pib.h頭文件中,我們應(yīng)該像 C 語(yǔ)言一樣在這里聲明我們的函數(shù):

/* pib.h 頭文件*/
PHP_FUNCTION(fahrenheit_to_celsius);

如你所見(jiàn),聲明函數(shù)確實(shí)很容易。宏為我們干完了所有難活。以下是和上文相同的代碼,但是卻擴(kuò)展了宏,因此你可以看下它們是如何運(yùn)行的:

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

請(qǐng)注意 PHP_FUNCTION()是如何以 zif_開(kāi)頭擴(kuò)展為 C 符號(hào)的。‘zif’被添加到你的函數(shù)名稱(chēng)中,以防止PHP 及其模塊在編譯中造成符號(hào)名稱(chēng)沖突。因此,我們的 fahrenheit_to_celsius()PHP 函數(shù)使用了 zif_fahrenheit_to_celsius()的處理程序。它幾乎和每個(gè) PHP 函數(shù)一樣。如果你搜索 zif_var_dump,就可以閱讀PHP var_dump()的源碼函數(shù)等。

聲明函數(shù)參數(shù)

到目前為止,如果 「你編譯」 擴(kuò)展并將其加載到PHP中,你可以看見(jiàn)函數(shù)呈現(xiàn)的反射機(jī)制:

> ~/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ā)布一個(gè) fahrenheit_to_celsius($fahrenheit)函數(shù)簽名,則需要一個(gè)強(qiáng)制參數(shù)。

你必須了解,函數(shù)聲明和函數(shù)內(nèi)部的運(yùn)行無(wú)關(guān)。這意味著即便沒(méi)有聲明參數(shù),我們現(xiàn)在編寫(xiě)函數(shù)也可能會(huì)起作用。

注意

聲明參數(shù)雖然不是強(qiáng)制性的,但是我們強(qiáng)烈推薦使用。反射 API 可通過(guò)使用參數(shù)獲取函數(shù)的信息。Zend 引擎也用到參數(shù),尤其是當(dāng)我們談及引用傳參或者返回引用的函數(shù)時(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;

沒(méi)必要詳細(xì)說(shuō)明每個(gè)字段,但是想要理解參數(shù)卻比這種單獨(dú)結(jié)構(gòu)復(fù)雜得多。幸運(yùn)的是,我們?cè)俅螢槟闾峁┝艘恍┖陙?lái)抽象這艱巨的工作。

ZEND_BEGIN_ARG_INFO_EX(arginfo_fahrenheit_to_celsius, 0, 0, 1)
    ZEND_ARG_INFO(0, fahrenheit)
ZEND_END_ARG_INFO()

上面的代碼詳細(xì)的說(shuō)明了如何創(chuàng)建參數(shù),但當(dāng)我們擴(kuò)展宏時(shí),我們會(huì)感到有些困難:

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 },
    };

正如我們所見(jiàn),宏創(chuàng)建了一個(gè) zend_internal_arg_info結(jié)構(gòu)。如果你閱讀過(guò)這類(lèi)宏的 API,那么對(duì)我們來(lái)說(shuō)一切都變得清楚了:

/* 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)

這一系列的宏可以讓你處理每個(gè)用例。
This bunch of macros allow you to deal with every use-case.

ZEND_BEGIN_ARG_INFO_EX()允許你聲明你的函數(shù)能接收多少個(gè)必要參數(shù)。它還允許你聲明一個(gè) &return_by_ref()函數(shù)。那么你每個(gè)參數(shù)都需要 ZEND_ARG_***_INFO()之一。使用它你可以判斷參數(shù)是否為 &$passed_by_ref以及是否需要類(lèi)型提示。

注意

如果你不知道怎樣去命名參數(shù)向量符號(hào),則一種做法是使用 ‘a(chǎn)rginfo_[function name]’模式。

所以回到我們的 fahrenheit_to_celsius()函數(shù),我們這里申明一個(gè)簡(jiǎn)單的按值返回函數(shù)(非常經(jīng)典的用例),其中一個(gè)參數(shù)稱(chēng)為 fahrenheit,且未通過(guò)引用傳遞(又一次的傳統(tǒng)用例)。

這就創(chuàng)建了類(lèi)型 zend_internal_arg_info[](一個(gè)向量, 或一個(gè)數(shù)組, 都相同) 的 arginfo_fahrenheit_to_celsius符號(hào),現(xiàn)在我們必須要使用該符號(hào)回到函數(shù)聲明中來(lái)添加給它一些參數(shù)。

PHP_FE(fahrenheit_to_celsius, arginfo_fahrenheit_to_celsius)

至此我們完成了,現(xiàn)在反射可以看見(jiàn)參數(shù)了,并會(huì)告知引擎在引用不匹配的情況下該怎么做。太棒了!

注意

還有其他宏。ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX()f.e. 你可以在 Zend/zend_api.h 的源代碼中找到所有這些文件。

C 語(yǔ)言的 PHP 函數(shù)結(jié)構(gòu)和 API

好的。下面是一個(gè) PHP 函數(shù)。你可以使用它,并用 PHP 語(yǔ)言聲明它(用戶(hù)區(qū)):

function fahrenheit_to_celsius($fahrenheit)
{
    return 5/9 * ($fahrenheit - 32);
}

這是一個(gè)簡(jiǎn)單的函數(shù),以便你可以理解它。這是用 C 編程時(shí)的樣子:

PHP_FUNCTION(fahrenheit_to_celsius)
{
    /* code to go here */
}

宏展開(kāi)后,將得到:

void zif_fahrenheit_to_celsius(zend_execute_data *execute_data, zval *return_value)
{
    /* code to go here */
}

休息一下,考慮一下主要差異。

首先奇怪的是,在 C 中,該函數(shù)不會(huì)返回任何東西。那是一個(gè) void聲明的函數(shù),你不可以在這里返回任何東西。但是我們注意到我們接收了一個(gè) zval *類(lèi)型的return_value參數(shù),看起來(lái)很不錯(cuò)。用 C 編寫(xiě) PHP 函數(shù)時(shí),你將得到一個(gè)指向 zval 的返回值 ,希望你們能玩一玩。這有更多關(guān)于 zval 的資源.

注意

在 C 擴(kuò)展中編寫(xiě) PHP 函數(shù)時(shí),你接收作為參數(shù)的返回值,并且你不會(huì)從 C 函數(shù)返回任何東西。

好的,第一點(diǎn)解釋了。第二點(diǎn)你可能已經(jīng)猜到了:PHP 函數(shù)的參數(shù)在哪里?$fahreinheit在哪里?很難解釋完全,事實(shí)上,這很難。

但是我們不需要在這里了解細(xì)節(jié)。讓我們解釋下關(guān)鍵的概念:

參數(shù)已經(jīng)通過(guò)引擎推入堆棧中。它們都在內(nèi)存的某個(gè)地方挨著堆放。如果你的函數(shù)被調(diào)用,這意味著沒(méi)有阻塞錯(cuò)誤,因此你可以瀏覽參數(shù)堆棧,并讀取運(yùn)行時(shí)傳遞的參數(shù)。不僅是你聲明的那些,還包括那些在調(diào)用函數(shù)時(shí)傳遞給函數(shù)的。引擎會(huì)為你處理一切。為了讀取參數(shù),你需要一個(gè)函數(shù)或者宏,并且需要知道有多少參數(shù)已經(jīng)推入堆棧中,以便知道什么時(shí)候應(yīng)該停止讀取它們。一切都按照你接收的作為參數(shù)的zend_execute_data *execute_data。但是現(xiàn)在我們不詳細(xì)說(shuō)明。解析參數(shù):zend_parse_parameters()

要讀取參數(shù),歡迎使用 zend_parse_parameters()API (稱(chēng)為 ‘zpp’).

注意

當(dāng)在 C 擴(kuò)展中編寫(xiě) PHP 函數(shù)時(shí),多虧了zend_parse_parameters()函數(shù)和它的朋友,你接收到 PHP 函數(shù)的參數(shù)。

zend_parse_parameters()是一個(gè)函數(shù),它將為你到 Zend 引擎的堆棧中讀取參數(shù)。你要告訴它要讀取多少個(gè)參數(shù),以及想要它為你提供哪種類(lèi)型。該函數(shù)將根據(jù) PHP 類(lèi)型轉(zhuǎn)換規(guī)則(如果需要,并且有可能的話(huà))將參數(shù)轉(zhuǎn)換為你要的類(lèi)型。如果你需要一個(gè)整型,但給了一個(gè)浮點(diǎn)型,如果沒(méi)有嚴(yán)格的類(lèi)型提示規(guī)則被阻塞,則引擎會(huì)將浮點(diǎn)型轉(zhuǎn)換為整型,然后給你。

讓我們來(lái)看看這個(gè)函數(shù):

PHP_FUNCTION(fahrenheit_to_celsius)
{
    double f;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &f) == FAILURE) {
        return;
    }

    /* continue */
}

我們希望在 f 變量上得到一個(gè) double 類(lèi)型。然后我們調(diào)用zend_parse_parameters()

第一個(gè)參數(shù)是運(yùn)行時(shí)已給定的參數(shù)數(shù)目。ZEND_NUM_ARGS()是一個(gè)宏,它會(huì)告訴我們,然后我們用它去告知 zpp() 需要讀取多少個(gè)參數(shù)。

然后我們傳遞一個(gè)const char *類(lèi)型的 “d”字符串。在這里,要求你為每一個(gè)接收的參數(shù)寫(xiě)一個(gè)字母,除了一些未在這里講述的特殊情況。一個(gè)簡(jiǎn)單的 “d”表示 “如果需要的話(huà),我想要第一個(gè)接收的參數(shù)轉(zhuǎn)換為 float (double)”

然后,在該字符串之后傳遞 C 真正需要的參數(shù),以滿(mǎn)足第二個(gè)參數(shù)。一個(gè) “d”表示 “一個(gè) double”,然后你現(xiàn)在傳遞 double 的 地址,引擎將會(huì)填充其值。

注意

你總是將一個(gè)指針傳遞給要填充的數(shù)據(jù)。

你可以在 PHP 源代碼的 README.PARAMETER_PARSING_API文件中找到關(guān)于 zpp() 的字符串格式的最新幫助。仔細(xì)閱讀,因?yàn)檫@是你可能搞錯(cuò)并造成程序崩潰的一步。始終檢查你的參數(shù),始終根據(jù)你提供的格式字符串傳遞相同數(shù)量的參數(shù)變量,以及你要求的類(lèi)型相同。要合乎邏輯。

同樣注意一下參數(shù)解析的正常過(guò)程。zend_parse_parameters()函數(shù)在成功時(shí)應(yīng)返回 SUCCESS或者在失敗時(shí)應(yīng)返回FAILURE。失敗可能表示你沒(méi)有使用ZEND_NUM_ARGS()值,而是手動(dòng)提供一個(gè)值(壞主意)。或者在參數(shù)解析時(shí)做錯(cuò)了什么。如果是這樣,那么是時(shí)候 return 了,終止當(dāng)前函數(shù)(你應(yīng)該從 C 函數(shù)中返回 void,所以只要 return)。

到目前為止,我們接收了一個(gè) double。讓我們執(zhí)行數(shù)學(xué)運(yùn)算并返回結(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 的工作原理,返回值對(duì)你來(lái)說(shuō)應(yīng)該很容易。你必須填寫(xiě) return_value

一些 RETURN_***()宏以及一些RETVAL_***()宏都是專(zhuān)門(mén)用來(lái)這么做的。這兩個(gè)僅設(shè)置return_valuezval 的類(lèi)型和值,但是RETURN_***()宏后面會(huì)跟著一個(gè)從當(dāng)前函數(shù)返回的 Creturn

或者,API 提供了一系列去處理和解析參數(shù)的宏。如果你對(duì) python 樣式說(shuō)明符困惑的話(huà),那么它更具有可讀性。

你需要使用以下宏來(lái)開(kāi)始和結(jié)束函數(shù)參數(shù)解析:

ZEND_PARSE_PARAMETERS_START(min_argument_count, max_argument_count) /* 需要兩個(gè)參數(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 "*" */

為了添加一個(gè)參數(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));
}
添加測(cè)試

如果你已閱讀有關(guān)測(cè)試的章節(jié)(看使用 .phpt 文件測(cè)試),現(xiàn)在你應(yīng)該編寫(xiě)一個(gè)簡(jiǎn)單的例子:

--TEST--
Test fahrenheit_to_celsius
--SKIPIF--
<?php if (!extension_loaded("pib")) print "skip"; ?>
--FILE--
<?php
printf("%.2f", fahrenheit_to_celsius(70));
?>
--EXPECTF--
21.11

并啟動(dòng)make test

玩轉(zhuǎn)常量

讓我們來(lái)看一個(gè)高級(jí)的例子。我們來(lái)添加相反的函數(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)在是一個(gè)更復(fù)雜的用例,在將它作為 C 擴(kuò)展實(shí)現(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;
    }
}

這個(gè)例子有助于我們介紹常量。

常量在擴(kuò)展中很容易管理,就像它們?cè)谟脩?hù)區(qū)一樣。常量通常是持久性的,意味著它們應(yīng)該在請(qǐng)求之間保持其值不變。如果你知道 PHP 的生命周期,則應(yīng)該猜到 MINIT()是向引擎注冊(cè)常量的正確階段。

在內(nèi)部,這有個(gè)常量,一個(gè)zend_constant結(jié)構(gòu):

typedef struct _zend_constant {
    zval value;
    zend_string *name;
    int flags;
    int module_number;
} zend_constant;

真的是一個(gè)簡(jiǎn)單的結(jié)構(gòu)(如果你深入了解常量是如何管理到引擎中,那可能會(huì)是一場(chǎng)噩夢(mèng))。你聲明了namevalue,一些flags(不是很多),并且module_number自動(dòng)設(shè)置為你的擴(kuò)展編號(hào)(不用注意它)。

要注冊(cè)常量,同樣的,這一點(diǎn)都不難,一堆宏可以幫你完成:

#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 常量值是一個(gè)很好的實(shí)踐。事情變得容易了,這就是我們做的。

根據(jù)你的常量類(lèi)型,你將使用 REGISTER_LONG_CONSTANT()REGISTER_DOUBLE_CONSTANT()等等。API 和宏位于 Zend/zend_constants.h中。

flag 在CONST_CS(case-sensitive constant 大小寫(xiě)敏感常量,我們想要的)和CONST_PERSISTENT(持久性常量,在請(qǐng)求中也是我們想要的)之間是混合的 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();

我們得到了一個(gè)必須的參數(shù),兩個(gè)中的一個(gè)。那就是我們聲明的。其默認(rèn)值不是一個(gè)參數(shù)聲明可以解決的,那將在一秒鐘內(nèi)完成。

然后我們將我們的新函數(shù)添加到函數(shù)注冊(cè)向量:

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。它不是一個(gè)很難的 API,你必須熟悉它。

我們使用 “d|l”作為 zend_parse_parameters()的參數(shù)。一個(gè) double、或(管道“|”)、一個(gè) long。注意,如果在運(yùn)行時(shí)不提供可選參數(shù)(提醒一下,ZEND_NUM_ARGS()是什么),則 &mode不會(huì)被 zpp() 觸及。這就是為什么我們提供了一個(gè)TEMP_CONVERTER_TO_CELSIUS默認(rèn)值給該變量。

然后我們使用 strpprintf()去構(gòu)建一個(gè) zend_string,并且使用 RETURN_STR()返回它到 return_valuezval。

注意

strpprintf()和它的朋友們?cè)诖蛴『瘮?shù)章節(jié)有解釋過(guò)。

使用 Hashtable (PHP 數(shù)組)

現(xiàn)在讓我們來(lái)玩一下PHP 數(shù)組并設(shè)計(jì):

function multiple_fahrenheit_to_celsius(array $temperatures)
{
    foreach ($temperatures as $temp) {
        $return[] = fahreinheit_to_celsius($temp);
    }

    return $return;
}

所以在 C 語(yǔ)言實(shí)現(xiàn)的時(shí)候,我們需要zend_parse_parameters()并請(qǐng)求一個(gè)數(shù)組,遍歷它,進(jìn)行數(shù)學(xué)運(yùn)算,并將結(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 語(yǔ)言那部分將更快,因?yàn)椴恍枰?C 循環(huán)中調(diào)用 PHP 函數(shù),但是一個(gè)靜態(tài)(可能由編輯器內(nèi)聯(lián)的)函數(shù),它的運(yùn)行速度快了幾個(gè)數(shù)量級(jí),并且運(yùn)行低級(jí) CPU 指令所需的時(shí)間也更少。這并不是說(shuō)這個(gè)小小的演示函數(shù)在代碼性能方面需要如此多的關(guān)注,只要記住為什么我們有時(shí)會(huì)使用 C 語(yǔ)言代替 PHP。

管理引用

現(xiàn)在讓我們開(kāi)始玩 PHP 引用。您已經(jīng)從 zval 章節(jié) 了解到引用是在引擎中使用的一種特殊技巧。作為提醒,引用(我們指的是&$php_reference)是分配給 zval的,存儲(chǔ)在 zval的容器中。

所以,只要記住引用是什么以及它們的設(shè)計(jì)目的,就不難將它們處理成 PHP 函數(shù)。

如果你的函數(shù)接受一個(gè)參數(shù)作為引用,你必須在參數(shù)簽名中聲明,并從你的 zend_parse_parameter()調(diào)用中傳遞一個(gè)引用。

讓我們像往常一樣,首先使用 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()宏告訴引擎必須通過(guò)引用傳遞參數(shù)。

然后,當(dāng)我們接收到參數(shù)時(shí),我們使用 z參數(shù)類(lèi)型,以告訴我們希望將它作為一個(gè) zval給出。當(dāng)我們向引擎提示它應(yīng)該向我們傳遞一個(gè)引用這一事實(shí)時(shí),我們將獲得對(duì)該 zval的引用,也就是它的類(lèi)型為is_reference時(shí),我們只需要解引用它(即獲取存儲(chǔ)到 zval中的 zval),并按原樣修改它,因?yàn)橐玫念A(yù)期行為是您必須修改引用所攜帶的值:

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)));
}

完成。

注意

默認(rèn) return_value值為 NULL。如果我們不碰它,函數(shù)將返回PHP的 NULL

想了解更多編程學(xué)習(xí),敬請(qǐng)關(guān)注php培訓(xùn)欄目!

本文名稱(chēng):小知識(shí)大學(xué)問(wèn)的注冊(cè)PHP函數(shù)
URL標(biāo)題:http://m.newbst.com/article44/cppehe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作做網(wǎng)站網(wǎng)站導(dǎo)航移動(dòng)網(wǎng)站建設(shè)靜態(tài)網(wǎng)站用戶(hù)體驗(yàn)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化