PHP 管理全局的方法-php教程

资源魔 29 0

【相干学习保举:php编程(视频)】

治理全局状态

正在饬令式言语中老是需求一些全局空间。正在编程 PHP 或扩大时,咱们将明白区别咱们所称的申请绑定全局变量以及真实的全局变量。

申请全局变量是解决申请进程中需求携带以及影象信息的全局变量。一个简略的例子是,您要求用户正在函数参数中提供一个值,而且心愿可以正在其余函数中应用它。除了了这条信息正在几个 PHP 函数挪用中 “放弃其值” 以外,它只为以后申请保存该值。下一个来的申请应该甚么都没有晓得。PHP 提供了一种机制来治理申请全局变量,不论抉择了甚么样的多解决模子,咱们将正在本章前面具体引见这一点。

真实的全局变量是跨申请保存的信息片断。这些信息一般为只读的。假如您需求写入这样的全局变量作为申请解决的一局部,那末 PHP 无奈协助您。假如您应用 线程作为多解决模子, 您需求本人执行内存锁。假如你应用 过程作为多解决模子, 您需求应用本人的IPC(过程间通讯)。然而,正在PHP扩大编程中不该该呈现这类状况。

治理申请全局变量

上面是一个应用申请全局的简略扩大例子:

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

static void pib_rnd_init(void)
{
    /* 正在 0 到 100 之间随机一个数字 */
    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) {
        /* 将数字重置以进行猜想 */
        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();
}

如你所见,这个扩大正在申请开端时筛选一个随机整型数,之后经过pib_guess()能够测验考试猜到这个数组。一旦猜到,该数字将重置。假如用户想要手动重置数字,它也能够本人手动挪用pib_reset() 去重置数值。
该随机数作为一个 C 全局变量完成。假如 PHP 正在过程中作为多过程模子的一局部应用再也不是个成绩,假如之后应用线程,这是没有行的

留意

作为提示,你无需把握将要应用哪一种多过程模子。当你设计扩大时,你必需为这两种模子做好预备。

当应用线程,会针对效劳器中的每一个线程同享一个 C 全局变量。例如咱们下面的例子,网络效劳器的每一个并行用户将同享同一个数值。一些可能会一开端就重置数值,而其余则测验考试去猜想它。简而言之,你分明地理解了线程的要害成绩。

咱们必需耐久化数据到同一申请,即便运转 PHP 多过程模子会行使线程,也必需让它绑定到以后申请中。

应用 TSRM 宏来维护全局空间

PHP 设计了能够协助扩大以及内核开发职员解决全局申请的层。该层称为TSRM (线程平安资本治理) ,而且作为一组宏地下,你必需正在任何需求拜访申请绑定全局(读以及写)的时分应用该宏。

正在多过程模子应用流程的状况下,正在后盾,这些宏将解析为相似咱们下面显示的代码。如咱们所见,假如没有实用线程,下面的代码是齐全无效的。以是,当应用过程时,这些宏将被扩大为相似的宏。

起首你要要做的就是申明一个构造,它将是你一切全局变量的根:

ZEND_BEGIN_MODULE_GLOBALS(pib)
    zend_long rnd;
ZEND_END_MODULE_GLOBALS(pib)

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

而后,创立一个这样的全局变量:

ZEND_DECLARE_MODULE_GLOBALS(pib)

/* 解析为 zend_pib_globals pib_globals; */

如今,你能够应用全局宏拜访器拜访数据。这个宏是由框架创立的,它应该正在你的 php_pib.h 头文件界说。这看起来是这样的:

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

如你所见,假如不启用 ZTS 模式,即编译非线程平安的 PHP 以及扩大(咱们称之为 NTS模式:非线程平安),宏只是解析到构造中申明的数据。因而,有如下变动:

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

留意

当应用一个过程模子,TSRM 宏解析为对 C 全局变量的拜访。

当应用线程时,即当你编译 ZTS PHP,事件变患上更复杂。而后,咱们看到的一切宏都解析为一些齐全没有同的货色,正在这里很难诠释。根本上,当应用 ZTS 编译时,TSRM 应用 TLS(线程内陆存储)执行了一项困难的工作。

留意

简而言之,当正在 ZTS 编译时,全局变量将绑定到以后线程。而正在 NTS 编译时,全局变量将绑定到以后过程上。TSRM 宏解决这项困难的工作。你可能对运作形式感兴味,阅读 PHP 源代码的/TSRM 目次理解更多对于 PHP 线程平安。

正在扩大中应用全局钩子

有时,可能需求将全局变量初始化为一些默许值,一般是零。引擎协助下的TSRM零碎提供了一个钩子来为您的全局变量提供默许值,咱们称之为GINIT

留意

对于 PHP 挂钩的完好信息,请参考 PHP 生命周期章节。

让咱们将随机值设为零:

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)的相干局部。如你所见,全局治理挂钩发作正在构造的两头。起首是PHP_MODULE_GLOBALS()来确定全局变量的巨细,而后是咱们的 GINIT以及 GSHUTDOWN钩子。而后咱们应用了STANDARD_MODULE_PROPERTIES_EX封闭构造,而没有是STANDARD_MODULE_PROPERTIES。只要以正确的形式实现构造便可,请参阅?:

#define STANDARD_MODULE_PROPERTIES
    NO_MODULE_GLOBALS, NULL, STANDARD_MODULE_PROPERTIES_EX

正在GINIT 函数中,你通报了一个指向全局变量以后存储地位的指针。你能够应用它来初始化全局变量。正在这里,咱们将零放入随机值(尽管没有是颇有用,但咱们承受它)。

正告

没有要正在 GINIT 中应用PIB_G()宏。应用你失去的指针。

留意

关于以后过程,正在MINIT()以前启动了GINIT()。假如是 NTS,就这样罢了。 假如是 ZTS,线程库孕育发生的每一个新线程城市额定挪用GINIT()

正告

GINIT()没有作为RINIT()的一局部被挪用。假如你需求正在每一次新申请时肃清全局变量,则需求像正在本章所示示例中所做的那样手动进行。

完好的例子

这是一个更初级的完好示例。假如玩家获胜,则将其患上分(测验考试次数)增加到能够从用户区猎取的患上分数组中。没甚么难的,患上分数组正在申请启动时初始化,而后正在玩家获胜时应用,并正在以后申请完结时肃清:

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)
{
    /* 重置以后分数 */
    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, /* 函数入口 */
    NULL, /* 模块初始化 */
    NULL, /* 模块封闭 */
    PHP_RINIT(pib), /* 申请初始化 */
    PHP_RSHUTDOWN(pib), /* 申请封闭 */
    NULL, /* 模块信息 */
    "0.1", /* 交换为扩大的版本号 */
    PHP_MODULE_GLOBALS(pib),
    PHP_GINIT(pib),
    NULL,
    NULL,
    STANDARD_MODULE_PROPERTIES_EX
};

这里必需要留意的是,假如你心愿正在申请之间放弃分数,PHP 没有提供任何便当。而是需求一个耐久的同享存储,例如文件,数据库,某些内存区域等。PHP 的设计目的没有是将信息耐久存储正在其外部的申请,因而它没有提供这么做,但它提供了适用顺序来拜访申请绑定的全局空间,如咱们所示。

而后,很容易地正在RINIT()中初始化一个数组,而后正在RSHUTDOWN()中销毁它。请记住,array_init创立一个zend_array 并放入一个 zval。但这是免调配的,没有要担忧调配用户无奈应用的数组(因而糜费调配),array_init()十分便宜 (浏览源代码)。

当咱们将这样的数组前往给用户时,咱们没有会遗记添加其援用计数(正在 RETVAL_ZVAL中),由于咱们正在扩大中保存了对此类数组的援用。

应用实在的全局变量

实在全局变量长短线程维护的实在C全局变量。有时可能会需求它们。然而请记住次要规定:正在解决申请时,不克不及平安地写入此类全局变量。因而,通常正在 PHP 中,咱们需求此类变量并将其用作只读变量。

请记住,正在 PHP 生命周期的MINIT()MSHUTDOWN()步骤中编写实在全局变量是相对平安的。然而不克不及正在解决申请时给他们写入值(但能够从他们哪里读取)。

因而,一个简略的示例是你想要读取环境值以对其进行解决。别的,初始化耐久性的 zend_string并正在之后解决某些申请时加以行使是很常见的。

这是引见实在全局变量的修补示例,咱们仅显示与先前代码的差别,而没有显示完好代码:

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)
{
    /* 重置以后分数 */
    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);
}

正在这里咱们创立了两个 zend_string 变量 more 以及 less。这些字符串没有需求像之前同样正在应用时立刻创立以及销毁。这些是不成变的字符串,只需放弃没有变,就能够调配一次并正在需求的任什么时候间反复应用(即只读)。咱们正在zend_string_init()中应用耐久调配,正在MINIT()中初始化这两个字符串,咱们如今事后较量争论其哈希值(而没有是先执行第一个申请),而且咱们通知 zval 渣滓搜集器,这些字符串已被拘留,因而它将永远没有会测验考试销毁它们(然而,假如将它们用作写操作(例如衔接)的一局部,则可能需求复制它们)。显然咱们没有会遗记正在MSHUTDOWN()中销毁这些字符串。

而后正在MINIT()中咱们探查一个PIB_RAND_MAX环境,并将其用作随机数抉择的最年夜范畴值。因为咱们应用无符号整数,而且咱们晓得strtoull()没有会埋怨正数(因而将整数范畴包裹为符号没有婚配),咱们只是防止应用正数(经典的libc处理办法)。


想理解更多编程学习,敬请存眷php培训栏目!

以上就是PHP 治理全局的办法的具体内容,更多请存眷资源魔其它相干文章!

标签: php php开发教程 php开发资料 php开发自学 全局

抱歉,评论功能暂时关闭!