真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

PHP管理全局的方法

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

橋西網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),橋西網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為橋西超過(guò)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站建設(shè)要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的橋西做網(wǎng)站的公司定做!管理全局狀態(tài)

在命令式語(yǔ)言中總是需要一些全局空間。在編程 PHP 或擴(kuò)展時(shí),我們將明確區(qū)分我們所稱的請(qǐng)求綁定全局變量和真正的全局變量。

請(qǐng)求全局變量是處理請(qǐng)求過(guò)程中需要攜帶和記憶信息的全局變量。一個(gè)簡(jiǎn)單的例子是,您要求用戶在函數(shù)參數(shù)中提供一個(gè)值,并且希望能夠在其他函數(shù)中使用它。除了這條信息在幾個(gè) PHP 函數(shù)調(diào)用中 “保持其值” 之外,它只為當(dāng)前請(qǐng)求保留該值。下一個(gè)來(lái)的請(qǐng)求應(yīng)該什么都不知道。PHP 提供了一種機(jī)制來(lái)管理請(qǐng)求全局變量,不管選擇了什么樣的多處理模型,我們將在本章后面詳細(xì)介紹這一點(diǎn)。

真正的全局變量是跨請(qǐng)求保留的信息片段。這些信息通常是只讀的。如果您需要寫(xiě)入這樣的全局變量作為請(qǐng)求處理的一部分,那么 PHP 無(wú)法幫助您。如果您使用 線程作為多處理模型, 您需要自己執(zhí)行內(nèi)存鎖。如果你使用 進(jìn)程作為多處理模型, 您需要使用自己的IPC(進(jìn)程間通信)。但是,在PHP擴(kuò)展編程中不應(yīng)該出現(xiàn)這種情況。

管理請(qǐng)求全局變量

下面是一個(gè)使用請(qǐng)求全局的簡(jiǎn)單擴(kuò)展例子:

/* 真正的 C 全局 */
static zend_long rnd = 0;

static void pib_rnd_init(void)
{
    /* 在 0 到 100 之間隨機(jī)一個(gè)數(shù)字 */
    php_random_int(0, 100, &rnd, 0);
}

PHP_RINIT_FUNCTION(pib)
{
    pib_rnd_init();

    return SUCCESS;
}

PHP_FUNCTION(pib_guess)
{
    zend_long r;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &r) == FAILURE) {
        return;
    }

    if (r == rnd) {
        /* 將數(shù)字重置以進(jìn)行猜測(cè) */
        pib_rnd_init();
        RETURN_TRUE;
    }

    if (r < rnd) {
        RETURN_STRING("more");
    }

    RETURN_STRING("less");
}

PHP_FUNCTION(pib_reset)
{
    if (zend_parse_parameters_none() == FAILURE) {
        return;
    }

    pib_rnd_init();
}

如你所見(jiàn),這個(gè)擴(kuò)展在請(qǐng)求開(kāi)始時(shí)挑選一個(gè)隨機(jī)整型數(shù),之后通過(guò)pib_guess()可以嘗試猜到這個(gè)數(shù)組。一旦猜到,該數(shù)字將重置。如果用戶想要手動(dòng)重置數(shù)字,它也可以自己手動(dòng)調(diào)用pib_reset()去重置數(shù)值。
該隨機(jī)數(shù)作為一個(gè) C 全局變量實(shí)現(xiàn)。如果 PHP 在進(jìn)程中作為多進(jìn)程模型的一部分使用不再是個(gè)問(wèn)題,如果之后使用線程,這是不行的。

注意

作為提醒,你無(wú)需掌握將要使用哪種多進(jìn)程模型。當(dāng)你設(shè)計(jì)擴(kuò)展時(shí),你必須為這兩種模型做好準(zhǔn)備。

當(dāng)使用線程,會(huì)針對(duì)服務(wù)器中的每個(gè)線程共享一個(gè) C 全局變量。例如我們上面的例子,網(wǎng)絡(luò)服務(wù)器的每個(gè)并行用戶將共享同一個(gè)數(shù)值。一些可能會(huì)一開(kāi)始就重置數(shù)值,而其他則嘗試去猜測(cè)它。簡(jiǎn)而言之,你清楚地了解了線程的關(guān)鍵問(wèn)題。

我們必須持久化數(shù)據(jù)到同一請(qǐng)求,即使運(yùn)行 PHP 多進(jìn)程模型會(huì)利用線程,也必須讓它綁定到當(dāng)前請(qǐng)求中。

使用 TSRM 宏來(lái)保護(hù)全局空間

PHP 設(shè)計(jì)了可以幫助擴(kuò)展和內(nèi)核開(kāi)發(fā)人員處理全局請(qǐng)求的層。該層稱為T(mén)SRM (線程安全資源管理) ,并且作為一組宏公開(kāi),你必須在任何需要訪問(wèn)請(qǐng)求綁定全局(讀和寫(xiě))的時(shí)候使用該宏。

在多進(jìn)程模型使用流程的情況下,在后臺(tái),這些宏將解析為類似我們上面顯示的代碼。如我們所見(jiàn),如果不適用線程,上面的代碼是完全有效的。所以,當(dāng)使用進(jìn)程時(shí),這些宏將被擴(kuò)展為類似的宏。

首先你要要做的就是聲明一個(gè)結(jié)構(gòu),它將是你所有全局變量的根:

ZEND_BEGIN_MODULE_GLOBALS(pib)
    zend_long rnd;
ZEND_END_MODULE_GLOBALS(pib)

/* 解析為 :
*
* typedef struct _zend_pib_globals {
*    zend_long rnd;
* } zend_pib_globals;
*/

然后,創(chuàng)建一個(gè)這樣的全局變量:

ZEND_DECLARE_MODULE_GLOBALS(pib)

/* 解析為 zend_pib_globals pib_globals; */

現(xiàn)在,你可以使用全局宏訪問(wèn)器訪問(wèn)數(shù)據(jù)。這個(gè)宏是由框架創(chuàng)建的,它應(yīng)該在你的 php_pib.h頭文件定義。這看起來(lái)是這樣的:

#ifdef ZTS
#define PIB_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(pib, v)
#else
#define PIB_G(v) (pib_globals.v)
#endif

如你所見(jiàn),如果沒(méi)有啟用 ZTS 模式,即編譯非線程安全的 PHP 和擴(kuò)展(我們稱之為 NTS模式:非線程安全),宏只是解析到結(jié)構(gòu)中聲明的數(shù)據(jù)。因此,有以下變化:

static void pib_rnd_init(void)
{
    php_random_int(0, 100, &PIB_G(rnd), 0);
}

PHP_FUNCTION(pib_guess)
{
    zend_long r;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &r) == FAILURE) {
        return;
    }

    if (r == PIB_G(rnd)) {
        pib_rnd_init();
        RETURN_TRUE;
    }

    if (r < PIB_G(rnd)) {
        RETURN_STRING("more");
    }

    RETURN_STRING("less");
}

注意

當(dāng)使用一個(gè)進(jìn)程模型,TSRM宏解析為對(duì) C 全局變量的訪問(wèn)。

當(dāng)使用線程時(shí),即當(dāng)你編譯 ZTS PHP,事情變得更復(fù)雜。然后,我們看到的所有宏都解析為一些完全不同的東西,在這里很難解釋?;旧?,當(dāng)使用 ZTS 編譯時(shí),TSRM使用 TLS(線程本地存儲(chǔ))執(zhí)行了一項(xiàng)艱難的工作。

注意

簡(jiǎn)而言之,當(dāng)在 ZTS 編譯時(shí),全局變量將綁定到當(dāng)前線程。而在 NTS 編譯時(shí),全局變量將綁定到當(dāng)前進(jìn)程上。TSRM 宏處理這項(xiàng)艱難的工作。你可能對(duì)運(yùn)作方式感興趣,瀏覽 PHP 源代碼的/TSRM 目錄了解更多關(guān)于 PHP 線程安全。

在擴(kuò)展中使用全局鉤子

有時(shí),可能需要將全局變量初始化為一些默認(rèn)值,通常為零。引擎幫助下的TSRM系統(tǒng)提供了一個(gè)鉤子來(lái)為您的全局變量提供默認(rèn)值,我們稱之為GINIT。

注意

關(guān)于 PHP 掛鉤的完整信息,請(qǐng)參考 PHP 生命周期章節(jié)。

讓我們將隨機(jī)值設(shè)為零:

PHP_GSHUTDOWN_FUNCTION(pib)
{ }

PHP_GINIT_FUNCTION(pib)
{
    pib_globals->rnd = 0;
}

zend_module_entry pib_module_entry = {
    STANDARD_MODULE_HEADER,
    "pib",
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    "0.1",
    PHP_MODULE_GLOBALS(pib),
    PHP_GINIT(pib),
    PHP_GSHUTDOWN(pib),
    NULL, /* PRSHUTDOWN() */
    STANDARD_MODULE_PROPERTIES_EX
};

我們選擇僅顯示 zend_module_entry(和其他 NULL)的相關(guān)部分。如你所見(jiàn),全局管理掛鉤發(fā)生在結(jié)構(gòu)的中間。首先是PHP_MODULE_GLOBALS()來(lái)確定全局變量的大小,然后是我們的 GINITGSHUTDOWN鉤子。然后我們使用了STANDARD_MODULE_PROPERTIES_EX關(guān)閉結(jié)構(gòu),而不是STANDARD_MODULE_PROPERTIES。只需以正確的方式完成結(jié)構(gòu)即可,請(qǐng)參閱?:

#define STANDARD_MODULE_PROPERTIES
    NO_MODULE_GLOBALS, NULL, STANDARD_MODULE_PROPERTIES_EX

GINIT函數(shù)中,你傳遞了一個(gè)指向全局變量當(dāng)前存儲(chǔ)位置的指針。你可以使用它來(lái)初始化全局變量。在這里,我們將零放入隨機(jī)值(雖然不是很有用,但我們接受它)。

警告

不要在 GINIT 中使用PIB_G()宏。使用你得到的指針。

注意

對(duì)于當(dāng)前進(jìn)程,在MINIT()之前啟動(dòng)了GINIT()。如果是 NTS,就這樣而已。 如果是 ZTS,線程庫(kù)產(chǎn)生的每個(gè)新線程都會(huì)額外調(diào)用GINIT()

警告

GINIT()不作為RINIT()的一部分被調(diào)用。如果你需要在每次新請(qǐng)求時(shí)清除全局變量,則需要像在本章所示示例中所做的那樣手動(dòng)進(jìn)行。

完整的例子

這是一個(gè)更高級(jí)的完整示例。如果玩家獲勝,則將其得分(嘗試次數(shù))添加到可以從用戶區(qū)獲取的得分?jǐn)?shù)組中。沒(méi)什么難的,得分?jǐn)?shù)組在請(qǐng)求啟動(dòng)時(shí)初始化,然后在玩家獲勝時(shí)使用,并在當(dāng)前請(qǐng)求結(jié)束時(shí)清除:

ZEND_BEGIN_MODULE_GLOBALS(pib)
    zend_long rnd;
    zend_ulong cur_score;
    zval scores;
ZEND_END_MODULE_GLOBALS(pib)

ZEND_DECLARE_MODULE_GLOBALS(pib)

static void pib_rnd_init(void)
{
    /* 重置當(dāng)前分?jǐn)?shù) */
    PIB_G(cur_score) = 0;
    php_random_int(0, 100, &PIB_G(rnd), 0);
}

PHP_GINIT_FUNCTION(pib)
{
    /* ZEND_SECURE_ZERO 是 memset(0)。也可以解析為 bzero() */
    ZEND_SECURE_ZERO(pib_globals, sizeof(*pib_globals));
}

ZEND_BEGIN_ARG_INFO_EX(arginfo_guess, 0, 0, 1)
    ZEND_ARG_INFO(0, num)
ZEND_END_ARG_INFO()

PHP_RINIT_FUNCTION(pib)
{
    array_init(&PIB_G(scores));
    pib_rnd_init();

    return SUCCESS;
}

PHP_RSHUTDOWN_FUNCTION(pib)
{
    zval_dtor(&PIB_G(scores));

    return SUCCESS;
}

PHP_FUNCTION(pib_guess)
{
    zend_long r;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &r) == FAILURE) {
        return;
    }

    if (r == PIB_G(rnd)) {
        add_next_index_long(&PIB_G(scores), PIB_G(cur_score));
        pib_rnd_init();
        RETURN_TRUE;
    }

    PIB_G(cur_score)++;

    if (r < PIB_G(rnd)) {
        RETURN_STRING("more");
    }

    RETURN_STRING("less");
}

PHP_FUNCTION(pib_get_scores)
{
    if (zend_parse_parameters_none() == FAILURE) {
        return;
    }

    RETVAL_ZVAL(&PIB_G(scores), 1, 0);
}

PHP_FUNCTION(pib_reset)
{
    if (zend_parse_parameters_none() == FAILURE) {
        return;
    }

    pib_rnd_init();
}

static const zend_function_entry func[] = {
    PHP_FE(pib_reset, NULL)
    PHP_FE(pib_get_scores, NULL)
    PHP_FE(pib_guess, arginfo_guess)
    PHP_FE_END
};

zend_module_entry pib_module_entry = {
    STANDARD_MODULE_HEADER,
    "pib",
    func, /* 函數(shù)入口 */
    NULL, /* 模塊初始化 */
    NULL, /* 模塊關(guān)閉 */
    PHP_RINIT(pib), /* 請(qǐng)求初始化 */
    PHP_RSHUTDOWN(pib), /* 請(qǐng)求關(guān)閉 */
    NULL, /* 模塊信息 */
    "0.1", /* 替換為擴(kuò)展的版本號(hào) */
    PHP_MODULE_GLOBALS(pib),
    PHP_GINIT(pib),
    NULL,
    NULL,
    STANDARD_MODULE_PROPERTIES_EX
};

這里必須要注意的是,如果你希望在請(qǐng)求之間保持分?jǐn)?shù),PHP 不提供任何便利。而是需要一個(gè)持久的共享存儲(chǔ),例如文件,數(shù)據(jù)庫(kù),某些內(nèi)存區(qū)域等。PHP 的設(shè)計(jì)目的不是將信息持久存儲(chǔ)在其內(nèi)部的請(qǐng)求,因此它不提供這么做,但它提供了實(shí)用程序來(lái)訪問(wèn)請(qǐng)求綁定的全局空間,如我們所示。

然后,很容易地在RINIT()中初始化一個(gè)數(shù)組,然后在RSHUTDOWN()中銷毀它。請(qǐng)記住,array_init創(chuàng)建一個(gè)zend_array 并放入一個(gè) zval。但這是免分配的,不要擔(dān)心分配用戶無(wú)法使用的數(shù)組(因此浪費(fèi)分配),array_init()非常廉價(jià) (閱讀源代碼)。

當(dāng)我們將這樣的數(shù)組返回給用戶時(shí),我們不會(huì)忘記增加其引用計(jì)數(shù)(在 RETVAL_ZVAL中),因?yàn)槲覀冊(cè)跀U(kuò)展中保留了對(duì)此類數(shù)組的引用。

使用真實(shí)的全局變量

真實(shí)全局變量是非線程保護(hù)的真實(shí)C全局變量。有時(shí)可能會(huì)需要它們。但是請(qǐng)記住主要規(guī)則:在處理請(qǐng)求時(shí),不能安全地寫(xiě)入此類全局變量。因此,通常在 PHP 中,我們需要此類變量并將其用作只讀變量。

請(qǐng)記住,在 PHP 生命周期的MINIT()MSHUTDOWN()步驟中編寫(xiě)真實(shí)全局變量是絕對(duì)安全的。但是不能在處理請(qǐng)求時(shí)給他們寫(xiě)入值(但可以從他們那里讀取)。

因此,一個(gè)簡(jiǎn)單的示例是你想要讀取環(huán)境值以對(duì)其進(jìn)行處理。此外,初始化持久性的 zend_string并在之后處理某些請(qǐng)求時(shí)加以利用是很常見(jiàn)的。

這是介紹真實(shí)全局變量的修補(bǔ)示例,我們僅顯示與先前代碼的差異,而不顯示完整代碼:

static zend_string *more, *less;
static zend_ulong max = 100;

static void register_persistent_string(char *str, zend_string **result)
{
    *result = zend_string_init(str, strlen(str), 1);
    zend_string_hash_val(*result);

    GC_FLAGS(*result) |= IS_INTERNED;
}

static void pib_rnd_init(void)
{
    /* 重置當(dāng)前分?jǐn)?shù) */
    PIB_G(cur_score) = 0;
    php_random_int(0, max, &PIB_G(rnd), 0);
}

PHP_MINIT_FUNCTION(pib)
{
    char *pib_max;

    register_persistent_string("more", &more);
    register_persistent_string("less", &less);

    if (pib_max = getenv("PIB_RAND_MAX")) {
        if (!strchr(pib_max, '-')) {
            max = ZEND_STRTOUL(pib_max, NULL, 10);
        }
    }

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(pib)
{
    zend_string_release(more);
    zend_string_release(less);

    return SUCCESS;
}

PHP_FUNCTION(pib_guess)
{
    zend_long r;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &r) == FAILURE) {
        return;
    }

    if (r == PIB_G(rnd)) {
        add_next_index_long(&PIB_G(scores), PIB_G(cur_score));
        pib_rnd_init();
        RETURN_TRUE;
    }

    PIB_G(cur_score)++;

    if (r < PIB_G(rnd)) {
        RETURN_STR(more);
    }

    RETURN_STR(less);
}

在這里我們創(chuàng)建了兩個(gè) zend_string 變量 moreless。這些字符串不需要像以前一樣在使用時(shí)立即創(chuàng)建和銷毀。這些是不可變的字符串,只要保持不變,就可以分配一次并在需要的任何時(shí)間重復(fù)使用(即只讀)。我們?cè)?code>zend_string_init()中使用持久分配,在MINIT()中初始化這兩個(gè)字符串,我們現(xiàn)在預(yù)先計(jì)算其哈希值(而不是先執(zhí)行第一個(gè)請(qǐng)求),并且我們告訴 zval 垃圾收集器,這些字符串已被扣留,因此它將永遠(yuǎn)不會(huì)嘗試銷毀它們(但是,如果將它們用作寫(xiě)操作(例如連接)的一部分,則可能需要復(fù)制它們)。顯然我們不會(huì)忘記在MSHUTDOWN()中銷毀這些字符串。

然后在MINIT()中我們探查一個(gè)PIB_RAND_MAX環(huán)境,并將其用作隨機(jī)數(shù)選擇的范圍值。由于我們使用無(wú)符號(hào)整數(shù),并且我們知道strtoull()不會(huì)抱怨負(fù)數(shù)(因此將整數(shù)范圍包裹為符號(hào)不匹配),我們只是避免使用負(fù)數(shù)(經(jīng)典的libc解決方法)。

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


網(wǎng)站題目:PHP管理全局的方法
當(dāng)前路徑:http://weahome.cn/article/cpsgcp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部