在CodeIgniter项目中处理业务上报的Excel文件时,经常会遇到单元格里不是纯数据,而是写了类似SUM、VLOOKUP这类公式的情况。如果只读取计算后的值,一旦原始公式逻辑变动或者需要校验用户填写的算法,系统就无法感知。借助PhpSpreadsheet库,我们可以在CodeIgniter里完整地读取公式本身以及其计算结果,并对结构复杂的表头、合并区域做解析。

一、环境准备与库接入
CodeIgniter作为轻量PHP框架,不直接提供Excel操作类,我们通过Composer引入PhpSpreadsheet。在项目根目录执行安装后,就能在控制器中直接use对应的命名空间,不需要额外写底层文件读取逻辑。
需要注意,服务器PHP版本应不低于7.2,并且开启zip与xml扩展,否则xlsx文件无法解压读取。下面是基础的控制器引入方式,我们把读取动作封装在独立方法里,方便复用。
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
use PhpOfficePhpSpreadsheetIOFactory;
class Excel_parse extends CI_Controller
{
public function read_formula()
{
// 文件路径根据实际上传目录调整
$file_path = './uploads/demo.xlsx';
$spreadsheet = IOFactory::load($file_path);
$sheet = $spreadsheet->getActiveSheet();
// 后续读取逻辑
}
}
二、公式与计算值的读取差异
很多开发者误以为单元格里看到的数字就是通过getValue拿到的内容,其实当单元格写入=SUM(A1:A3)时,getValue返回的是字符串形式的公式,而getCalculatedValue才会触发计算引擎给出结果。在复杂表格校验场景中,我们往往两个都要取。
例如财务表要求用户用公式填列,后台既要看公式是否合规,也要存计算结果进数据库。下面的代码演示了遍历A列前十行并分别输出公式与值的过程,其中计算前可先调用公式校验避免非法引用导致异常。
<?php
foreach (range(1, 10) as $row) {
$cell = $sheet->getCell('A' . $row);
$formula = $cell->getValue(); // 可能为 =B1+C1
$value = $cell->getCalculatedValue(); // 计算后的数字
if (strpos((string)$formula, '=') === 0) {
log_message('debug', '行' . $row . '公式:' . $formula . ' 值:' . $value);
}
}
如果表格中存在跨工作表引用,比如=Sheet2!B2,PhpSpreadsheet在计算时会自动跳转对应sheet获取数据。但这要求整本工作簿已完全加载,不能只读取单个sheet对象,否则计算会报引用错误。
三、复杂表格结构解析实战
真实业务表通常带有合并单元格与多行表头。直接用坐标循环会漏掉被合并掩盖的数据,因此要先通过getMergeCells拿到合并范围,将主单元格内容映射到附属坐标上,保证每行记录字段完整。
以下示例先收集合并区域,再按行读取,遇到合并起始格就把值赋给区间内所有逻辑列。这种方式在解析工资表或库存明细时非常实用,不会因为视觉合并而丢字段。
<?php
$merge_list = $sheet->getMergeCells();
$merge_map = [];
foreach ($merge_list as $range) {
list($start, $end) = explode(':', $range);
$val = $sheet->getCell($start)->getCalculatedValue();
$merge_map[$range] = $val;
}
// 假设表头占前两行,数据从第三行起
for ($r = 3; $r <= 20; $r++) {
$row_data = [];
foreach (range('A', 'F') as $col) {
$coord = $col . $r;
$row_data[$col] = $sheet->getCell($coord)->getCalculatedValue();
}
// 此处可将$row_data写入数据库或做公式校验
}
对于动态列头,即列数随月份或部门变化的报表,建议在读取前用getHighestColumn确定边界,再结合首行文本建立字段名映射。这样即使运营临时加了一列公式指标,解析脚本也不用改代码。
四、大文件与性能注意事项
当Excel超过五万行且含大量公式时,一次性加载极易耗尽内存。PhpSpreadsheet提供只读缓存与分块读取器,可以在CodeIgniter里配置将单元格数据暂存到磁盘或数据库,降低峰值占用。
下面的配置在load之前设置,让读取器只保留必要索引,适合后台队列任务慢慢解析。同时公式的计算可以延后到真正使用某行时再触发,避免全表运算卡死进程。
<?php
use PhpOfficePhpSpreadsheetCellCell;
use PhpOfficePhpSpreadsheetReaderIReadFilter;
class Chunk_filter implements IReadFilter
{
public function readCell($col, $row, $ws = '')
{
return $row >= 1 && $row <= 1000;
}
}
$reader = IOFactory::createReader('Xlsx');
$reader->setReadFilter(new Chunk_filter());
$spreadsheet = $reader->load($file_path);
经过上述分层处理,CodeIgniter就能稳妥地完成带公式Excel的读取与复杂表格解析,既保留用户的算法逻辑,又输出干净的结构化数据供后续业务使用。
CodeIgniterPhpSpreadsheetExcel_formula修改时间:2026-08-04 15:21:29