PhpSpreadsheet(原 PHPExcel)是 PHP 中最流行的电子表格软件(如 Excel)的处理类库,可以读取、写入 Excel 数据等,经常用于业务系统的报表导出等。
问题复现
在使用过程中,经常遇到的问题是,无法正常显示身份证号。我们看下实例:
源文件:sheet1.php
<?php
require __DIR__ . '/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$users = [
['姓名', '身份证号'],
['莱德', '120110202001019001'],
['阿奇', '120110202001019002'],
];
$sheet->fromArray($users);
$sheet->setCellValue('A4', '毛毛');
$sheet->setCellValue('B4', '120110202001019003');
$writer = new Xlsx($spreadsheet);
$writer->save('users.xlsx');
终端下执行下:% php sheet1.php,保存到了文件 users.xlsx,查看结果:
可见,莱德等小狗狗的身份证号,默认被当作了数字数据类型。
原因分析
这是因为在通过 fromArray 间接或 或直接使用 setCellValue 方法时,未指定数据类型。这时,PhpSpreadsheet 使用值绑定器(Value Binder)自动将传入的参数转换为合适的 Cell 数据类型。具体源码实现,见默认的值绑定器是 DefaultValueBinder。此处身份证号虽然是原生 string,但符合类对数字的定义,处理成了数字数据类型。
解决办法:使用 setCellValueExplicit
我们在设置值时使用 setCellValueExplicit 方法指定类型。
// 省略此部分,参考上文
use PhpOffice\PhpSpreadsheet\Cell\DataType;
// 省略此部分,参考上文
$sheet->setCellValue('A5', '天天');
$sheet->setCellValueExplicit('B5', '120110202001019004', DataType::TYPE_STRING);
// 省略此部分,参考上文
天天的身份证号已经正常显示。
若希望类似 fromArray 批量写入数据,可自行实现一个迭代处理。
解决办法:自定义值绑定器
另外一种办法是实现自定义的值绑定器类。
直接看实现源码:
<?php
require __DIR__ . '/vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class MyValueBinder extends DefaultValueBinder
{
public static function dataTypeForValue($pValue)
{
// 此处判断只提供一种实现思路,只是简单判断长度,可优化。
if (strlen($pValue) == 18) {
return DataType::TYPE_STRING;
}
return parent::dataTypeForValue($pValue);
}
}
Cell::setValueBinder(new MyValueBinder());
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$users = [
['姓名', '身份证号'],
['莱德', '120110202001019001'],
['阿奇', '120110202001019002'],
];
$sheet->fromArray($users);
$sheet->setCellValue('A4', '毛毛');
$sheet->setCellValue('B4', '120110202001019003');
$writer = new Xlsx($spreadsheet);
$writer->save('users.xlsx');
结果
实现说明:
这里自定义了类 MyValueBinder,扩展了 DefaultValueBinder,“重写”了方法 dataTypeForValue。
// 此处判断只提供一种实现思路,只是简单判断长度,可优化。
if (strlen($pValue) == 18) {
return DataType::TYPE_STRING;
}
参考资料
(全文完)