活力39167
在线时间13434 小时
阅读权限200
管理员
自由的灵魂
- 积分
- 106811
- 主题
- 5597
- 回帖
- 26608
- 注册时间
- 2003-4-10
- 最后登录
- 2026-8-11
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1、问题缘起
后台插件管理点击"发现新版"时,页面抛出系统致命错误:
- Cannot access offset of type type string on string
复制代码
堆栈定位:
- admin.php:58 → source/admincp/admincp_plugins.php:1554 (getimportdata()) → source/function/function_admincp.php:1069 (xml2array()) → source/class/class_xml.php:16 (XMLparse->parse()) → open()
根因分析:
应用中心返回的数据并非标准 Discuz XML(可能是 HTML 错误页、网络超时页或 XML 中 <item> 缺失 id 属性),导致 XMLparse 内部引用游标 $this->document 从数组退化为字符串。当解析器继续遇到下一个带 id 的标签时,执行 $this->document = &$this->document[$attributes['id']],在 PHP 8.x 下对字符串使用字符串下标会直接抛出 fatal error(PHP 7 及以下仅 Warning 并返回 null,不会中断页面)。
2、解决方案
(1)简要技术分析
在 XML 解析器的 open() 回调入口处增加 is_array 类型保护:一旦内部游标因异常数据退化为字符串,立即重置为空数组,阻断 fatal error 传播,让解析器继续执行并最终返回空数组,由上层逻辑展示"数据无效"的友好提示。
(2)修改、新增文件列表
| 类型 | 完整路径 | | 修改 | source/class/class_xml.php |
(3)关键作用代码段全文
source/class/class_xml.php —— open() 方法(第 68 行附近)
- function open($parser, $tag, $attributes) {
- $this->data = '';
- $this->failed = FALSE;
-
- /* === 新增:PHP 8 兼容性保护 === */
- if(!is_array($this->document)) {
- $this->document = array();
- }
- /* ============================= */
-
- if(!$this->isnormal) {
- if(isset($attributes['id']) && !(isset($this->document[$attributes['id']]) && is_string($this->document[$attributes['id']]))) {
- $this->document = &$this->document[$attributes['id']];
- } else {
- $this->failed = TRUE;
- }
- } else {
- if(!isset($this->document[$tag]) || !is_string($this->document[$tag])) {
- $this->document = &$this->document[$tag];
- } else {
- $this->failed = TRUE;
- }
- }
- $this->stack[] = &$this->document;
- $this->last_opened_tag = $tag;
- $this->attrs = $attributes;
- }
复制代码
3、最终实现效果
| 场景 | PHP 7.x 行为 | 修复前 PHP 8.x 行为 | 修复后 PHP 8.x 行为 | | 应用中心返回标准 XML | 正常解析 | 正常解析 | 正常解析(无变化) | | 应用中心返回 HTML/异常 XML | Warning + 返回空数组 | Fatal Error 页面中断 | 优雅降级,返回空数组 | | 用户感知 | 提示"导入数据无效" | 系统报错,无法继续 | 提示"导入数据无效",可继续操作
(实测已经正常无报错,完美解决!) |
4、重要技术沉淀
- PHP 8 字符串下标严格化:$string['key'] 在 PHP 8 中不再静默返回 null,而是直接 TypeError fatal。所有旧项目中基于"容错假设"写的数组游标代码都需要审计。
- Discuz XML 解析器设计缺陷:XMLparse 使用全局引用游标 $this->document 作为解析栈指针,一旦某一步异常导致类型退化,后续所有操作都会级联崩溃。防御式编程(入口类型检查)比依赖下游容错更安全。
5、进一步优化的可能性或尚未实现的构想
- 前置 HTTP 响应校验:在 getimportdata() 调用 xml2array() 之前,先判断返回内容是否以 <?xml 开头,不是则直接提示"应用中心服务异常",避免进入解析器。
- 解析器现代化替换:XMLparse 基于 xml_parser_create 的 SAX 风格手写引用栈已非常古老,可考虑逐步迁移至 SimpleXML 或 DOMDocument,彻底消除引用游标类型退化风险。
- 应用中心域名可用性探测:Discuz 应用中心域名偶尔不稳定,可在后台增加连通性检测或超时降级逻辑。
|
|