PHP 管理全局的方法

发布时间:2023-09-09 点击:94
【相关学习推荐: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()不

腾讯云服务器怎么关闭网站
一级域名怎么申请?如何选取域名?
域名行业季报反应什么趋势?有哪些主要的域名类型?
网站建设市场报价是多少 网站建设需要学习什么
买域名要多少钱?域名备案有什么准备事项?
co域名具有哪些优势 co域名的特点是什么
网站建设知识介绍 网站建设需要学多长时间
lighttpd配置HTTPS(SSL)方法