安装 league/csv 需注意 PHP 版本兼容性(v9+ 要求 PHP ≥8.0,7.4 应用 ^8.3),国内需切换 Composer 源;读取带 BOM/中文/换行符 CSV 时须手动跳过 BOM、显式设置分隔符与编码、启用 auto_detect_line_endings;写入 Excel 兼容 CSV 必须添加 UTF-8 BOM。

直接执行 composer require league/csv 就能装上,但装完大概率读不了带 BOM 的中文 CSV——不是你代码写错了,是 v9+ 默认行为和 PHP 底层解析机制卡住了。
PHP 版本和 Composer 源不匹配会直接失败
league/csv v9+ 强制要求 PHP >= 8.0。如果你用的是 PHP 7.4,composer require league/csv 会静默回退或报依赖冲突,不会明确提示“PHP 版本太低”。这时得显式指定旧版:composer require league/csv:^8.3。
国内 Composer 镜像(如 phpcomposer.com)已停用,若卡在“Resolving dependencies”,先换源:composer config -g repo.packagist composer https://packagist.org 或阿里云源:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/。
常见报错如:Could not find a version of package league/csv matching your minimum-stability,大概率是源缓存异常或项目里已有冲突的 psr/http-message 版本。
装完就读报错 Invalid argument supplied for foreach()?检查三件事
这错误几乎都出在 $reader->getRecords() 返回空或非数组,根源不在你的 foreach,而在 Reader 初始化阶段就失败了:
-
Reader::createFromPath('data.csv', 'r')返回的是只读对象,不能直接->fetchOne(),必须先调->getRecords()或->setOffset() - 文件带 UTF-8 BOM(Windows 记事本保存的常见情况),
Reader不自动 strip,会导致首行解析乱码、getRecords()返回 false - PHP 8.2+ 下,
fgetcsv()默认不识别换行,需提前ini_set('auto_detect_line_endings', '1')
别信“自动检测”,所有格式参数都要显式设:$reader->setDelimiter(',')、$reader->setEnclosure('"')、$reader->setEscape('\')(注意:Excel 导出有时用 " 自身转义,此时 setEscape(''))。
想安全读带 BOM / 换行符 / 中文的 CSV,绕开 fgetcsv()
v9+ 底层仍走 fgetcsv(),它对多行字段、BOM、混合换行极其敏感。真要稳,就得手动接管流:
先用 fopen() 打开,检查并跳过 BOM:
$fp = fopen('data.csv', 'rb');
if (fread($fp, 3) === "") {
// BOM 存在,已跳过
} else {
fseek($fp, 0);
}
$reader = Reader::createFromStream($fp);
$reader->setDelimiter(';'); // 显式设分隔符,别依赖 guess
$reader->setHeaderOffset(0); // 有表头必须设,否则 getRecords() 返回索引数组
注意:createFromStream() 比 createFromPath() 更底层、更可控;但也要记得 fclose($fp),否则文件句柄泄漏。
写入 CSV 给 Excel 用,BOM 是硬性门槛
Excel 打开 UTF-8 CSV 显示小方块?不是编码问题,是缺 BOM。League/Csv 写入时不自动加 BOM,必须手动处理:
用 Writer::createFromPath() 创建后,先写 BOM:
$writer = Writer::createFromPath('output.csv', 'w');
$writer->addStreamFilter('convert.iconv.UTF-8/UTF-8//TRANSLIT'); // 可选,防乱码
$writer->insertOne(["姓名", "邮箱"]); // 把 BOM 塞进第一列第一格
$writer->insertAll([["张三", "zhang@example.com"]]);
或者更稳妥:用 fopen('output.csv', 'wb') 手动写 BOM,再传给 Writer::createFromStream()。只要没 BOM,Excel 就默认用 ANSI 解析——这点毫无商量余地。
另外,Excel 对字段含换行符极其挑剔,务必确保字段用双引号包裹,且导出时启用 RFC 4180 兼容模式(setEnclosure('"') + setEscape('\'))。











