PHP写入CSV报Cannot access offset of type string on string解决
报错原因定位
你遇到的Uncaught TypeError: Cannot access offset of type string on string错误,核心触发原因有两点:
- 代码默认
$jd遍历出的每一个$line都是关联数组,但实际数据源中可能存在非数组类型的异常行(比如某条$line本身是字符串),此时用$line['phone']这种数组键访问方式操作字符串,就会直接抛出类型错误。 - 代码直接将整条
$line传入fputcsv(),该函数要求所有元素都是可转字符串的标量值,但源数据中存在customer_card、customer_group、lifetime_value这类嵌套数组字段,写入时不仅会触发数组转字符串的警告,还会干扰字段读取逻辑触发类型错误。
修复方案
核心逻辑是先做数据合法性校验,再通过字段白名单只提取业务需要的标量字段,自动跳过所有嵌套结构,同时优化去重逻辑的性能,完整可运行代码如下:
// 按需配置需要提取的业务字段白名单,不需要的嵌套字段不用列进来 $targetFields = [ 'address_line_1', 'address_line_2', 'city', 'company', 'country', 'email', 'first_name', 'last_name', 'phone', 'full_address', 'postal_code', 'created_date_unix' ]; // 提前提取CSV中已存在的手机号,转成键值对提升去重判断效率 $existPhoneMap = array_flip(array_filter(array_column($csv, 'phone'))); $newarray = []; foreach ($jd as $line) { // 过滤非数组的异常数据行,从根源避免数组键访问字符串的报错 if (!is_array($line)) { continue; } // 跳过手机号字段缺失、格式非法的无效行 if (empty($line['phone']) || is_array($line['phone'])) { continue; } $currentPhone = trim($line['phone']); // 手机号重复则跳过 if (isset($existPhoneMap[$currentPhone])) { continue; } // 仅提取白名单内的标量字段,自动跳过所有数组/对象类型的嵌套值 $csvRow = []; foreach ($targetFields as $field) { $csvRow[$field] = (isset($line[$field]) && is_scalar($line[$field])) ? trim($line[$field]) : ''; } // 写入结果集与CSV文件 $newarray[] = $csvRow; $existPhoneMap[$currentPhone] = true; fputcsv($file, $csvRow); }
方案特性
- 全链路类型校验:从遍历入口到单字段写入都做类型判断,彻底解决数组访问、CSV写入环节的类型错误
- 白名单机制完全匹配需求:所有嵌套数组字段因为不在提取列表里,会被自动跳过,不会进入写入流程
- 性能优化:将原代码中循环内重复调用
array_column、in_array的逻辑提前处理,去重判断复杂度从O(n)降到O(1),数据量较大时运行效率提升明显 - 容错性强:就算白名单内的字段意外出现数组、空值等异常情况,也会自动置空处理,不会中断整体写入流程
内容的提问来源于stack exchange,提问作者Yehuda Clinton
相关产品推荐
相关产品推荐

