1.安装:
$composer require phpoffice/phpspreadsheet
如果您想查看文档和示例,执行:
$composer require phpoffice/phpspreadsheet --prefer-sourc
如果您在与将要部署的服务器不同的PHP版本的开发机器上构建安装,或者如果您的PHP CLI版本与您的运行时(如PHP fpm或Apache的mod_PHP)不同,那么您可能需要在安装之前将以下内容添加到您的composer.json中:
{
"require": {
"phpoffice/phpspreadsheet": "^1.23"
},
"config": {
"platform": {
"php": "7.4"
}
}
}
$composer install
2.简单的demo:
require 'vendor/autoload.php';
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$activeWorksheet = $spreadsheet->getActiveSheet();
$activeWorksheet->setCellValue('A1', 'Hello World !');#注A1 A表示列 1表示行
$writer = new Xlsx($spreadsheet);
$writer->save('hello world.xlsx');
3.示例可以直接从命令行运行,例如:
$php vendor/phpoffice/phpspreadsheet/samples/Basic/01_Simple.php
4.编码问题
PhpSpreadsheet中的所有文本都必须使用UTF-8编码。如果脚本使用不同的编码,那么可以使用PHP的iconv()或mb_convert_encoding()函数转换这些文本。
5.Fatal error: Allowed memory size of xxx bytes exhausted (tried to allocate yyy bytes) in zzz on line aaa
PhpSpreadsheet保存电子表格的“内存中”表示,因此它容易受到PHP内存限制的影响。通过在PHP.ini文件中编辑memory_limit指令的值,或者在代码中使用ini_set('memory_limit','128M'),可以增加PHP可用的内存。
5.Protection on my worksheet is not working?
当您使用任何工作表保护功能(例如,单元格范围保护、禁止删除行等)时,请确保启用工作表安全。例如,可以这样做
$spreadsheet->getActiveSheet()->getProtection()->setSheet(true);
6.Excel2003中似乎没有使用兼容包计算公式?
这是兼容包的正常行为,Xlsx正确显示了这一点。如果您确实需要计算值,或者在Excel2003中强制重新计算,请使用\PhpOffice\PhpSpreadsheet\Writer\Xls。
7.合并单元格
$activeWorksheet->mergeCells('A1:I1');
8.设置单元格值
// Set cell A1 with a string value
$spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet');
// Set cell A2 with a numeric value
$spreadsheet->getActiveSheet()->setCellValue('A2', 12345.6789);
// Set cell A3 with a boolean value
$spreadsheet->getActiveSheet()->setCellValue('A3', TRUE);
// Set cell A4 with a formula
$spreadsheet->getActiveSheet()->setCellValue(
'A4',
'=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
);
注: '=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
当A3值存在时A4显示CONCATENATE(A1, " ", A2)否则显示CONCATENATE(A2, " ", A1))',
CONCATENATE(A2, " ", A1))'表示将A2与A1以空格拼接,更多公式请学习excel。
或者,您可以检索单元格对象,然后调用单元格的setValue()方法:
$spreadsheet->getActiveSheet()
->getCell('B8')
->setValue('Some value');
9.创建新单元格
如果您调用getCell(),但该单元格还不存在,那么PhpSpreadsheet将为您创建该单元格。
10.BEWARE:作为分离引用分配给变量的单元格
作为一种“内存中”模型,PHPSpreadsheet可能对内存要求很高,尤其是在处理大型电子表格时。用于减少这种内存开销的一种技术是单元格缓存,因此在使用电子表格时,单元格实际上被维护在一个集合中,该集合可能保存在内存中,也可能不保存在内存。因此,对getCell()(或任何类似方法)的调用都会返回单元格数据,并设置指向集合中该单元格的单元格指针。虽然这通常不是一个问题,但如果将getCell()调用的结果分配给变量,则可能会变得很重要。任何检索其他单元格的后续调用都将更改该指针,尽管单元格对象仍将保留其数据值。
这是什么意思?考虑以下代码:
$spreadSheet = new Spreadsheet();
$workSheet = $spreadSheet->getActiveSheet();
// 设置要评估的公式的详细信息,以及它所依赖的任何数据
$workSheet->fromArray(
[1, 2, 3],
null,
'A1'
);
$cellC1 = $workSheet->getCell('C1');
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;
#结果:Value: 3; Address: C1
$cellA1 = $workSheet->getCell('A1');
echo 'Value: ', $cellA1->getValue(), '; Address: ', $cellA1->getCoordinate(), PHP_EOL;
#结果:Value: 1; Address: A1
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;
#结果:Value: 3; Address:
Fatal error: Uncaught PhpOffice\PhpSpreadsheet\Exception: Coordinate no longer exists in .....
对getCell('C1')的调用返回C1处的单元格,该单元格包含其值(3),以及其到集合的链接(用于标识其地址/坐标C1)。访问小区A1的后续调用修改$cellC1的值,从而分离其到集合的链接。
因此,当我们尝试第二次显示值和地址时,我们可以显示其值,但尝试显示其地址/坐标会引发异常,因为该链接已设置为null。
注意:有些内部方法会从集合中提取其他单元格,这也会将指向集合的链接从您可能分配给变量的任何单元格中分离出来
11.MS Excel支持7种基本数据类型:
string
number
boolean
null
formula
error
Inline (or rich text) string
默认情况下,当您调用工作表的setCellValue()方法或单元格的setValue()法时,PhpSpreadsheet将为PHP null、boolean、float或integers使用适当的数据类型;或者将传递给方法的任何字符串数据值强制转换为最合适的数据类型,这样数字字符串将被强制转换为数字,而以=开头的字符串值将被转换为公式。非数字字符串或不是以=开头的字符串将被视为真正的字符串值。
请注意,以前导零开头的数字字符串(后面不紧跟十进制分隔符)不会转换为数字,因此电话号码等值(例如“01615991375”)将保留为字符串。
这种“转换”由一个单元格“值绑定器”处理,您可以编写自定义的“值绑定”来改变这些“转换”的行为。标准PhpSpreadsheet包还提供了一个“高级值绑定器”,可以处理许多更复杂的转换,例如将“3/4”等分数格式的字符串转换为数值(在这种情况下为0.75),并设置适当的“分数”数字格式掩码。类似地,像“5%”这样的字符串将被转换为0.05的值,并应用百分比数字格式掩码,包含看起来像日期的值的字符串将转换为Excel序列化的日期时间戳值,并使用相应的掩码。这在从csv文件加载数据或从数据库设置单元格值时特别有用。
高级价值绑定处理的格式包括:
TRUE或FALSE(取决于区域设置)将转换为布尔值。
以科学(指数)格式标识的数字字符串将转换为数字。
分数和粗俗分数被转换为数字,并应用适当的数字格式掩码。
百分比被转换为数字,除以100,并应用适当的数字格式掩码。
日期和时间转换为Excel时间戳值(数字),并应用适当的数字格式掩码。
如果字符串包含换行符(\n),则单元格样式设置为换行。
基本上,它试图模仿MS Excel GUI的行为。
12.在单元格中设置公式
如上所述,如果在单元格中存储第一个字符为an=的字符串值。PHPSpreadsheet将该值视为一个公式,然后您可以通过对单元格调用getCalculatedValue()来计算该公式。
不过,有时您可能希望将以=开头的值存储为字符串,并且不希望PHPSpreadsheet像公式一样进行计算。
要做到这一点,您需要通过将该值设置为“带引号的文本”来“转义”该值。
// 使用公式设置单元格A4
$spreadsheet->getActiveSheet()->setCellValue(
'A4',
'=IF(A3, CONCATENATE(A1, " ", A2), CONCATENATE(A2, " ", A1))'
);
$spreadsheet->getActiveSheet()->getCell('A4')
->getStyle()->setQuotePrefix(true);#将会以公式的格式显示在A4
然后,即使您要求PHPSpreadsheet返回单元格A4的计算值,它也会以字符串形式返回=if(A3,CONCATNATE(A1,“”,A2),CONCATENATE(A2,“”、A1)),而不尝试计算公式
13.在单元格中设置日期或时间值
日期或时间值在Excel中作为时间戳(一个简单的浮点值),并使用数字格式掩码来显示该值的格式;因此,如果我们想在单元格中存储日期,我们需要计算正确的Excel时间戳,并设置数字格式掩码。
// 获取当前日期/时间并转换为Excel日期/时间
$dateTimeNow = time();
$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow );
//$excelDateValue=45023.133333333 ???
// 使用Excel日期/时间值设置单元格A6
$spreadsheet->getActiveSheet()->setCellValue(
'A6',
$excelDateValue
);
// 设置数字格式掩码,使excel时间戳显示为人类可读的日期/时间
$spreadsheet->getActiveSheet()->getStyle('A6')
->getNumberFormat()
->setFormatCode(
\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME
);
14.设置以零开头的数字
默认情况下,PhpSpreadsheet将自动检测值类型,并将其设置为适当的Excel数字数据类型。这种类型转换由价值绑定器处理,如本文件标题为“使用价值绑定器促进数据输入”的部分所述。
数字没有前导零,所以如果你试图设置一个有前导零的数值(比如电话号码),那么当该值被转换为数字时,这些数字通常会丢失,所以“01513789642”将显示为1513789642。
有两种方法可以强制PhpSpreadsheet覆盖此行为。
首先,可以将数据类型显式设置为字符串,这样就不会将其转换为数字。
// 用数值设置单元格A8,但告诉PhpSpreadsheet它应该被视为字符串
$spreadsheet->getActiveSheet()->setCellValueExplicit(
'A8',
"01513789642",
\PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING
);
或者,您可以使用数字格式掩码来显示以零开头的值。
// 用数值设置单元格A9
$spreadsheet->getActiveSheet()->setCellValue('A9', 1513789642);
// 设置一个数字格式掩码,将值显示为11位带前导零的数字
$spreadsheet->getActiveSheet()->getStyle('A9')
->getNumberFormat()
->setFormatCode(
'00000000000'
);
通过数字格式屏蔽,您甚至可以将数字分组,使值更易于读取。
// 用数值设置单元格A10
$spreadsheet->getActiveSheet()->setCellValue('A10', 1513789642);
//设置一个数字格式掩码,将值显示为11位带前导零的数字
$spreadsheet->getActiveSheet()->getStyle('A10')
->getNumberFormat()
->setFormatCode(
'0000-000-0000'
);
并非所有复杂的格式掩码(如此掩码)在检索格式化值以“在屏幕上”显示时都能工作,或者对于某些编写器(如HTML或PDF)也能工作,但它可以与真正的电子表格编写器(Xlsx和Xls)一起工作。
15.设置数组中的单元格范围
还可以通过将一个值数组传递给fromArray()方法,在单个调用中设置单元格值的范围。
$arrayData = [
[NULL, 2010, 2011, 2012],
['Q1', 12, 15, 21],
['Q2', 56, 73, 86],
['Q3', 52, 61, 69],
['Q4', 30, 32, 0],
];
$spreadsheet->getActiveSheet()
->fromArray(
$arrayData, // 要设置的数据
NULL, // 不会设置具有此值的数组值
'C3' // 工作表区域的左上角坐标,我们想要设置这些值(默认值为A1)
);
E3~H0)CG05J`A1)YI6VK})6.png
如果您传递一个二维数组,那么它将被视为一系列的行和列。一个一维数组将被视为一行,如果您要从数据库中获取一个数据数组,这一点特别有用。
$rowArray = ['10', '20', '30', '40'];
$spreadsheet->getActiveSheet()
->fromArray(
$rowArray,
NULL,
'C3'
);
3.png
如果你有一个简单的一维数组,并且想把它写为一列,那么下面将把它转换成一个结构适当的二维数组,可以输入fromArray()方法:
$rowArray = ['10', '20', '30', '40'];
$columnArray = array_chunk($rowArray, 1);
$spreadsheet->getActiveSheet()
->fromArray(
$columnArray,
NULL,
'C3'
);
4.png
16.按坐标检索单元格值
要检索单元格的值,应首先使用getCell()方法从工作表中检索该单元格。可以使用getValue()方法读取单元格的值。
// Get the value from cell A1
$cellValue = $spreadsheet->getActiveSheet()->getCell('A1')->getValue();
这将检索单元格中包含的未格式化的原始值。
如果单元格包含公式,并且需要检索计算值而不是公式本身,则使用该单元格的getCalculatedValue()方法。这将在计算引擎中进一步解释。
// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCell('A4')->getCalculatedValue();
或者,如果您想查看应用了任何单元格格式的值(例如,对于人类可读的日期或时间值),则可以使用单元格的getFormattedValue()方法。
// Get the value from cell A6
$cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue();
17.按列和行设置单元格值
可以使用工作表的setCellValueByColumnAndRow()方法按坐标设置单元格值。
// Set cell A5 with a string value
$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet');
注意:对于列A,列引用以1开头。
18.按列和行检索单元格值
要检索单元格的值,应首先使用getCellByColumnAndRow()方法从工作表中检索该单元格。可以使用以下代码行再次读取单元格的值:
// Get the value from cell B5
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getValue();
如果需要单元格的计算值,请使用以下代码:
// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 4)->getCalculatedValue();
19.检索数组的单元格值范围
也可以使用toArray()、rangeToArray()或namedRangeToArray()方法在单个调用中检索数组的单元格值范围。
$dataArray = $spreadsheet->getActiveSheet()
->rangeToArray(
'C3:E5', // 我们要检索的工作表区域
NULL, // 应为空单元格返回的值
TRUE, // 是否应计算公式(相当于每个单元格的getCalculatedValue())
TRUE, // 是否应格式化值(相当于每个单元格的getFormattedValue())
TRUE // 数组是否应按单元格行和单元格列进行索引
);
这些方法都将返回行和列的二维数组。toArray()方法将返回整个工作表;rangeToArray()将返回指定的范围或单元格;而namedRangeToArray()将返回定义的命名范围内的单元格。
20.使用迭代器在单元格中循环
循环单元格最简单的方法是使用迭代器。使用迭代器,可以使用foreach循环工作表、工作表中的行和行中的单元格。
下面是一个例子,我们读取工作表中的所有值,并将其显示在表格中。
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
$worksheet = $spreadsheet->getActiveSheet();
echo '
| ' .
$cell->getValue() . ' | ' . PHP_EOL;
请注意,我们已经将单元格迭代器的setIterateOnlyExistingCells()设置为FALSE。这使得迭代器循环工作表范围内的所有单元格,即使这些单元格尚未设置。
如果工作表中不存在空单元格,则单元格迭代器将在工作表中创建一个新的空单元格;如果工作表中未设置null,则返回null作为单元格值;尽管我们也可以告诉它返回一个null值,而不是返回一个新的空单元格。将单元格迭代器的setIterateOnlyExistingCells()设置为false将循环工作表中此时可用的所有单元格。如果将其设置为在需要时创建新的单元格,则可能会增加内存使用量!只有在打算循环所有可能可用的单元格时才使用它;否则,如果一个单元格不存在,则使用该选项返回null值,或者只迭代已经存在的单元格。
也可以调用Row对象的isEmpty()方法来确定是否需要实例化该Row的Cell迭代器。
21.使用索引在单元格中循环
可以使用通过列和行索引访问单元格值的可能性,如[1,1],而不是“A1”,用于在循环中读取和写入单元格值。
注:在PhpSpreadsheet中,列索引和行索引以1为基础。这意味着‘A1’~[1,1]
下面是一个例子,我们读取工作表中的所有值,并将其显示在表格中。
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
$worksheet = $spreadsheet->getActiveSheet();
// 获取工作表中引用的最高行数和列数
$highestRow = $worksheet->getHighestRow(); // e.g. 10
$highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn); // e.g. 5
echo '
| ' . $value . ' | ' . PHP_EOL;
或者,您可以利用PHP的“Perl风格”字符增量器,通过坐标在单元格中循环:
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
$worksheet = $spreadsheet->getActiveSheet();
// 获取工作表中引用的最高行号和列号
$highestRow = $worksheet->getHighestRow(); // e.g. 10
$highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
//增加最高列字母
$highestColumn++;
echo '
| ' .
$worksheet->getCell($col . $row) ->getValue() . ' | ' . PHP_EOL;
请注意,我们不能在这里使用<=比较,因为“AA”将匹配为<=“B”,所以我们递增最高的列字母,然后循环,同时$col!=递增的最高列。
21.使用值绑定器方便数据输入
在内部,PhpSpreadsheet使用默认的\PhpOffice\PhpSpreadsheet\Cell\IValueBinder实现(\PhpOffice\PhpSpredsheet\Cell\DefaultValueBinder)来使用单元格的setValue()方法确定输入数据的数据类型(setValueExplicit()方法绕过此检查)。
可选地,可以修改PhpSpreadsheet的默认行为,从而更容易地输入数据。例如,可以使用\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder类。它会自动将百分比、科学格式的数字和以字符串形式输入的日期转换为正确的格式,并设置单元格的样式信息。以下示例演示了如何在PhpSpreadsheet中设置值绑定器:
/** PhpSpreadsheet */
require_once 'src/Boostrap.php';
// Set value binder
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );
// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
//添加一些数据,类似于一些不同的数据类型
$spreadsheet->getActiveSheet()->setCellValue('A4', 'Percentage value:');
// 将字符串值转换为0.1并设置百分比单元格样式
$spreadsheet->getActiveSheet()->setCellValue('B4', '10%');
$spreadsheet->getActiveSheet()->setCellValue('A5', 'Date/time value:');
// 将字符串值转换为Excel日期戳,并设置日期格式单元格样式
$spreadsheet->getActiveSheet()->setCellValue('B5', '21 December 1983');
或者,如果要将所有内容保留为字符串,则可以使用\PhpOffice\PhpSpreadsheet\Cell\StringValueBinder类。如果您加载的文件包含可以被解释为数字的值(例如,带前导号的数字,如+4441615579382等国际电话号码),但应保留为字符串(带前导零的非国际电话号码已保留为字符串),则这可能是合适的。
默认情况下,StringValueBinder会将传递给它的任何数据类型强制转换为字符串。然而,有许多设置允许您指定某些数据类型不应强制转换为字符串,而是保持“原样”:
$stringValueBinder = new \PhpOffice\PhpSpreadsheet\Cell\StringValueBinder();
$stringValueBinder->setNumericConversion(false)
->setBooleanConversion(false)
->setNullConversion(false)
->setFormulaConversion(false);
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( $stringValueBinder );
在设置单个单元格值时,可以通过指定要在单元格的setValue()或工作表的setCellValue()方法中使用的不同绑定器来覆盖当前绑定器。
$spreadsheet = new Spreadsheet();
Cell::setValueBinder(new AdvancedValueBinder());
$value = '12.5%';
$cell = $spreadsheet->getActiveSheet()->getCell('A1');
// 值将设置为数字0.125,格式掩码为“0.00%”
$cell->setValue($value); // Using the Advanced Value Binder
$cell = $spreadsheet->getActiveSheet()->getCell('A2');
// Value will be set as a string '12.5%' with a format mask 'General'
$cell->setValue($value, new StringValueBinder()); // Overriding the Advanced Value Binder
22.创建自己的值绑定
创建自己的价值绑定相对简单。当需要更专业的值绑定时,您可以实现\PhpOffice\PhpSpreadsheet\Cell\IValueBinder接口,或者扩展现有的\PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder或\PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder类。
官网文档:https://phpspreadsheet.readthedocs.io/en/latest/