升级PHP8报Cannot access offset of type string on string错误修复
问题背景
- 运行环境升级至PHP 8版本后,触发致命错误:
Uncaught TypeError: Cannot access offset of type string on string - 错误定位代码行:
if(is_array($course_data['steps']['h']["sfwd-lessons"])){ - 代码用于提取LearnDash插件的课程步骤数据,在低版本PHP环境中可正常运行,原业务代码如下:
// bail out if this is not an event item if ('sfwd-courses' !== get_post_type($post_id)) { return; } $course_data = get_post_meta($post_id, 'ld_course_steps', true); if (!empty($course_data)) { $lessons = array_keys($course_data['steps']['h']["sfwd-lessons"]); $quizes = array_keys($course_data['steps']['h']["sfwd-quiz"]); } $lesson_data = []; // Extract all steps of the courses if(is_array($course_data['steps']['h']["sfwd-lessons"])){ foreach($course_data['steps']['h']["sfwd-lessons"] as $lesson){ $lesson_data = array_merge($lesson_data,array_values(array_keys($lesson["sfwd-topic"]))); $lesson_data = array_merge($lesson_data,array_values(array_keys($lesson["sfwd-quiz"]))); foreach($lesson["sfwd-topic"] as $topic){ $lesson_data = array_merge($lesson_data,array_values(array_keys($topic["sfwd-quiz"]))); } } }
报错原因
问题由PHP版本语法规则调整、代码缺少多层结构合法性校验共同导致:
- PHP 7.x及更早版本中,对字符串等非数组类型的值使用数组方括号语法访问偏移量时,仅抛出非致命的Notice/Warning级提示,不会中断执行,此时访问返回
null,后续is_array(null)结果为false,逻辑自动跳过分支,表面上运行正常。 - PHP 8.0起,这类非法操作被升级为致命
TypeError,程序会直接终止运行,不再兼容非数组类型的数组式访问写法。 - 代码逻辑缺陷:仅判断了
$course_data非空,没有校验$course_data['steps']、$course_data['steps']['h']是否为数组。一旦数据库中存储的ld_course_steps元数据存在格式损坏、旧版本数据结构不匹配、返回值为非数组类型的情况,链式访问多层数组下标就会触发报错。
修复方案
核心修复逻辑为:在访问每一层数组下标前,先校验对应层级的值为数组、且目标键存在,避免对非数组值执行数组式访问,同时补全变量初始化避免未定义变量警告。修复后完整代码如下:
// bail out if this is not an event item if ('sfwd-courses' !== get_post_type($post_id)) { return; } $course_data = get_post_meta($post_id, 'ld_course_steps', true); // 初始化变量避免未定义告警 $lessons = []; $quizes = []; $lesson_data = []; // 逐层校验数组结构合法性 if ( is_array($course_data) && isset($course_data['steps']['h']) && is_array($course_data['steps']['h']) ) { $steps_h = $course_data['steps']['h']; // 校验课程列表键存在且为数组再取值 if (isset($steps_h["sfwd-lessons"]) && is_array($steps_h["sfwd-lessons"])) { $lessons = array_keys($steps_h["sfwd-lessons"]); } // 校验测验列表键存在且为数组再取值 if (isset($steps_h["sfwd-quiz"]) && is_array($steps_h["sfwd-quiz"])) { $quizes = array_keys($steps_h["sfwd-quiz"]); } // 提取课程下所有关联步骤 if(isset($steps_h["sfwd-lessons"]) && is_array($steps_h["sfwd-lessons"])){ foreach($steps_h["sfwd-lessons"] as $lesson){ // 校验课时下的主题结构 if (isset($lesson["sfwd-topic"]) && is_array($lesson["sfwd-topic"])) { $lesson_data = array_merge($lesson_data, array_values(array_keys($lesson["sfwd-topic"]))); foreach($lesson["sfwd-topic"] as $topic){ // 校验主题下的测验结构 if (isset($topic["sfwd-quiz"]) && is_array($topic["sfwd-quiz"])) { $lesson_data = array_merge($lesson_data, array_values(array_keys($topic["sfwd-quiz"]))); } } } // 校验课时下的测验结构 if (isset($lesson["sfwd-quiz"]) && is_array($lesson["sfwd-quiz"])) { $lesson_data = array_merge($lesson_data, array_values(array_keys($lesson["sfwd-quiz"]))); } } } }
修复点说明:
- 提前初始化
$lessons、$quizes变量,避免数据结构不符合预期时出现变量未定义的警告 - 从最外层
$course_data开始逐层做类型校验和键存在判断,所有数组下标访问都在确认前序值为数组的前提下执行,完全适配PHP 8的语法规则 - 对循环内部的
$lesson、$topic层级也补充了类型校验,避免遍历过程中出现同类类型错误 - 提取重复访问的
$steps_h变量减少重复的数组层级访问,性能略有提升
内容的提问来源于stack exchange,提问作者Ilianskia
相关产品推荐
相关产品推荐

