Categories
PHP

PhpSpreadsheet(PHP Excel 类库)正确显示身份证号的解决办法

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;
}

参考资料

(全文完)

扫码阅读和分享

Leave a Reply

Your email address will not be published. Required fields are marked *