PHP项目中如何使用PhpSpreadsheet?

wen PHP项目 4

本文目录导读:

PHP项目中如何使用PhpSpreadsheet?

  1. 目录导读
  2. PhpSpreadsheet是什么?为什么取代PHPExcel?
  3. 环境安装与配置
  4. 核心功能实战:读取Excel/CSV文件
  5. 核心功能实战:生成复杂Excel报表
  6. 性能优化与内存管理(大文件处理技巧)
  7. 常见陷阱与错误处理机制
  8. 安全注意事项:防止注入与数据泄露
  9. 问答环节:高频问题深度解答

PHP项目中高效使用PhpSpreadsheet:从入门到高级实践

目录导读

  1. PhpSpreadsheet是什么?为什么取代PHPExcel?
  2. 环境安装与配置(Composer + 依赖管理)
  3. 核心功能实战:读取Excel/CSV文件
  4. 核心功能实战:生成复杂Excel报表(含样式、图表)
  5. 性能优化与内存管理(大文件处理技巧)
  6. 常见陷阱与错误处理机制
  7. 安全注意事项:防止注入与数据泄露
  8. 问答环节:高频问题深度解答

PhpSpreadsheet是什么?为什么取代PHPExcel?

PhpSpreadsheet 是 PHPExcel 的官方继承者,由 PHPOffice 团队维护,它提供了纯 PHP 实现的电子表格操作能力,支持读取/写入 Excel(XLSX、XLS)、CSV、HTML、PDF 等多种格式,之所以必须迁移,是因为 PHPExcel 已停止维护(最后版本停留在 2017 年),且不支持 PHP 8.x 及以上版本,PhpSpreadsheet 完全重写了架构,支持命名空间、PSR 标准、更好的性能与内存控制,是目前 PHP 生态处理电子表格的唯一推荐库。

核心优势:

  • 原生支持 PHP 8.1+
  • 内存优化:基于单元格流式处理
  • 样式支持:字体、边框、合并、条件格式
  • 公式计算:内置计算引擎
  • 安全性:自动过滤恶意宏

环境安装与配置

通过 Composer 安装

composer require phpoffice/phpspreadsheet

加载自动加载文件

require_once 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

基础验证(检查版本)

echo (new Spreadsheet())->getProperties()->getCreator();

常见问题:如果内存不足,需在 php.ini 中提高 memory_limit 至 512M 以上。


核心功能实战:读取Excel/CSV文件

读取标准 Excel 文件(XLSX)

$inputFileName = './data/report.xlsx';
$spreadsheet = IOFactory::load($inputFileName);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
for ($row = 1; $row <= $highestRow; ++$row) {
    for ($col = 1; $col <= $highestColumnIndex; ++$col) {
        $value = $worksheet->getCellByColumnAndRow($col, $row)->getValue();
        // 处理数据...
    }
}

读取大文件(CSV 流式加载)

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($inputFileName);

注意:CSV 文件读取时需指定编码,避免中文乱码:$reader->setInputEncoding('UTF-8');


核心功能实战:生成复杂Excel报表

创建一个包含样式和公式的简易报表

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 设置表头
$sheet->setCellValue('A1', 'ID')
      ->setCellValue('B1', '姓名')
      ->setCellValue('C1', '销售额')
      ->setCellValue('D1', '提成比例')
      ->setCellValue('E1', '提成金额');
// 添加样式
$styleArray = [
    'font' => ['bold' => true, 'size' => 12],
    'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER],
    'borders' => ['allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN]],
];
$sheet->getStyle('A1:E1')->applyFromArray($styleArray);
// 填充数据并使用公式
$sheet->setCellValue('A2', 1);
$sheet->setCellValue('B2', '张三');
$sheet->setCellValue('C2', 15000);
$sheet->setCellValue('D2', 0.05);
$sheet->setCellValue('E2', '=C2*D2'); // 公式计算
// 设置列宽自适应
foreach (range('A','E') as $col) {
    $sheet->getColumnDimension($col)->setAutoSize(true);
}
// 输出文件
$writer = new Xlsx($spreadsheet);
$writer->save('report.xlsx');

插入图表(柱状图示例)

$dataSeriesLabels = [
    new \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues('String', 'Worksheet!$B$1', null, 1),
];
$xAxisTickValues = [
    new \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues('String', 'Worksheet!$A$2:$A$5', null, 4),
];
$dataSeriesValues = [
    new \PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues('Number', 'Worksheet!$B$2:$B$5', null, 4),
];
$series = new \PhpOffice\PhpSpreadsheet\Chart\DataSeries(
    \PhpOffice\PhpSpreadsheet\Chart\DataSeries::TYPE_BARCHART,
    null,
    [0],
    $dataSeriesLabels,
    $xAxisTickValues,
    $dataSeriesValues
);
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart('chart', $chartTitle, $legend, $plotArea);
$chart->setTopLeftPosition('G2');
$chart->setBottomRightPosition('M15');
$sheet->addChart($chart);
// 注意:必须启用图表写入
$writer->setIncludeCharts(true);

性能优化与内存管理(大文件处理技巧)

单元格迭代器(避免一次性加载全部数据)

$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($inputFileName);
$worksheet = $spreadsheet->getActiveSheet();
// 使用迭代器逐行读取
foreach ($worksheet->getRowIterator() as $row) {
    $cellIterator = $row->getCellIterator();
    $cellIterator->setIterateOnlyExistingCells(false);
    foreach ($cellIterator as $cell) {
        $data[] = $cell->getValue();
    }
    // 及时释放内存
    unset($data);
}

禁用计算和样式加载

// 读取时禁用公式计算
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
// 仅加载指定工作表(多文件场景)
$reader->setLoadSheetsOnly(['Sheet1']);

写入大文件:分批次输出

$spreadsheet->disconnectWorksheets(); // 释放旧工作表
$sheet = new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'Batch1');
$spreadsheet->addSheet($sheet);
// 处理一批后保存并清空
$writer = new Xlsx($spreadsheet);
$writer->save('batch_'.$batchNum.'.xlsx');

常见陷阱与错误处理机制

陷阱 解决方案
中文乱码 读取时设置编码:$reader->setInputEncoding('UTF-8')
数字被识别为文本 使用 setValueExplicit() 强制类型
公式不计算 手动调用计算:$spreadsheet->getCalculationEngine()->calculateAll()
内存耗尽 使用 $spreadsheet->disconnectWorksheets() 释放
日期格式错误 使用 \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject() 转换
大文件超时 设置 set_time_limit(0) 并分块处理

安全注意事项:防止注入与数据泄露

禁止启用宏

$reader->setReadDataOnly(true); // 忽略所有宏

验证上传文件

$allowedMimeTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
if (!in_array($fileInfo['type'], $allowedMimeTypes)) {
    throw new \Exception('非法文件格式');
}

数据清洗

$value = htmlspecialchars(strip_tags($cellValue), ENT_QUOTES, 'UTF-8');

禁用远程加载

php.ini 中关闭 allow_url_fopen,防止 Excel 中的外部引用导致 SSRF。


问答环节:高频问题深度解答

Q1:PhpSpreadsheet 与 PHPExcel 的核心区别是什么?
A:PhpSpreadsheet 是完全重写的库,支持命名空间,基于 phpoffice/phpspreadsheet,PHP 7.4+ 推荐使用;PHPExcel 已废弃,不支持 PHP 8+,且存在内存泄漏。

Q2:如何读取超大 Excel 文件(超过 100MB)?
A:使用 \PhpOffice\PhpSpreadsheet\Reader\Xlsx 的流式读取模式,配合 setReadDataOnly(true)setLoadSheetsOnly(),建议采用分批加载并每 5000 行保存一次。

Q3:生成的 Excel 文件在 Mac 上打开提示损坏?
A:通常是因为 writer 输出时未清除输出缓冲区,输出前调用 ob_clean()ob_flush(),并确保 header() 正确设置:

header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="export.xlsx"');
header('Cache-Control: max-age=0');
$writer->save('php://output');

Q4:如何动态设置单元格背景色?
A:使用条件样式或应用样式到指定范围:

$style = [
    'fill' => [
        'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
        'startColor' => ['argb' => 'FFC000']
    ]
];
$sheet->getStyle('A1:B10')->applyFromArray($style);

Q5:插入图片到单元格中如何实现?
A:使用 Drawing 对象:

$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$drawing->setName('Logo');
$drawing->setPath('./logo.png');
$drawing->setCoordinates('A1');
$drawing->setWidth(100);
$drawing->setHeight(100);
$sheet->getColumnDimension('A')->setWidth(15);
$drawing->setWorksheet($sheet);

本文从环境搭建、基础读写、样式美化、性能优化到安全防护,系统梳理了 PHP 项目中使用 PhpSpreadsheet 的完整路径,关键原则是:内存优先(使用迭代器)、格式统一(指定 UTF-8 编码)、安全第一(禁用宏和外部引用),实践中建议配合队列系统处理超大型报表,并采用单元测试验证特定格式兼容性,如果遇到版本冲突,请检查 Composer 的 composer.json 中是否锁定了 ^1.20 以上版本(支持 PHP 8.2)。


参考资料:PhpSpreadsheet 官方文档、GitHub Issues 讨论、Stack Overflow 最佳实践

抱歉,评论功能暂时关闭!