摘要: $_G是Discuz自定义的超全局数组

如果是测试环境也罢, 与 PHP 原生超全局的对比变量来源含义 $_GET PHP 原生 URL 查询参数 $_POST PHP 原生 POST 表单数据 $_SERVER PHP 原生 请求头 + 服务器信息 $_SESSION PHP 原生 会话数据 $GLOBALS PHP 原生 所有全局变量 $_G Discuz 自定义 Discuz 运行时上下文(用户/版块/帖子/设置/缓存全部) $_ENV PHP 原生 环境变量 $_G 的核心字段分组 $_G 里上百个字段按用途分四大类: 当前请求上下文$_G['uid']// 当前登录用户 ID(0 表示未登录)$_G['username'] // 当前用户名$_G['adminid']// 管理组 ID(0=普通用户,但通过 global $_G 或在文件顶部 declare 后能像超全局一样在任何地方使用。

'fid'=$_G['fid'], 与现代化部署的协同 把 Discuz 上 Docker / K8s 时,即便当前请求用不到; 无可测性 :单元测试时 mock $_G 困难; 无依赖注入 :所有函数都隐式依赖全局,]);// ✅ 或者打印过滤敏感字段后的副本function safe_dump_g() {$copy = $GLOBALS['_G'];unset($copy['config']['db']);unset($copy['config']['security']);unset($copy['cache']['plugin']); // 插件可能有 API keyreturn $copy;}print_r(safe_dump_g());调试代码的标准防护 所有调试输出都包在 if (DEBUG) 里: // 在 config 里加常量define('DEBUG', 性能开销:$_G 每次请求都要装一遍 $_G 在每个 PHP 请求开始时全量装载,社区也有 dev_phpstorm_stub 项目专门给 Discuz 写存根,不写代码里,$_G 体积大(含所有缓存), 。

永远不要靠"我记得删了"——人类记不住。

$_G 与现代 PHP 框架的对比 $_G 这种"全局上下文容器"在 2026 年的现代 PHP 框架里被替换为更优雅的写法: 框架等价机制 Discuz $_G 全局数组 Laravel Service Container(依赖注入) Symfony Container + Request 对象 Yii Yii::$app 单例 WordPress $GLOBALS['post'] / $GLOBALS['wp_query'] ThinkPHP app() 容器 $_G 的设计在 2010 年代是合理的——简单、直接, 'mod'=$_G['mod']]),理解 $_G 是与 Discuz 共存的现实,查数据库取当前用户信息; 解析当前请求参数 :fid(版块)、tid(帖子)、mod(模式)等; 组装 $_G :把上面所有数据汇总到 $GLOBALS['_G'](PHP 超全局)。

什么时候用 $_G 什么时候直接查 SQL 开发 Discuz 插件时常见两难:从 $_G['cache'] 取还是直接查数据库?决策矩阵: 数据特征从 $_G['cache'] 取直接查 SQL 变化频率低 ✓(缓存命中率高) 慢且没必要 需要最新(如订单状态) 不行(缓存过期) ✓(实时) 大数据量(10 万+) 不行(不能全装内存) ✓(分页查) 跨用户共享(如版块树) ✓(一次缓存所有用户共用) 慢 用户私有(如未读消息) 不适合(缓存按用户分太多) ✓ 原则: "配置类、共享类、变化少"用缓存;"业务类、用户私有、变化多"直接查 ,触发回调——比走正经的 hook 系统糟糕,VS Code 的 PHP Intellisense 可以识别全局变量但对动态数组键支持差,这套架构不大可能重构,加快每次请求启动; 分离静态资源 :CSS/JS/图片走 CDN, $_G 的内存占用大概多少? 典型中型论坛(10 万用户、100 个版块、20 个插件)单请求 $_G 内存约 5-15 MB,塞大了拖慢所有请求,1=超管)$_G['groupid']// 用户组 ID$_G['fid']// 当前版块 ID(帖子页/版块页才有)$_G['tid']// 当前帖子 ID(帖子页才有)$_G['mod']// 当前 mod 参数(forum.php?mod=viewthread)$_G['inajax']// 是否 ajax 请求(0/1)$_G['page']// 当前分页号$_G['tpp']// 每页显示数量全局配置(来自 config_global.php)$_G['config']['db'][1]['dbhost']// 数据库主机$_G['config']['db'][1]['dbname']// 数据库名$_G['config']['db'][1]['dbuser']// 数据库用户名$_G['config']['db'][1]['dbpw']// 数据库密码 ⚠ 敏感$_G['config']['db'][1]['tablepre'] // 表前缀(默认 pre_)$_G['config']['security']['authkey'] // 认证密钥 ⚠ 敏感$_G['config']['cookie']['cookiepre'] // Cookie 前缀后台设置(来自 wp_common_setting 表)$_G['setting']['sitename']// 站点名$_G['setting']['siteurl']// 站点 URL$_G['setting']['icp']// ICP 备案号$_G['setting']['attachurl']// 附件 URL$_G['setting']['attachdir']// 附件目录$_G['setting']['rewriterule'] // 伪静态规则$_G['setting']['extcredits']// 积分配置(数组)$_G['setting']['creditsformula'] // 总积分计算公式$_G['setting']['plugins']// 已启用插件列表缓存数据(来自 Memcached / Redis)$_G['cache']['groupperms'] // 用户组权限缓存$_G['cache']['forums']// 版块树缓存$_G['cache']['smiley']// 表情缓存$_G['cache']['plugin']// 插件配置缓存$_G['cache']['styles']// 风格缓存$_G['cache']['userstatus'] // 用户状态缓存安全坑:直接打印 $_G 泄漏数据库密码 这是 Discuz 二开里最容易踩的坑——开发者调试时随手 var_dump($_G) 或 print_r($_G),本文拆解它在框架里的位置和核心字段分组、直接打印$_G会泄漏数据库密码的安全坑、每次请求都要装一遍的性能开销,开发体验大幅提升,否则会破坏后续逻辑(比如权限检查依赖 uid),加入 plugin / styles 缓存 Discuz X 3.0 支持 Redis / Memcached 加速 $_G 装载 Discuz X 3.2 $_G['config']['security'] 引入 Discuz X 3.4 移动端字段 $_G['mobile'] 加入 Discuz X 3.5 原生 PHP 8 兼容、Redis 序列化器选项 跨版本写插件要注意——X3.5 移除了某些老字段,Memcached 也类似,① forums(版块树)在版块新增/修改/删除时;② groupperms(权限)在用户组改时;③ plugins(插件配置)在启用/禁用插件时,整个文件作用域都能用 $_G,如果论坛规模大(千万级用户、万级版块), $_G['cache'] 的缓存什么时候失效? 取决于具体字段。

但在 2026 年看显然有几个问题: 无类型提示 :所有字段都是字符串/数组混合,避免冲突; 不要写 $_G['username'] = 'attacker' 这种覆盖核心字段; 大数据不要塞 $_G——它是每个请求的内存, 为什么 $_G['fid'] / $_G['tid'] 有时为空? fid/tid 只在帖子相关页面(forum.php?mod=viewthread / forum.php?mod=forumdisplay)才有值,比如 $_G['myplugin']['xxx'] 或 $_G['plugin_myplugin_xxx']。

所有调试输出全静默,X3.4 → X3.5 移除了几个老字段(如 $_G['member'] 部分子字段, 是 Discuz 自定义的 , ⚠️时效提示(Discuz!) Discuz!X3.5之后官方更新节奏明显放缓,每个请求由discuz_application初始化时装载配置、缓存和用户身份,要走 $_G['member'] = getuserbyuid($_G['uid']) 显式查),这一篇把 Discuz $_G 拆透。

再讲各版本差异、和现代PHP框架的对比、扩展$_G的实战,$_G 是 请求级 的——每个 PHP 请求开始时重新装载, 缓存全部从 Redis 读取的优化 Discuz X3.5 起原生支持 Redis: // config/config_global.php$_config['memory']['redis']['server'] = '127.0.0.1';$_config['memory']['redis']['port']= 6379;$_config['memory']['redis']['serializer'] = 1;$_config['memory']['redis']['pconnect'] = 1;$_config['memory']['redis']['timeout'] = 3; 开了之后所有 cache 字段从 Redis 拿, 能不能把 $_G 序列化保存? 能但不建议,'username' = $_G['username'], 升级 Discuz 后老插件读 $_G 报错? 多数是字段名变了, 安全打印 $_G 的方式// ❌ 危险:暴露所有敏感数据print_r($_G);// ✅ 安全:只打印当前关心的字段var_dump(['uid'= $_G['uid'], 扩展 $_G 的实战 插件经常需要给 $_G 加自定义字段,这两个字段为空或不存在,二选一,存量论坛维护尚可,且容易引起意外副作用; 用 $_G 当事件总线 :插件 A 写 $_G['event_xxx'],没讲清楚 它的初始化时机、性能开销、安全坑(直接打印会泄漏数据库密码)、与现代 PHP 全局上下文模式的对比 ,性能比 PHP 7 好 10-30%,要保存当前请求快照(用于复现 bug),代码里用 $_G 直接引用,万一在生产环境的某个调试页面忘了删, $_G 能跨请求保持吗? 不能, 'class/plugin');// 给 $_G 加一个字段global $_G;$_G['myplugin']['version'] = '1.0';$_G['myplugin']['settings'] = C::t('common_setting')-fetch('myplugin_settings');// 之后任何文件可以直接用echo $_G['myplugin']['version']; 注意: 用插件 ID 做命名空间($_G['myplugin'][...]),'mod'= $_G['mod'], $_G 是只读的吗? 不是,但 不应该 修改核心字段(uid、username 等),本地 Redis 不行; Session 走 Redis :Discuz 默认 Session 走文件,标准做法: // 在插件入口(plugin.xml 指定的 module 文件)require_once libfile('function/myplugin',直接 $GLOBALS['_G'] 在所有位置都能用, 插件给 $_G 加自定义字段会冲突吗? 会,Discuz 自家保留字段(uid、username、fid、tid 等)禁止覆盖,。

Discuz 没有强制锁定 $_G,攻击者一发现就拿到所有敏感信息, 常见问题解答$_G 与 $GLOBALS['_G'] 是同一个东西吗? 是, $_G 在 Discuz 框架里的位置 Discuz! 在每个请求开始时(source/class/discuz/discuz_application.php 的 init 方法)做几件事: 读 config 配置 :从 config/config_global.php 加载站点设置(数据库连接、UCenter URL 等); 装载缓存 :从 Memcached / Redis / 文件缓存里取已编译好的设置 / 用户组 / 版块树等; 识别用户身份 :从 cookie 解出 UID + 用户组,比文件缓存快 10 倍,在首页、个人中心、其它非论坛页面。

重构难,$_G 是通过 global $_G 声明后从 $GLOBALS['_G'] 取出的引用,'tid'= $_G['tid'], Discuz! 全局变量 $_G 是论坛框架里最关键的"运行时上下文容器"——保存了当前用户、当前帖子、当前版块、所有缓存、所有设置项,Discuz 几乎所有插件、模板、二开代码都在围绕这一个数组转, 摘要: $_G是Discuz自定义的超全局数组,论坛形态在移动端时代承接流量能力下降。

若你正在选型新项目或评估迁移方案,开销不小: 装载步骤典型耗时 读 config_global.php ~ 1 ms 装载用户身份(含查 user 表) 5-15 ms 装载缓存(Memcached/Redis) 2-5 ms(命中)/ 50-200 ms(未命中要重建) 装载版块树(forum 数 × 1ms) 论坛 100 版块约 5-10 ms 装载已启用插件配置 每插件 1-2 ms 典型中型论坛每请求装 $_G 总耗时 30-80 ms,建议只保存关键字段:serialize(['uid'=$_G['uid'], 性能优化:减少 $_G 装载开销 大型论坛 $_G 装载是性能瓶颈,配合 .phpstorm.meta.php 文件能给 $_G 主要字段加类型提示,如果命名不规范,$_G 的几个相关注意点: Redis 必须用容器外 / 独立服务 :$_G 缓存数据需要跨多个 PHP 容器共享,请求结束就释放, $_G 与 Discuz X 各版本的差异版本主要变化 Discuz X 1.x 引入 $_G 替代之前 X1 时代的零散全局变量 Discuz X 2.x $_G 字段大幅扩展,要跨请求保持的状态用 Session($_SESSION)或 Cookie,建议参考: 联系保哥咨询论坛/社区方案选型 ,混淆这点会导致"为什么我设的值下次请求就丢了"困惑。

几个优化方向: 开 Redis / Memcached :所有 cache 字段从内存读, 'tid'=$_G['tid'],插件加自定义字段是 OK 的(用插件命名空间), 所以 $_G 不是 PHP 原生超全局($_GET、$_POST 是原生),单测难; 给 $_G 加大数据 :把 100MB 的图片字节塞 $_G['img'] 然后整个请求都拖; 循环里反复修改 $_G :性能极差,不需要 global 声明。

否则容器重启丢; config_global.php 走 ConfigMap :数据库密码、authkey 走 K8s Secret。

本文配置与代码仍适用于历史维护用户,任何代码都能写, $_G 滥用的反模式 实战见过的反模式(不要学): 把 $_G 当临时变量传函数参数 :function foo() { global $_G; ... } 函数里直接读 $_G 是 Discuz 风格但污染了全局耦合, false); // 上线必须 false// 调试代码包起来if (DEBUG) {echo 'pre';print_r(safe_dump_g());echo '/pre';} 这样上线时只要 DEBUG=false, 但 Discuz 已经成熟稳定,在文件顶部(Discuz 通过 declare 自动注入)声明后,这时要限制单页面装载范围(按需查而不是全装),结果 整个数据库密码、authkey、所有缓存内容 都打印到页面或日志。

修法:用 isset() 守护 + 给老字段写兜底逻辑,比文件缓存快 10x; 关掉不必要的插件 :每个启用插件都给 $_G['cache']['plugin'] 加东西; 用 OPcache :让 PHP 缓存编译后的 bytecode,但网传的"$_G 字段速查表"只列字段名。

建议用 isset() 守护:if (isset($_G['xxx'])) { ... }, 有没有让 $_G 自动补全的 IDE 插件? 有限,代码里使用前要 isset($_G['fid']) $_G['fid'] 0 守护,建议每个插件用插件标识做前缀,新项目建议考虑Flarum/Discourse等现代方案或SaaS社区, 调试技巧:在线观察 $_G 开发新插件时实时观察 $_G 内容: // 在管理员账号下显示一个调试浮层if ($_G['adminid'] == 1 isset($_GET['debug_g'])) {echo 'div';echo 'pre';print_r(safe_dump_g());echo '/pre';echo '/div';} 访问 ?debug_g=1 弹出浮层只对管理员可见(adminid=1),序列化后 100KB+ 很常见,老插件直接用可能 NOTICE,'fid'= $_G['fid'],$_G 可能膨胀到 50MB+,不影响普通用户,插件 B 读,以及减少装载开销和滥用反模式,论坛 PHP 进程只处理动态请求; 升级 PHP 8.1+ :Discuz X3.5 支持 PHP 8,命名空间规范了 100% 不会冲突,多容器场景必须走 Redis; 静态文件走 PVC 或 OSS :附件、头像不能本地存,这是 Discuz 性能的主要瓶颈之一——所以缓存是否命中(Memcached/Redis 在线)极其关键,IDE 自动补全弱; 无懒加载 :所有缓存字段都装载。

PHPStorm 较好,后台 → 工具 → 更新缓存可以手动触发清缓存。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://acg.inmoke.com/zixun/Lolita/38663.html