ThinkPHP 8.0 导出 Excel 必须用 phpoffice/phpspreadsheet,因 PHPExcel 已废弃且不兼容 PHP 8.0+,强行使用会报 Class 'PHPExcel' not found 或方法签名不兼容错误;其最后版仅支持 PHP 7.2,而 phpspreadsheet 是官方继任者,API 兼容但底层重写。

ThinkPHP 8.0 导出 Excel 必须用 phpoffice/phpspreadsheet,PHPExcel 已废弃且不兼容 PHP 8.0+,硬上会直接报 Class 'PHPExcel' not found 或 Declaration of ... must be compatible with ... 错误。
为什么不能继续用 PHPExcel
PHPExcel 在 2016 年已停止维护,其最后稳定版(1.8.2)仅支持到 PHP 7.2;TP8 默认要求 PHP 8.0+,而 phpspreadsheet 是它的官方继任者,API 兼容但底层完全重写。常见错误包括:
Fatal error: Declaration of PhpOffice\PhpSpreadsheet\Reader\Xlsx::canRead($pFilename) must be compatible with PhpOffice\PhpSpreadsheet\Reader\IReader::canRead($filename)-
Call to undefined method PhpOffice\PhpSpreadsheet\Spreadsheet::getActiveSheetIndex()(旧代码混用新类)
所有基于 PHPExcel 的 ThinkPHP 扩展(如 Org/Util/PHPExcel.class.php)在 TP8 中均不可用,强行 require 会触发 autoloader 冲突。
composer 安装与基础导出结构
执行安装命令,注意不要加 --dev(导出是运行时依赖):
立即学习“PHP免费学习笔记(深入)”;
composer require phpoffice/phpspreadsheet
导出逻辑应封装为独立服务类(非控制器内硬编码),核心步骤如下:
- 实例化
PhpOffice\PhpSpreadsheet\Spreadsheet - 获取当前 sheet:
$sheet->getActiveSheet()(不是getSheet(0)) - 表头写入:用
setCellValue('A1', '姓名')或批量循环 +ord()计算列字母 - 数据写入:逐行遍历数组,避免用
toArray()反向生成(性能差、内存溢出风险高) - 设置响应头强制下载,**必须禁用输出缓冲**:
ob_end_clean();否则返回空文件或乱码
导出中文乱码和样式丢失的根源
不是字体问题,而是默认编码和单元格格式未显式声明:
- 中文显示异常 → 每个单元格需调用
getStyle()->getFont()->setName('SimSun')(Windows)或'Noto Sans CJK SC'(Linux/macOS),否则 fallback 到无中文字体的 DejaVu - 数字被转成科学计数 → 对数值列(如手机号、ID)提前设置单元格格式:
getStyle('B2:B1000')->setNumberFormatCode('@')(文本格式) - 自动换行失效 → 需同时设置:
getAlignment()->setWrapText(true)+getRowDimension($row)->setRowHeight(-1) - 导出后 Excel 提示“发现不可读内容” → 未调用
$writer->save('php://output')而用了file_put_contents写临时文件再读取(破坏流完整性)
TP8 原生导出方法推荐写法
避免封装过度,直接在控制器中轻量调用(适合中小数据量):
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写表头
$header = ['ID', '姓名', '邮箱', '创建时间'];
foreach ($header as $col => $title) {
$cell = chr(65 + $col) . '1';
$sheet->setCellValue($cell, $title);
$sheet->getStyle($cell)->getFont()->setBold(true);
}
// 写数据(假设 $data 是二维数组)
$row = 2;
foreach ($data as $record) {
$col = 0;
foreach ($record as $value) {
$cell = chr(65 + $col) . $row;
$sheet->setCellValueExplicit($cell, $value, \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
$col++;
}
$row++;
}
// 设置响应头
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="export_' . date('Ymd_His') . '.xlsx"');
header('Cache-Control: max-age=0');
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save('php://output');
exit;
关键点:setCellValueExplicit(..., TYPE_STRING) 强制字符串类型,防止长数字(如身份证)被转义;exit 必须紧跟 save('php://output'),中间不能有 echo/print/debug 输出。
最易忽略的是导出前的输出缓冲清理和字符类型强制——这两项不处理,90% 的“导出失败”“文件损坏”“中文变方块”问题就出在这里。



















