diff --git a/.travis.yml b/.travis.yml
index 31ae804e..05e964a1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,7 +38,7 @@ jobs:
- php ocular.phar code-coverage:upload --format=php-clover tests/coverage-clover.xml
- stage: API documentations
- if: tag is present AND branch = master
+ if: branch = master
php: 7.4
before_script:
- curl -LO https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.0.0-rc/phpDocumentor.phar
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22fd8c3d..0c139850 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,12 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).
-## [Unreleased]
+## [1.13.0] - 2020-05-31
### Added
- Support writing to streams in all writers [#1292](https://github.com/PHPOffice/PhpSpreadsheet/issues/1292)
- Support CSV files with data wrapping a lot of lines [#1468](https://github.com/PHPOffice/PhpSpreadsheet/pull/1468)
+- Support protection of worksheet by a specific hash algorithm [#1485](https://github.com/PHPOffice/PhpSpreadsheet/pull/1485)
### Fixed
@@ -21,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464)
- Fix incorrect behaviour when saving XLSX file with drawings [#1462](https://github.com/PHPOffice/PhpSpreadsheet/pull/1462),
- Fix Crash while trying setting a cell the value "123456\n" [#1476](https://github.com/PHPOffice/PhpSpreadsheet/pull/1481)
+- Improved DATEDIF() function and reduced errors for Y and YM units [#1466](https://github.com/PHPOffice/PhpSpreadsheet/pull/1466)
+- Stricter typing for mergeCells [#1494](https://github.com/PHPOffice/PhpSpreadsheet/pull/1494)
### Changed
diff --git a/docs/faq.md b/docs/faq.md
index 19f5f8fc..ac69e415 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -23,7 +23,7 @@ When you make use of any of the worksheet protection features (e.g. cell
range protection, prohibiting deleting rows, ...), make sure you enable
worksheet security. This can for example be done like this:
-``` php
+```php
$spreadsheet->getActiveSheet()->getProtection()->setSheet(true);
```
diff --git a/docs/topics/accessing-cells.md b/docs/topics/accessing-cells.md
index 4770d721..edb71514 100644
--- a/docs/topics/accessing-cells.md
+++ b/docs/topics/accessing-cells.md
@@ -8,7 +8,7 @@ topic lists some of the options to access a cell.
Setting a cell value by coordinate can be done using the worksheet's
`setCellValue()` method.
-``` php
+```php
// Set cell A1 with a string value
$spreadsheet->getActiveSheet()->setCellValue('A1', 'PhpSpreadsheet');
@@ -28,7 +28,7 @@ $spreadsheet->getActiveSheet()->setCellValue(
Alternatively, you can retrieve the cell object, and then call the
cell’s `setValue()` method:
-``` php
+```php
$spreadsheet->getActiveSheet()
->getCell('B8')
->setValue('Some value');
@@ -56,7 +56,7 @@ the cell object will still retain its data values.
What does this mean? Consider the following code:
-```
+```php
$spreadSheet = new Spreadsheet();
$workSheet = $spreadSheet->getActiveSheet();
@@ -74,7 +74,7 @@ $cellA1 = $workSheet->getCell('A1');
echo 'Value: ', $cellA1->getValue(), '; Address: ', $cellA1->getCoordinate(), PHP_EOL;
echo 'Value: ', $cellC1->getValue(), '; Address: ', $cellC1->getCoordinate(), PHP_EOL;
-```
+```
The call to `getCell('C1')` returns the cell at `C1` containing its value (`3`),
together with its link to the collection (used to identify its
@@ -153,7 +153,7 @@ was a formula.
To do this, you need to "escape" the value by setting it as "quoted text".
-```
+```php
// Set cell A4 with a formula
$spreadsheet->getActiveSheet()->setCellValue(
'A4',
@@ -175,7 +175,7 @@ point value), and a number format mask is used to show how that value
should be formatted; so if we want to store a date in a cell, we need to
calculate the correct Excel timestamp, and set a number format mask.
-``` php
+```php
// Get the current date/time and convert to an Excel date/time
$dateTimeNow = time();
$excelDateValue = \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $dateTimeNow );
@@ -210,7 +210,7 @@ behaviour.
Firstly, you can set the datatype explicitly as a string so that it is
not converted to a number.
-``` php
+```php
// Set cell A8 with a numeric value, but tell PhpSpreadsheet it should be treated as a string
$spreadsheet->getActiveSheet()->setCellValueExplicit(
'A8',
@@ -222,7 +222,7 @@ $spreadsheet->getActiveSheet()->setCellValueExplicit(
Alternatively, you can use a number format mask to display the value
with leading zeroes.
-``` php
+```php
// Set cell A9 with a numeric value
$spreadsheet->getActiveSheet()->setCellValue('A9', 1513789642);
// Set a number format mask to display the value as 11 digits with leading zeroes
@@ -236,7 +236,7 @@ $spreadsheet->getActiveSheet()->getStyle('A9')
With number format masking, you can even break up the digits into groups
to make the value more easily readable.
-``` php
+```php
// Set cell A10 with a numeric value
$spreadsheet->getActiveSheet()->setCellValue('A10', 1513789642);
// Set a number format mask to display the value as 11 digits with leading zeroes
@@ -259,7 +259,7 @@ writers (Xlsx and Xls).
It is also possible to set a range of cell values in a single call by
passing an array of values to the `fromArray()` method.
-``` php
+```php
$arrayData = [
[NULL, 2010, 2011, 2012],
['Q1', 12, 15, 21],
@@ -282,7 +282,7 @@ If you pass a 2-d array, then this will be treated as a series of rows
and columns. A 1-d array will be treated as a single row, which is
particularly useful if you're fetching an array of data from a database.
-``` php
+```php
$rowArray = ['Value1', 'Value2', 'Value3', 'Value4'];
$spreadsheet->getActiveSheet()
->fromArray(
@@ -299,7 +299,7 @@ If you have a simple 1-d array, and want to write it as a column, then
the following will convert it into an appropriately structured 2-d array
that can be fed to the `fromArray()` method:
-``` php
+```php
$rowArray = ['Value1', 'Value2', 'Value3', 'Value4'];
$columnArray = array_chunk($rowArray, 1);
$spreadsheet->getActiveSheet()
@@ -319,7 +319,7 @@ To retrieve the value of a cell, the cell should first be retrieved from
the worksheet using the `getCell()` method. A cell's value can be read
using the `getValue()` method.
-``` php
+```php
// Get the value from cell A1
$cellValue = $spreadsheet->getActiveSheet()->getCell('A1')->getValue();
```
@@ -331,7 +331,7 @@ value rather than the formula itself, then use the cell's
`getCalculatedValue()` method. This is further explained in
[the calculation engine](./calculation-engine.md).
-``` php
+```php
// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCell('A4')->getCalculatedValue();
```
@@ -340,7 +340,7 @@ Alternatively, if you want to see the value with any cell formatting
applied (e.g. for a human-readable date or time value), then you can use
the cell's `getFormattedValue()` method.
-``` php
+```php
// Get the value from cell A6
$cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue();
```
@@ -350,7 +350,7 @@ $cellValue = $spreadsheet->getActiveSheet()->getCell('A6')->getFormattedValue();
Setting a cell value by coordinate can be done using the worksheet's
`setCellValueByColumnAndRow()` method.
-``` php
+```php
// Set cell A5 with a string value
$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow(1, 5, 'PhpSpreadsheet');
```
@@ -363,7 +363,7 @@ To retrieve the value of a cell, the cell should first be retrieved from
the worksheet using the `getCellByColumnAndRow()` method. A cell’s value can
be read again using the following line of code:
-``` php
+```php
// Get the value from cell B5
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getValue();
```
@@ -371,7 +371,7 @@ $cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(2, 5)->getVal
If you need the calculated value of a cell, use the following code. This
is further explained in [the calculation engine](./calculation-engine.md).
-``` php
+```php
// Get the value from cell A4
$cellValue = $spreadsheet->getActiveSheet()->getCellByColumnAndRow(1, 4)->getCalculatedValue();
```
@@ -382,7 +382,7 @@ It is also possible to retrieve a range of cell values to an array in a
single call using the `toArray()`, `rangeToArray()` or
`namedRangeToArray()` methods.
-``` php
+```php
$dataArray = $spreadsheet->getActiveSheet()
->rangeToArray(
'C3:E5', // The worksheet range that we want to retrieve
@@ -409,7 +409,7 @@ cells within a row.
Below is an example where we read all the values in a worksheet and
display them in a table.
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
@@ -456,7 +456,7 @@ loops.
Below is an example where we read all the values in a worksheet and
display them in a table.
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
@@ -482,7 +482,7 @@ echo '' . PHP_EOL;
Alternatively, you can take advantage of PHP's "Perl-style" character
incrementors to loop through the cells by coordinate:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(TRUE);
$spreadsheet = $reader->load("test.xlsx");
@@ -528,7 +528,7 @@ dates entered as strings to the correct format, also setting the cell's
style information. The following example demonstrates how to set the
value binder in PhpSpreadsheet:
-``` php
+```php
/** PhpSpreadsheet */
require_once 'src/Boostrap.php';
diff --git a/docs/topics/architecture.md b/docs/topics/architecture.md
index 0295d672..1c544ef7 100644
--- a/docs/topics/architecture.md
+++ b/docs/topics/architecture.md
@@ -43,7 +43,7 @@ PhpSpreadsheet supports fluent interfaces in most locations. This means
that you can easily "chain" calls to specific methods without requiring
a new PHP statement. For example, take the following code:
-``` php
+```php
$spreadsheet->getProperties()->setCreator("Maarten Balliauw");
$spreadsheet->getProperties()->setLastModifiedBy("Maarten Balliauw");
$spreadsheet->getProperties()->setTitle("Office 2007 XLSX Test Document");
@@ -55,7 +55,7 @@ $spreadsheet->getProperties()->setCategory("Test result file");
This can be rewritten as:
-``` php
+```php
$spreadsheet->getProperties()
->setCreator("Maarten Balliauw")
->setLastModifiedBy("Maarten Balliauw")
diff --git a/docs/topics/autofilters.md b/docs/topics/autofilters.md
index 66321ee9..d5a07f8b 100644
--- a/docs/topics/autofilters.md
+++ b/docs/topics/autofilters.md
@@ -42,7 +42,7 @@ column, such as "Equals a red cell color" or "Larger than 150".
To set an autoFilter on a range of cells.
-``` php
+```php
$spreadsheet->getActiveSheet()->setAutoFilter('A1:E20');
```
@@ -56,7 +56,7 @@ developer to avoid such errors.
If you want to set the whole worksheet as an autofilter region
-``` php
+```php
$spreadsheet->getActiveSheet()->setAutoFilter(
$spreadsheet->getActiveSheet()
->calculateWorksheetDimension()
@@ -74,7 +74,7 @@ will extend this to other formats.
To apply a filter expression to an autoFilter range, you first need to
identify which column you're going to be applying this filter to.
-``` php
+```php
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
$columnFilter = $autoFilter->getColumn('C');
```
@@ -114,7 +114,7 @@ To create a filter expression, we need to start by identifying the
filter type. In this case, we're just going to specify that this filter
is a standard filter.
-``` php
+```php
$columnFilter->setFilterType(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER
);
@@ -127,7 +127,7 @@ When creating a simple filter in PhpSpreadsheet, you only need to
specify the values for "checked" columns: you do this by creating a
filter rule for each value.
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
@@ -152,7 +152,7 @@ standard filters are always treated as being joined by an OR condition.
If you want to create a filter to select blank cells, you would use:
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
@@ -170,7 +170,7 @@ within a year, or individual days within each month.
DateGroup filters are still applied as a Standard Filter type.
-``` php
+```php
$columnFilter->setFilterType(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_FILTER
);
@@ -181,7 +181,7 @@ for "checked" columns as an associative array of year. month, day, hour
minute and second. To select a year and month, you need to create a
DateGroup rule identifying the selected year and month:
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
@@ -229,7 +229,7 @@ either an AND or an OR.
We start by specifying a Filter type, this time a CUSTOMFILTER.
-``` php
+```php
$columnFilter->setFilterType(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER
);
@@ -240,7 +240,7 @@ And then define our rules.
The following shows a simple wildcard filter to show all column entries
beginning with the letter `U`.
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
@@ -264,7 +264,7 @@ is the \~ itself.
To create a "between" condition, we need to define two rules:
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL,
@@ -289,7 +289,7 @@ This defined two rules, filtering numbers that are `>= -20` OR `<=
20`, so we also need to modify the join condition to reflect AND rather
than OR.
-``` php
+```php
$columnFilter->setAndOr(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_ANDOR_AND
);
@@ -320,7 +320,7 @@ column at a time.
Again, we start by specifying a Filter type, this time a DYNAMICFILTER.
-``` php
+```php
$columnFilter->setFilterType(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER
);
@@ -330,7 +330,7 @@ When defining the rule for a dynamic filter, we don't define a value (we
can simply set that to NULL) but we do specify the dynamic filter
category.
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
@@ -420,7 +420,7 @@ column at a time.
We start by specifying a Filter type, this time a DYNAMICFILTER.
-``` php
+```php
$columnFilter->setFilterType(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER
);
@@ -428,7 +428,7 @@ $columnFilter->setFilterType(
Then we create the rule:
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT,
@@ -444,7 +444,7 @@ This will filter the Top 5 percent of values in the column.
To specify the lowest (bottom 2 values), we would specify a rule of:
-``` php
+```php
$columnFilter->createRule()
->setRule(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE,
@@ -490,7 +490,7 @@ If you wish to execute your filter from within a script, you need to do
this manually. You can do this using the autofilters `showHideRows()`
method.
-``` php
+```php
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
$autoFilter->showHideRows();
```
@@ -505,7 +505,7 @@ ever row, whether it matches the filter criteria or not. To selectively
access only the filtered rows, you need to test each row’s visibility
settings.
-``` php
+```php
foreach ($spreadsheet->getActiveSheet()->getRowIterator() as $row) {
if ($spreadsheet->getActiveSheet()
->getRowDimension($row->getRowIndex())->getVisible()) {
diff --git a/docs/topics/calculation-engine.md b/docs/topics/calculation-engine.md
index 779d73e1..d15dbb16 100644
--- a/docs/topics/calculation-engine.md
+++ b/docs/topics/calculation-engine.md
@@ -13,7 +13,7 @@ evaluates to the sum of values in A1, A2, ..., A10.
To calculate a formula, you can call the cell containing the formula’s
method `getCalculatedValue()`, for example:
-``` php
+```php
$spreadsheet->getActiveSheet()->getCell('E11')->getCalculatedValue();
```
@@ -32,7 +32,7 @@ You see that the formula contained in cell E11 is "SUM(E4:E9)". Now,
when I write the following line of code, two new product lines are
added:
-``` php
+```php
$spreadsheet->getActiveSheet()->insertNewRowBefore(7, 2);
```
@@ -55,7 +55,7 @@ However, there may be times when you don't want this, perhaps you've changed
the underlying data and need to re-evaluate the same formula with that new
data.
-```
+```php
Calculation::getInstance($spreadsheet)->disableCalculationCache();
```
@@ -63,7 +63,7 @@ Will disable calculation caching, and flush the current calculation cache.
If you want only to flush the cache, then you can call
-```
+```php
Calculation::getInstance($spreadsheet)->clearCalculationCache();
```
@@ -118,7 +118,7 @@ date values by calling the
`\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType()`
method:
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType($returnDateType);
```
@@ -134,7 +134,7 @@ if an invalid value is passed in for the return date type).
The `\PhpOffice\PhpSpreadsheet\Calculation\Functions::getReturnDateType()`
method can be used to determine the current value of this setting:
-``` php
+```php
$returnDateType = \PhpOffice\PhpSpreadsheet\Calculation\Functions::getReturnDateType();
```
@@ -172,7 +172,7 @@ It is possible for scripts to change the calendar used for calculating
Excel date values by calling the
`\PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar()` method:
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Shared\Date::setExcelCalendar($baseDate);
```
@@ -187,7 +187,7 @@ if an invalid value is passed in).
The `\PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar()` method can
be used to determine the current value of this setting:
-``` php
+```php
$baseDate = \PhpOffice\PhpSpreadsheet\Shared\Date::getExcelCalendar();
```
@@ -353,7 +353,7 @@ This is the statistical mean.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -421,7 +421,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -492,7 +492,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -563,7 +563,7 @@ in which you specify a condition for the column.
#### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -631,7 +631,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -699,7 +699,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -767,7 +767,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -836,7 +836,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -905,7 +905,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -973,7 +973,7 @@ in which you specify a condition for the column.
##### Examples
-``` php
+```php
$database = [
[ 'Tree', 'Height', 'Age', 'Yield', 'Profit' ],
[ 'Apple', 18, 20, 14, 105.00 ],
@@ -1074,7 +1074,7 @@ or an Excel timestamp value (real), depending on the value of
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Year')
->setCellValue('A2', 'Month')
->setCellValue('A3', 'Day');
@@ -1089,7 +1089,7 @@ $retVal = $worksheet->getCell('D1')->getCalculatedValue();
// $retVal = 1230681600
```
-``` php
+```php
// We're going to be calling the same cell calculation multiple times,
// and expecting different return values, so disable calculation cacheing
\PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->setCalculationCacheEnabled(FALSE);
@@ -1170,7 +1170,7 @@ the third parameter.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Year')
->setCellValue('A2', 'Month')
->setCellValue('A3', 'Day');
@@ -1208,7 +1208,7 @@ $retVal = $worksheet->getCell('D6')->getCalculatedValue();
// $retVal = 30
```
-``` php
+```php
$date1 = 1193317015; // PHP timestamp for 25-Oct-2007
$date2 = 1449579415; // PHP timestamp for 8-Dec-2015
@@ -1279,7 +1279,7 @@ or an Excel timestamp value (real), depending on the value of
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String');
->setCellValue('A2', '31-Dec-2008')
->setCellValue('A3', '31/12/2008')
@@ -1301,7 +1301,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue();
// $retVal = 39813.0 for all cases
```
-``` php
+```php
// We're going to be calling the same cell calculation multiple times,
// and expecting different return values, so disable calculation cacheing
\PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->setCalculationCacheEnabled(FALSE);
@@ -1371,7 +1371,7 @@ This is an integer ranging from 1 to 31.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String')
->setCellValue('A2', '31-Dec-2008')
->setCellValue('A3', '14-Feb-2008');
@@ -1386,7 +1386,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue();
// $retVal = 14
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'DAYOFMONTH'],
['25-Dec-2008']
@@ -1444,7 +1444,7 @@ day year.
##### Examples
-``` php
+```php
$worksheet->setCellValue('B1', 'Start Date')
->setCellValue('C1', 'End Date')
->setCellValue('A2', 'Year')
@@ -1469,7 +1469,7 @@ $retVal = $worksheet->getCell('E4')->getCalculatedValue();
// $retVal = 1557
```
-``` php
+```php
$date1 = 37655.0; // Excel timestamp for 25-Oct-2007
$date2 = 39233.0; // Excel timestamp for 8-Dec-2015
@@ -1529,7 +1529,7 @@ or an Excel timestamp value (real), depending on the value of
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String')
->setCellValue('A2', '1-Jan-2008')
->setCellValue('A3', '29-Feb-2008');
@@ -1548,7 +1548,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue();
// $retVal = 39141.0 (28-Feb-2007)
```
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType(
\PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_EXCEL
);
@@ -1602,7 +1602,7 @@ or an Excel timestamp value (real), depending on the value of
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String')
->setCellValue('A2', '1-Jan-2000')
->setCellValue('A3', '14-Feb-2009');
@@ -1619,7 +1619,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue();
// $retVal = 39507.0 (29-Feb-2008)
```
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Calculation\Functions::setReturnDateType(
\PhpOffice\PhpSpreadsheet\Calculation\Functions::RETURNDATE_EXCEL
);
@@ -1661,7 +1661,7 @@ This is an integer ranging from 0 to 23.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Time String')
->setCellValue('A2', '31-Dec-2008 17:30')
->setCellValue('A3', '14-Feb-2008 4:20 AM')
@@ -1681,7 +1681,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue();
// $retVal = 16
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'HOUROFDAY'],
['09:30']
@@ -1719,7 +1719,7 @@ This is an integer ranging from 0 to 59.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Time String')
->setCellValue('A2', '31-Dec-2008 17:30')
->setCellValue('A3', '14-Feb-2008 4:20 AM')
@@ -1739,7 +1739,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue();
// $retVal = 45
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'MINUTE'],
['09:30']
@@ -1777,7 +1777,7 @@ This is an integer ranging from 1 to 12.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String');
$worksheet->setCellValue('A2', '31-Dec-2008');
$worksheet->setCellValue('A3', '14-Feb-2008');
@@ -1792,7 +1792,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue();
// $retVal = 2
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'MONTHOFYEAR'],
['14-July-2008']
@@ -1847,10 +1847,10 @@ The number of working days between startDate and endDate.
##### Examples
-``` php
+```php
```
-``` php
+```php
```
##### Notes
@@ -1880,10 +1880,10 @@ or an Excel timestamp value (real), depending on the value of
##### Examples
-``` php
+```php
```
-``` php
+```php
```
##### Notes
@@ -1917,7 +1917,7 @@ This is an integer ranging from 0 to 59.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Time String')
->setCellValue('A2', '31-Dec-2008 17:30:20')
->setCellValue('A3', '14-Feb-2008 4:20 AM')
@@ -1937,7 +1937,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue();
// $retVal = 59
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'SECOND'],
['09:30:17']
@@ -2002,7 +2002,7 @@ value of method.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String')
->setCellValue('A2', '31-Dec-2008')
->setCellValue('A3', '14-Feb-2008');
@@ -2021,7 +2021,7 @@ $retVal = $worksheet->getCell('B4')->getCalculatedValue();
// $retVal = 2
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'WEEKDAY'],
['14-July-2008']
@@ -2066,7 +2066,7 @@ This is an integer year value.
##### Examples
-``` php
+```php
$worksheet->setCellValue('A1', 'Date String')
->setCellValue('A2', '17-Jul-1982')
->setCellValue('A3', '16-Apr-2009');
@@ -2081,7 +2081,7 @@ $retVal = $worksheet->getCell('B3')->getCalculatedValue();
// $retVal = 2009
```
-``` php
+```php
$retVal = call_user_func_array(
['\PhpOffice\PhpSpreadsheet\Calculation\Functions', 'YEAR'],
['14-July-2001']
diff --git a/docs/topics/creating-spreadsheet.md b/docs/topics/creating-spreadsheet.md
index dceafe4b..3a82623e 100644
--- a/docs/topics/creating-spreadsheet.md
+++ b/docs/topics/creating-spreadsheet.md
@@ -20,7 +20,7 @@ Details of the different spreadsheet formats supported, and the options
available to read them into a Spreadsheet object are described fully in
the [Reading Files](./reading-files.md) document.
-``` php
+```php
$inputFileName = './sampleData/example1.xls';
/** Load $inputFileName to a Spreadsheet object **/
@@ -32,7 +32,7 @@ $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName);
If you want to create a new workbook, rather than load one from file,
then you simply need to instantiate it as a new Spreadsheet object.
-``` php
+```php
/** Create a new Spreadsheet Object **/
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
```
@@ -53,7 +53,7 @@ then you also need to "break" these cyclic references before doing so.
PhpSpreadsheet provides the `disconnectWorksheets()` method for this
purpose.
-``` php
+```php
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
```
diff --git a/docs/topics/memory_saving.md b/docs/topics/memory_saving.md
index 4c9a848f..157bb704 100644
--- a/docs/topics/memory_saving.md
+++ b/docs/topics/memory_saving.md
@@ -16,7 +16,7 @@ cache usages.
To enable cell caching, you must provide your own implementation of cache like so:
-``` php
+```php
$cache = new MyCustomPsr16Implementation();
\PhpOffice\PhpSpreadsheet\Settings::setCache($cache);
diff --git a/docs/topics/migration-from-PHPExcel.md b/docs/topics/migration-from-PHPExcel.md
index cc469768..6dc14f1f 100644
--- a/docs/topics/migration-from-PHPExcel.md
+++ b/docs/topics/migration-from-PHPExcel.md
@@ -12,7 +12,7 @@ need to be done.
automatically your codebase. Assuming your files to be migrated lives
in `src/`, you can run the migration like so:
-``` sh
+```sh
composer require rector/rector --dev
vendor/bin/rector process src --set phpexcel-to-phpspreadsheet
composer remove rector/rector
diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md
index 13b62e01..2e0a9d5f 100644
--- a/docs/topics/reading-and-writing-to-file.md
+++ b/docs/topics/reading-and-writing-to-file.md
@@ -33,7 +33,7 @@ You can create a `\PhpOffice\PhpSpreadsheet\Reader\IReader` instance using
`\PhpOffice\PhpSpreadsheet\IOFactory` in automatic file type resolving
mode using the following code sample:
-``` php
+```php
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load("05featuredemo.xlsx");
```
@@ -45,7 +45,7 @@ If you need to set some properties on the reader, (e.g. to only read
data, see more about this later), then you may instead want to use this
variant:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("05featuredemo.xlsx");
$reader->setReadDataOnly(true);
$reader->load("05featuredemo.xlsx");
@@ -55,7 +55,7 @@ You can create a `\PhpOffice\PhpSpreadsheet\Reader\IReader` instance using
`\PhpOffice\PhpSpreadsheet\IOFactory` in explicit mode using the following
code sample:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader("Xlsx");
$spreadsheet = $reader->load("05featuredemo.xlsx");
```
@@ -68,7 +68,7 @@ mode.
You can create a `\PhpOffice\PhpSpreadsheet\Writer\IWriter` instance using
`\PhpOffice\PhpSpreadsheet\IOFactory`:
-``` php
+```php
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, "Xlsx");
$writer->save("05featuredemo.xlsx");
```
@@ -84,7 +84,7 @@ outputting the in-memory spreadsheet to a .xlsx file.
You can read an .xlsx file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$spreadsheet = $reader->load("05featuredemo.xlsx");
```
@@ -94,7 +94,7 @@ $spreadsheet = $reader->load("05featuredemo.xlsx");
You can set the option setReadDataOnly on the reader, to instruct the
reader to ignore styling, data validation, … and just read cell data:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load("05featuredemo.xlsx");
@@ -105,7 +105,7 @@ $spreadsheet = $reader->load("05featuredemo.xlsx");
You can set the option setLoadSheetsOnly on the reader, to instruct the
reader to only load the sheets with a given name:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]);
$spreadsheet = $reader->load("05featuredemo.xlsx");
@@ -122,7 +122,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`.
The following code will only read row 1 and rows 20 – 30 of any sheet in
the Excel file:
-``` php
+```php
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
public function readCell($column, $row, $worksheetName = '') {
@@ -145,7 +145,7 @@ $spreadsheet = $reader->load("06largescale.xlsx");
You can write an .xlsx file using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save("05featuredemo.xlsx");
```
@@ -156,7 +156,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet.
This can be slow on large spreadsheets, and maybe even unwanted. You can
however disable formula pre-calculation:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false);
$writer->save("05featuredemo.xlsx");
@@ -201,7 +201,7 @@ PHP.
You can read an .xls file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
$spreadsheet = $reader->load("05featuredemo.xls");
```
@@ -211,7 +211,7 @@ $spreadsheet = $reader->load("05featuredemo.xls");
You can set the option setReadDataOnly on the reader, to instruct the
reader to ignore styling, data validation, … and just read cell data:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load("05featuredemo.xls");
@@ -222,7 +222,7 @@ $spreadsheet = $reader->load("05featuredemo.xls");
You can set the option setLoadSheetsOnly on the reader, to instruct the
reader to only load the sheets with a given name:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
$reader->setLoadSheetsOnly(["Sheet 1", "My special sheet"]);
$spreadsheet = $reader->load("05featuredemo.xls");
@@ -239,7 +239,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`.
The following code will only read row 1 and rows 20 to 30 of any sheet
in the Excel file:
-``` php
+```php
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
public function readCell($column, $row, $worksheetName = '') {
@@ -262,7 +262,7 @@ $spreadsheet = $reader->load("06largescale.xls");
You can write an .xls file using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet);
$writer->save("05featuredemo.xls");
```
@@ -282,7 +282,7 @@ spreadsheets via PHP.
You can read an Excel 2003 .xml file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xml();
$spreadsheet = $reader->load("05featuredemo.xml");
```
@@ -298,7 +298,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`.
The following code will only read row 1 and rows 20 to 30 of any sheet
in the Excel file:
-``` php
+```php
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
public function readCell($column, $row, $worksheetName = '') {
@@ -333,7 +333,7 @@ regarding to styling cells and handling large spreadsheets via PHP.
You can read an .slk file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Slk();
$spreadsheet = $reader->load("05featuredemo.slk");
```
@@ -349,7 +349,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`.
The following code will only read row 1 and rows 20 to 30 of any sheet
in the SYLK file:
-``` php
+```php
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
public function readCell($column, $row, $worksheetName = '') {
@@ -378,7 +378,7 @@ Open Office or Libre Office Calc files.
You can read an .ods file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Ods();
$spreadsheet = $reader->load("05featuredemo.ods");
```
@@ -394,7 +394,7 @@ read using the `\PhpOffice\PhpSpreadsheet\Reader\DefaultReadFilter`.
The following code will only read row 1 and rows 20 to 30 of any sheet
in the Calc file:
-``` php
+```php
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter {
public function readCell($column, $row, $worksheetName = '') {
@@ -427,7 +427,7 @@ regarding to styling cells, number formatting, ...
You can read a .csv file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$spreadsheet = $reader->load("sample.csv");
```
@@ -449,7 +449,7 @@ were created in Microsoft Office Excel the correct input encoding may
rather be Windows-1252 (CP1252). Always make sure that the input
encoding is set appropriately.
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$reader->setInputEncoding('CP1252');
$reader->setDelimiter(';');
@@ -464,7 +464,7 @@ $spreadsheet = $reader->load("sample.csv");
CSV files can only contain one worksheet. Therefore, you can specify
which sheet to read from CSV:
-``` php
+```php
$reader->setSheetIndex(0);
```
@@ -475,7 +475,7 @@ data into an existing `Spreadsheet` object. The following code loads a
CSV file into an existing `$spreadsheet` containing some sheets, and
imports onto the 6th sheet:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$reader->setDelimiter(';');
$reader->setEnclosure('');
@@ -490,7 +490,7 @@ $reader->loadIntoExisting("05featuredemo.csv", $spreadsheet);
You can write a .csv file using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->save("05featuredemo.csv");
```
@@ -502,7 +502,7 @@ as a separator. You can instruct
`\PhpOffice\PhpSpreadsheet\Writer\Csv` some options before writing a CSV
file:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setDelimiter(';');
$writer->setEnclosure('');
@@ -517,7 +517,7 @@ $writer->save("05featuredemo.csv");
CSV files can only contain one worksheet. Therefore, you can specify
which sheet to write to CSV:
-``` php
+```php
$writer->setSheetIndex(0);
```
@@ -527,7 +527,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet.
This can be slow on large spreadsheets, and maybe even unwanted. You can
however disable formula pre-calculation:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setPreCalculateFormulas(false);
$writer->save("05featuredemo.csv");
@@ -542,7 +542,7 @@ it should explicitly include a BOM file header;
if it doesn't, Excel will not interpret those characters correctly.
This can be enabled by using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setUseBOM(true);
$writer->save("05featuredemo.csv");
@@ -560,14 +560,14 @@ to set the characters explicitly as shown below.
English users will want to use this before doing the export:
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator('.');
\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator(',');
```
German users will want to use the opposite values.
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setDecimalSeparator(',');
\PhpOffice\PhpSpreadsheet\Shared\StringHelper::setThousandsSeparator('.');
```
@@ -592,7 +592,7 @@ regarding to styling cells, number formatting, ...
You can read an .html or .htm file using the following code:
-``` php
+```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Html();
$spreadsheet = $reader->load("05featuredemo.html");
@@ -610,7 +610,7 @@ first worksheet by default.
You can write a .htm file using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->save("05featuredemo.htm");
@@ -621,7 +621,7 @@ $writer->save("05featuredemo.htm");
HTML files can contain one or more worksheets. If you want to write all
sheets into a single HTML file, use the following code:
-``` php
+```php
$writer->writeAllSheets();
```
@@ -630,7 +630,7 @@ $writer->writeAllSheets();
HTML files can contain one or more worksheets. Therefore, you can
specify which sheet to write to HTML:
-``` php
+```php
$writer->setSheetIndex(0);
```
@@ -639,19 +639,19 @@ $writer->setSheetIndex(0);
There might be situations where you want to explicitly set the included
images root. For example, instead of:
- ``` html
+ ```html
```
You might want to see:
-``` html
+```html
```
You can use the following code to achieve this result:
-``` php
+```php
$writer->setImagesRoot('http://www.example.com');
```
@@ -661,7 +661,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet.
This can be slow on large spreadsheets, and maybe even unwanted. You can
however disable formula pre-calculation:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->setPreCalculateFormulas(false);
@@ -686,7 +686,7 @@ Supported methods:
Here's an example which retrieves all parts independently and merges
them into a resulting HTML page:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$hdr = $writer->generateHTMLHeader();
$sty = $writer->generateStyles(false); // do not write
@@ -757,7 +757,7 @@ own circumstances.
You can instantiate a writer with its specific name, like so:
-``` php
+```php
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Mpdf');
```
@@ -765,7 +765,7 @@ Or you can register which writer you are using with a more generic name,
so you don't need to remember which library you chose, only that you want
to write PDF files:
-``` php
+```php
$class = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf::class;
\PhpOffice\PhpSpreadsheet\IOFactory::registerWriter('Pdf', $class);
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf');
@@ -773,7 +773,7 @@ $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Pdf')
Or you can instantiate directly the writer of your choice like so:
-``` php
+```php
$writer = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet);
```
@@ -782,7 +782,7 @@ $writer = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet);
If you need a custom implementation, or custom configuration, of a supported
PDF library. You can extends the PDF library, and the PDF writer like so:
-``` php
+```php
class My_Custom_TCPDF extends TCPDF
{
// ...
@@ -808,7 +808,7 @@ class My_Custom_TCPDF_Writer extends \PhpOffice\PhpSpreadsheet\Writer\Pdf\Tcpdf
Once you have identified the Renderer that you wish to use for PDF
generation, you can write a .pdf file using the following code:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet);
$writer->save("05featuredemo.pdf");
```
@@ -821,7 +821,7 @@ first worksheet by default.
PDF files can contain one or more worksheets. If you want to write all
sheets into a single PDF file, use the following code:
-``` php
+```php
$writer->writeAllSheets();
```
@@ -830,7 +830,7 @@ $writer->writeAllSheets();
PDF files can contain one or more worksheets. Therefore, you can specify
which sheet to write to PDF:
-``` php
+```php
$writer->setSheetIndex(0);
```
@@ -840,7 +840,7 @@ By default, this writer pre-calculates all formulas in the spreadsheet.
This can be slow on large spreadsheets, and maybe even unwanted. You can
however disable formula pre-calculation:
-``` php
+```php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf($spreadsheet);
$writer->setPreCalculateFormulas(false);
@@ -868,7 +868,7 @@ page setup properties, headers etc.
Here is an example how to open a template file, fill in a couple of
fields and save it again:
-``` php
+```php
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('template.xlsx');
$worksheet = $spreadsheet->getActiveSheet();
diff --git a/docs/topics/reading-files.md b/docs/topics/reading-files.md
index 779082dc..1451f2ab 100644
--- a/docs/topics/reading-files.md
+++ b/docs/topics/reading-files.md
@@ -22,7 +22,7 @@ The simplest way to load a workbook file is to let PhpSpreadsheet's IO
Factory identify the file type and load it, calling the static `load()`
method of the `\PhpOffice\PhpSpreadsheet\IOFactory` class.
-``` php
+```php
$inputFileName = './sampleData/example1.xls';
/** Load $inputFileName to a Spreadsheet Object **/
@@ -59,7 +59,7 @@ supported filetype by name. However, you may get unpredictable results
if the file isn't of the right type (e.g. it is a CSV with an extension
of .xls), although this type of exception should normally be trapped.
-``` php
+```php
$inputFileName = './sampleData/example1.xls';
/** Create a new Xls Reader **/
@@ -81,7 +81,7 @@ Alternatively, you can use the IO Factory's `createReader()` method to
instantiate the reader object for you, simply telling it the file type
of the reader that you want instantiating.
-``` php
+```php
$inputFileType = 'Xls';
// $inputFileType = 'Xlsx';
// $inputFileType = 'Xml';
@@ -104,7 +104,7 @@ If you're uncertain of the filetype, you can use the `IOFactory::identify()`
method to identify the reader that you need, before using the
`createReader()` method to instantiate the reader object.
-``` php
+```php
$inputFileName = './sampleData/example1.xls';
/** Identify the type of $inputFileName **/
@@ -131,7 +131,7 @@ need any of the cell formatting information, then you can set the reader
to read only the data values and any formulae from each cell using the
`setReadDataOnly()` method.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example1.xls';
@@ -176,7 +176,7 @@ in reading.
To read a single sheet, you can pass that sheet name as a parameter to
the `setLoadSheetsOnly()` method.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example1.xls';
$sheetname = 'Data Sheet #2';
@@ -195,7 +195,7 @@ for a working example of this code.
If you want to read more than just a single sheet, you can pass a list
of sheet names as an array parameter to the `setLoadSheetsOnly()` method.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example1.xls';
$sheetnames = ['Data Sheet #1','Data Sheet #3'];
@@ -214,7 +214,7 @@ for a working example of this code.
To reset this option to the default, you can call the `setLoadAllSheets()`
method.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example1.xls';
@@ -248,7 +248,7 @@ should be read by the loader. A read filter must implement the
whether a workbook cell identified by those arguments should be read or
not.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example1.xls';
$sheetname = 'Data Sheet #3';
@@ -286,7 +286,7 @@ a very specific circumstance (when you only want cells in the range
A1:E7 from your worksheet. A generic Read Filter would probably be more
useful:
-``` php
+```php
/** Define a Read Filter class implementing \PhpOffice\PhpSpreadsheet\Reader\IReadFilter */
class MyReadFilter implements \PhpOffice\PhpSpreadsheet\Reader\IReadFilter
{
@@ -324,7 +324,7 @@ to read and process a large workbook in "chunks": an example of this
usage might be when transferring data from an Excel worksheet to a
database.
-``` php
+```php
$inputFileType = 'Xls';
$inputFileName = './sampleData/example2.xls';
@@ -393,7 +393,7 @@ the `setSheetIndex()` method of the `$reader`, then use the
`loadIntoExisting()` method rather than the `load()` method to actually read
the file into that worksheet.
-``` php
+```php
$inputFileType = 'Csv';
$inputFileNames = [
'./sampleData/example1.csv',
@@ -452,7 +452,7 @@ Class that we defined in [the above section](#reading-only-specific-columns-and-
and the `setSheetIndex()` method of the `$reader`, we can split the CSV
file across several individual worksheets.
-``` php
+```php
$inputFileType = 'Csv';
$inputFileName = './sampleData/example2.csv';
@@ -523,7 +523,7 @@ cannot auto-detect, it will default to the comma. If this does not fit your
use-case, you can manually specify a separator by using the `setDelimiter()`
method.
-``` php
+```php
$inputFileType = 'Csv';
$inputFileName = './sampleData/example1.tsv';
@@ -585,7 +585,7 @@ it encountered a hyperlink, or HTML markup within a CSV file.
So using a Value Binder allows a great deal more flexibility in the
loader logic when reading unformatted text files.
-``` php
+```php
/** Tell PhpSpreadsheet that we want to use the Advanced Value Binder **/
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );
@@ -619,7 +619,7 @@ manner.
The PhpSpreadsheet Readers throw a
`\PhpOffice\PhpSpreadsheet\Reader\Exception`.
-``` php
+```php
$inputFileName = './sampleData/example-1.xls';
try {
@@ -646,7 +646,7 @@ whole file.
The `listWorksheetNames()` method returns a simple array listing each
worksheet name within the workbook:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
$worksheetNames = $reader->listWorksheetNames($inputFileName);
@@ -667,7 +667,7 @@ for a working example of this code.
The `listWorksheetInfo()` method returns a nested array, with each entry
listing the name and dimensions for a worksheet:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
$worksheetData = $reader->listWorksheetInfo($inputFileName);
diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md
index b0956b6e..f85576a2 100644
--- a/docs/topics/recipes.md
+++ b/docs/topics/recipes.md
@@ -20,7 +20,7 @@ metadata to search for a specific document in its document lists.
Setting spreadsheet metadata is done as follows:
-``` php
+```php
$spreadsheet->getProperties()
->setCreator("Maarten Balliauw")
->setLastModifiedBy("Maarten Balliauw")
@@ -38,13 +38,13 @@ $spreadsheet->getProperties()
The following line of code sets the active sheet index to the first
sheet:
-``` php
+```php
$spreadsheet->setActiveSheetIndex(0);
```
You can also set the active sheet by its name/title
-``` php
+```php
$spreadsheet->setActiveSheetIndexByName('DataSheet')
```
@@ -68,7 +68,7 @@ UST.
Writing a date value in a cell consists of 2 lines of code. Select the
method that suits you the best. Here are some examples:
-``` php
+```php
// MySQL-like timestamp '2008-12-31' or date string
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );
@@ -136,14 +136,14 @@ The following line of code writes the formula
formula must start with `=` to make PhpSpreadsheet recognise this as a
formula.
-``` php
+```php
$spreadsheet->getActiveSheet()->setCellValue('B8','=IF(C4>500,"profit","loss")');
```
If you want to write a string beginning with an `=` character to a
cell, then you should use the `setCellValueExplicit()` method.
-``` php
+```php
$spreadsheet->getActiveSheet()
->setCellValueExplicit(
'B8',
@@ -154,14 +154,14 @@ $spreadsheet->getActiveSheet()
A cell's formula can be read again using the following line of code:
-``` php
+```php
$formula = $spreadsheet->getActiveSheet()->getCell('B8')->getValue();
```
If you need the calculated value of a cell, use the following code. This
is further explained in [the calculation engine](./calculation-engine.md).
-``` php
+```php
$value = $spreadsheet->getActiveSheet()->getCell('B8')->getCalculatedValue();
```
@@ -171,7 +171,7 @@ Some localisation elements have been included in PhpSpreadsheet. You can
set a locale by changing the settings. To set the locale to Russian you
would use:
-``` php
+```php
$locale = 'ru';
$validLocale = \PhpOffice\PhpSpreadsheet\Settings::setLocale($locale);
if (!$validLocale) {
@@ -185,7 +185,7 @@ will return an error, and English settings will be used throughout.
Once you have set a locale, you can translate a formula from its
internal English coding.
-``` php
+```php
$formula = $spreadsheet->getActiveSheet()->getCell('B8')->getValue();
$translatedFormula = \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->_translateFormulaToLocale($formula);
```
@@ -194,7 +194,7 @@ You can also create a formula using the function names and argument
separators appropriate to the defined locale; then translate it to
English before setting the cell value:
-``` php
+```php
$formula = '=ДНЕЙ360(ДАТА(2010;2;5);ДАТА(2010;12;31);ИСТИНА)';
$internalFormula = \PhpOffice\PhpSpreadsheet\Calculation\Calculation::getInstance()->translateFormulaToEnglish($formula);
$spreadsheet->getActiveSheet()->setCellValue('B8',$internalFormula);
@@ -232,7 +232,7 @@ the cell.
Here is how to achieve this in PhpSpreadsheet:
-``` php
+```php
$spreadsheet->getActiveSheet()->getCell('A1')->setValue("hello\nworld");
$spreadsheet->getActiveSheet()->getStyle('A1')->getAlignment()->setWrapText(true);
```
@@ -247,7 +247,7 @@ AdvancedValuebinder.php automatically turns on "wrap text" for the cell
when it sees a newline character in a string that you are inserting in a
cell. Just like Microsoft Office Excel. Try this:
-``` php
+```php
\PhpOffice\PhpSpreadsheet\Cell\Cell::setValueBinder( new \PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder() );
$spreadsheet->getActiveSheet()->getCell('A1')->setValue("hello\nworld");
@@ -261,7 +261,7 @@ You can set a cell's datatype explicitly by using the cell's
setValueExplicit method, or the setCellValueExplicit method of a
worksheet. Here's an example:
-``` php
+```php
$spreadsheet->getActiveSheet()->getCell('A1')
->setValueExplicit(
'25',
@@ -273,7 +273,7 @@ $spreadsheet->getActiveSheet()->getCell('A1')
You can make a cell a clickable URL by setting its hyperlink property:
-``` php
+```php
$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
$spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://www.example.com');
```
@@ -281,7 +281,7 @@ $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl('https://
If you want to make a hyperlink to another worksheet/cell, use the
following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->setCellValue('E26', 'www.phpexcel.net');
$spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl("sheet://'Sheetname'!A1");
```
@@ -293,7 +293,7 @@ $spreadsheet->getActiveSheet()->getCell('E26')->getHyperlink()->setUrl("sheet://
Setting a worksheet's page orientation and size can be done using the
following lines of code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()
->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE);
$spreadsheet->getActiveSheet()->getPageSetup()
@@ -324,7 +324,7 @@ setFitToHeight(...) | 1 | setFitToPage(TRUE) | value 0 mean
Here is how to fit to 1 page wide by infinite pages tall:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()->setFitToWidth(1);
$spreadsheet->getActiveSheet()->getPageSetup()->setFitToHeight(0);
```
@@ -340,7 +340,7 @@ the initial values.
To set page margins for a worksheet, use this code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageMargins()->setTop(1);
$spreadsheet->getActiveSheet()->getPageMargins()->setRight(0.75);
$spreadsheet->getActiveSheet()->getPageMargins()->setLeft(0.75);
@@ -356,7 +356,7 @@ Note that the margin values are specified in inches.
To center a page horizontally/vertically, you can use the following
code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()->setHorizontalCentered(true);
$spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false);
```
@@ -366,7 +366,7 @@ $spreadsheet->getActiveSheet()->getPageSetup()->setVerticalCentered(false);
Setting a worksheet's print header and footer can be done using the
following lines of code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getHeaderFooter()
->setOddHeader('&C&HPlease treat this document as confidential!');
$spreadsheet->getActiveSheet()->getHeaderFooter()
@@ -460,13 +460,13 @@ $spreadsheet->getActiveSheet()->getHeaderFooter()->addImage($drawing, \PhpOffice
To set a print break, use the following code, which sets a row break on
row 10.
-``` php
+```php
$spreadsheet->getActiveSheet()->setBreak('A10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
```
The following line of code sets a print break on column D:
-``` php
+```php
$spreadsheet->getActiveSheet()->setBreak('D10', \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN);
```
@@ -484,7 +484,7 @@ PhpSpreadsheet can repeat specific rows/cells at top/left of a page. The
following code is an example of how to repeat row 1 to 5 on each printed
page of a specific worksheet:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(1, 5);
```
@@ -492,13 +492,13 @@ $spreadsheet->getActiveSheet()->getPageSetup()->setRowsToRepeatAtTopByStartAndEn
To specify a worksheet's printing area, use the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:E5');
```
There can also be multiple printing areas in a single worksheet:
-``` php
+```php
$spreadsheet->getActiveSheet()->getPageSetup()->setPrintArea('A1:E5,G4:M20');
```
@@ -511,7 +511,7 @@ For example, one can set the foreground colour of a cell to red, aligned
to the right, and the border to black and thick border style. Let's do
that on cell B2:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('B2')
->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);
$spreadsheet->getActiveSheet()->getStyle('B2')
@@ -533,7 +533,7 @@ $spreadsheet->getActiveSheet()->getStyle('B2')
`getStyle()` also accepts a cell range as a parameter. For example, you
can set a red background color on a range of cells:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('B3:B7')->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('FFFF0000');
@@ -548,7 +548,7 @@ There is also an alternative manner to set styles. The following code
sets a cell's style to font bold, alignment right, top border thin and a
gradient fill:
-``` php
+```php
$styleArray = [
'font' => [
'bold' => true,
@@ -578,7 +578,7 @@ $spreadsheet->getActiveSheet()->getStyle('A3')->applyFromArray($styleArray);
Or with a range of cells:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('B3:B7')->applyFromArray($styleArray);
```
@@ -602,7 +602,7 @@ number format code unless you need a custom number format.
In PhpSpreadsheet, you can also apply various predefined number formats.
Example:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()
->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
```
@@ -614,7 +614,7 @@ up as 1.587,20)
You can achieve exactly the same as the above by using this:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()
->setFormatCode('#,##0.00');
```
@@ -623,7 +623,7 @@ In Microsoft Office Excel, as well as in PhpSpreadsheet, you will have
to interact with raw number format codes whenever you need some special
custom number format. Example:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()
->setFormatCode('[Blue][>=3000]$#,##0;[Red][<0]$#,##0;$#,##0');
```
@@ -631,7 +631,7 @@ $spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()
Another example is when you want numbers zero-padded with leading zeros
to a fixed length:
-``` php
+```php
$spreadsheet->getActiveSheet()->getCell('A1')->setValue(19);
$spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()
->setFormatCode('0000'); // will show as 0019 in Excel
@@ -646,7 +646,7 @@ The readers shipped with PhpSpreadsheet come to the rescue. Load your
template workbook using e.g. Xlsx reader to reveal the number format
code. Example how read a number format code for cell A1:
-``` php
+```php
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
$spreadsheet = $reader->load('template.xlsx');
var_dump($spreadsheet->getActiveSheet()->getStyle('A1')->getNumberFormat()->getFormatCode());
@@ -661,14 +661,14 @@ code in *xl/styles.xml*.
Let's set vertical alignment to the top for cells A1:D4
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('A1:D4')
->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_TOP);
```
Here is how to achieve wrap text:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('A1:D4')
->getAlignment()->setWrapText(true);
```
@@ -678,7 +678,7 @@ $spreadsheet->getActiveSheet()->getStyle('A1:D4')
It is possible to set the default style of a workbook. Let's set the
default font to Arial size 8:
-``` php
+```php
$spreadsheet->getDefaultStyle()->getFont()->setName('Arial');
$spreadsheet->getDefaultStyle()->getFont()->setSize(8);
```
@@ -689,7 +689,7 @@ In PhpSpreadsheet it is easy to apply various borders on a rectangular
selection. Here is how to apply a thick red border outline around cells
B2:G8.
-``` php
+```php
$styleArray = [
'borders' => [
'outline' => [
@@ -839,7 +839,7 @@ is below zero, and to green if its value is zero or more.
One can set a conditional style ruleset to a cell using the following
code:
-``` php
+```php
$conditional1 = new \PhpOffice\PhpSpreadsheet\Style\Conditional();
$conditional1->setConditionType(\PhpOffice\PhpSpreadsheet\Style\Conditional::CONDITION_CELLIS);
$conditional1->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPERATOR_LESSTHAN);
@@ -864,7 +864,7 @@ $spreadsheet->getActiveSheet()->getStyle('B2')->setConditionalStyles($conditiona
If you want to copy the ruleset to other cells, you can duplicate the
style object:
-``` php
+```php
$spreadsheet->getActiveSheet()
->duplicateStyle(
$spreadsheet->getActiveSheet()->getStyle('B2'),
@@ -877,7 +877,7 @@ $spreadsheet->getActiveSheet()
To add a comment to a cell, use the following code. The example below
adds a comment to cell E11:
-``` php
+```php
$spreadsheet->getActiveSheet()
->getComment('E11')
->setAuthor('Mark Baker');
@@ -899,7 +899,7 @@ $spreadsheet->getActiveSheet()
To apply an autofilter to a range of cells, use the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->setAutoFilter('A1:C9');
```
@@ -919,45 +919,85 @@ disallow inserting rows on a specific sheet, disallow sorting, ...
- Cell: offers the option to lock/unlock a cell as well as show/hide
the internal formula.
+**Make sure you enable worksheet protection if you need any of the
+worksheet or cell protection features!** This can be done using the following
+code:
+
+```php
+$spreadsheet->getActiveSheet()->getProtection()->setSheet(true);
+```
+
+### Document
+
An example on setting document security:
-``` php
-$spreadsheet->getSecurity()->setLockWindows(true);
-$spreadsheet->getSecurity()->setLockStructure(true);
-$spreadsheet->getSecurity()->setWorkbookPassword("PhpSpreadsheet");
+```php
+$security = $spreadsheet->getSecurity();
+$security->setLockWindows(true);
+$security->setLockStructure(true);
+$security->setWorkbookPassword("PhpSpreadsheet");
```
+### Worksheet
+
An example on setting worksheet security:
-``` php
-$spreadsheet->getActiveSheet()
- ->getProtection()->setPassword('PhpSpreadsheet');
-$spreadsheet->getActiveSheet()
- ->getProtection()->setSheet(true);
-$spreadsheet->getActiveSheet()
- ->getProtection()->setSort(true);
-$spreadsheet->getActiveSheet()
- ->getProtection()->setInsertRows(true);
-$spreadsheet->getActiveSheet()
- ->getProtection()->setFormatCells(true);
+```php
+$protection = $spreadsheet->getActiveSheet()->getProtection();
+$protection->setPassword('PhpSpreadsheet');
+$protection->setSheet(true);
+$protection->setSort(true);
+$protection->setInsertRows(true);
+$protection->setFormatCells(true);
```
+If writing Xlsx files you can specify the algorithm used to hash the password
+before calling `setPassword()` like so:
+
+```php
+$protection = $spreadsheet->getActiveSheet()->getProtection();
+$protection->setAlgorithm(Protection::ALGORITHM_SHA_512);
+$protection->setSpinCount(20000);
+$protection->setPassword('PhpSpreadsheet');
+```
+
+The salt should **not** be set manually and will be automatically generated
+when setting a new password.
+
+### Cell
+
An example on setting cell security:
-``` php
+```php
$spreadsheet->getActiveSheet()->getStyle('B1')
->getProtection()
->setLocked(\PhpOffice\PhpSpreadsheet\Style\Protection::PROTECTION_UNPROTECTED);
```
-**Make sure you enable worksheet protection if you need any of the
-worksheet protection features!** This can be done using the following
-code:
+## Reading protected spreadsheet
-``` php
-$spreadsheet->getActiveSheet()->getProtection()->setSheet(true);
+Spreadsheets that are protected as described above can always be read by
+PhpSpreadsheet. There is no need to know the password or do anything special in
+order to read a protected file.
+
+However if you need to implement a password verification mechanism, you can use the
+following helper method:
+
+
+```php
+$protection = $spreadsheet->getActiveSheet()->getProtection();
+$allowed = $protection->verify('my password');
+
+if ($allowed) {
+ doSomething();
+} else {
+ throw new Exception('Incorrect password');
+}
```
+If you need to completely prevent reading a file by any tool, including PhpSpreadsheet,
+then you are looking for "encryption", not "protection".
+
## Setting data validation on a cell
Data validation is a powerful feature of Xlsx. It allows to specify an
@@ -968,7 +1008,7 @@ filter can be a range (i.e. value must be between 0 and 10), a list
The following piece of code only allows numbers between 10 and 20 to be
entered in cell B3:
-``` php
+```php
$validation = $spreadsheet->getActiveSheet()->getCell('B3')
->getDataValidation();
$validation->setType( \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_WHOLE );
@@ -987,7 +1027,7 @@ $validation->setFormula2(20);
The following piece of code only allows an item picked from a list of
data to be entered in cell B5:
-``` php
+```php
$validation = $spreadsheet->getActiveSheet()->getCell('B5')
->getDataValidation();
$validation->setType( \PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST );
@@ -1017,7 +1057,7 @@ the item values themselves can contain the comma `,` character itself.
If you need data validation on multiple cells, one can clone the
ruleset:
-``` php
+```php
$spreadsheet->getActiveSheet()->getCell('B8')->setDataValidation(clone $validation);
```
@@ -1025,7 +1065,7 @@ $spreadsheet->getActiveSheet()->getCell('B8')->setDataValidation(clone $validati
A column's width can be set using the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setWidth(12);
```
@@ -1033,7 +1073,7 @@ If you want PhpSpreadsheet to perform an automatic width calculation,
use the following code. PhpSpreadsheet will approximate the column with
to the width of the widest column value.
-``` php
+```php
$spreadsheet->getActiveSheet()->getColumnDimension('B')->setAutoSize(true);
```
@@ -1070,7 +1110,7 @@ To set a worksheet's column visibility, you can use the following code.
The first line explicitly shows the column C, the second line hides
column D.
-``` php
+```php
$spreadsheet->getActiveSheet()->getColumnDimension('C')->setVisible(true);
$spreadsheet->getActiveSheet()->getColumnDimension('D')->setVisible(false);
```
@@ -1079,7 +1119,7 @@ $spreadsheet->getActiveSheet()->getColumnDimension('D')->setVisible(false);
To group/outline a column, you can use the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getColumnDimension('E')->setOutlineLevel(1);
```
@@ -1087,7 +1127,7 @@ You can also collapse the column. Note that you should also set the
column invisible, otherwise the collapse will not be visible in Excel
2007.
-``` php
+```php
$spreadsheet->getActiveSheet()->getColumnDimension('E')->setCollapsed(true);
$spreadsheet->getActiveSheet()->getColumnDimension('E')->setVisible(false);
```
@@ -1098,7 +1138,7 @@ on collapsing.
You can instruct PhpSpreadsheet to add a summary to the right (default),
or to the left. The following code adds the summary to the left:
-``` php
+```php
$spreadsheet->getActiveSheet()->setShowSummaryRight(false);
```
@@ -1106,7 +1146,7 @@ $spreadsheet->getActiveSheet()->setShowSummaryRight(false);
A row's height can be set using the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getRowDimension('10')->setRowHeight(100);
```
@@ -1119,7 +1159,7 @@ of values is between 0 and 409 pts, where 0 pts is a hidden row.
To set a worksheet''s row visibility, you can use the following code.
The following example hides row number 10.
-``` php
+```php
$spreadsheet->getActiveSheet()->getRowDimension('10')->setVisible(false);
```
@@ -1131,21 +1171,21 @@ AutoFilter range if you save the file.
To group/outline a row, you can use the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getRowDimension('5')->setOutlineLevel(1);
```
You can also collapse the row. Note that you should also set the row
invisible, otherwise the collapse will not be visible in Excel 2007.
-``` php
+```php
$spreadsheet->getActiveSheet()->getRowDimension('5')->setCollapsed(true);
$spreadsheet->getActiveSheet()->getRowDimension('5')->setVisible(false);
```
Here's an example which collapses rows 50 to 80:
-``` php
+```php
for ($i = 51; $i <= 80; $i++) {
$spreadsheet->getActiveSheet()->setCellValue('A' . $i, "FName $i");
$spreadsheet->getActiveSheet()->setCellValue('B' . $i, "LName $i");
@@ -1162,7 +1202,7 @@ $spreadsheet->getActiveSheet()->getRowDimension(81)->setCollapsed(true);
You can instruct PhpSpreadsheet to add a summary below the collapsible
rows (default), or above. The following code adds the summary above:
-``` php
+```php
$spreadsheet->getActiveSheet()->setShowSummaryBelow(false);
```
@@ -1172,13 +1212,13 @@ If you have a big piece of data you want to display in a worksheet, you
can merge two or more cells together, to become one cell. This can be
done using the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->mergeCells('A18:E22');
```
Removing a merge can be done using the unmergeCells method:
-``` php
+```php
$spreadsheet->getActiveSheet()->unmergeCells('A18:E22');
```
@@ -1187,7 +1227,7 @@ $spreadsheet->getActiveSheet()->unmergeCells('A18:E22');
You can insert/remove rows/columns at a specific position. The following
code inserts 2 new rows, right before row 7:
-``` php
+```php
$spreadsheet->getActiveSheet()->insertNewRowBefore(7, 2);
```
@@ -1198,7 +1238,7 @@ to a worksheet. Therefore, you must first instantiate a new
`\PhpOffice\PhpSpreadsheet\Worksheet\Drawing`, and assign its properties a
meaningful value:
-``` php
+```php
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$drawing->setName('Logo');
$drawing->setDescription('Logo');
@@ -1210,13 +1250,13 @@ To add the above drawing to the worksheet, use the following snippet of
code. PhpSpreadsheet creates the link between the drawing and the
worksheet:
-``` php
+```php
$drawing->setWorksheet($spreadsheet->getActiveSheet());
```
You can set numerous properties on a drawing, here are some examples:
-``` php
+```php
$drawing->setName('Paid');
$drawing->setDescription('Paid');
$drawing->setPath('./images/paid.png');
@@ -1230,7 +1270,7 @@ $drawing->getShadow()->setDirection(45);
You can also add images created using GD functions without needing to
save them to disk first as In-Memory drawings.
-``` php
+```php
// Use GD to create an in-memory image
$gdImage = @imagecreatetruecolor(120, 20) or die('Cannot Initialize new GD image stream');
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
@@ -1258,7 +1298,7 @@ that has been loaded, and save them as individual image files to disk.
The following code extracts images from the current active worksheet,
and writes each as a separate file.
-``` php
+```php
$i = 0;
foreach ($spreadsheet->getActiveSheet()->getDrawingCollection() as $drawing) {
if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing) {
@@ -1303,7 +1343,7 @@ creates the following rich text string:
> This invoice is ***payable within thirty days after the end of the
> month*** unless specified otherwise on the invoice.
-``` php
+```php
$richText = new \PhpOffice\PhpSpreadsheet\RichText\RichText();
$richText->createText('This invoice is ');
$payable = $richText->createTextRun('payable within thirty days after the end of the month');
@@ -1319,7 +1359,7 @@ $spreadsheet->getActiveSheet()->getCell('A18')->setValue($richText);
PhpSpreadsheet supports the definition of named ranges. These can be
defined using the following code:
-``` php
+```php
// Add some data
$spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet()->setCellValue('A1', 'Firstname:');
@@ -1362,7 +1402,7 @@ your document is needed, it is recommended not to use `php://output`.
Example of a script redirecting an Excel 2007 file to the client's
browser:
-``` php
+```php
/* Here there will be some code where you create $spreadsheet */
// redirect output to client browser
@@ -1376,7 +1416,7 @@ $writer->save('php://output');
Example of a script redirecting an Xls file to the client's browser:
-``` php
+```php
/* Here there will be some code where you create $spreadsheet */
// redirect output to client browser
@@ -1404,7 +1444,7 @@ at the client browser, and/or that headers cannot be set by PHP
Default column width can be set using the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(12);
```
@@ -1412,7 +1452,7 @@ $spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(12);
Default row height can be set using the following code:
-``` php
+```php
$spreadsheet->getActiveSheet()->getDefaultRowDimension()->setRowHeight(15);
```
@@ -1425,7 +1465,7 @@ file to a temporary location.
Here''s an example which generates an image in memory and adds it to the
active worksheet:
-``` php
+```php
// Generate an image
$gdImage = @imagecreatetruecolor(120, 20) or die('Cannot Initialize new GD image stream');
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
@@ -1446,7 +1486,7 @@ $drawing->setWorksheet($spreadsheet->getActiveSheet());
To set a worksheet's zoom level, the following code can be used:
-``` php
+```php
$spreadsheet->getActiveSheet()->getSheetView()->setZoomScale(75);
```
@@ -1457,7 +1497,7 @@ Note that zoom level should be in range 10 - 400.
Sometimes you want to set a color for sheet tab. For example you can
have a red sheet tab:
-``` php
+```php
$worksheet->getTabColor()->setRGB('FF0000');
```
@@ -1465,7 +1505,7 @@ $worksheet->getTabColor()->setRGB('FF0000');
If you need to create more worksheets in the workbook, here is how:
-``` php
+```php
$worksheet1 = $spreadsheet->createSheet();
$worksheet1->setTitle('Another sheet');
```
@@ -1478,7 +1518,7 @@ worksheets in the workbook.
Set a worksheet to be **hidden** using this code:
-``` php
+```php
$spreadsheet->getActiveSheet()
->setSheetState(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN);
```
@@ -1500,7 +1540,7 @@ Worksheets can be set individually whether column `A` should start at
left or right side. Default is left. Here is how to set columns from
right-to-left.
-``` php
+```php
// right-to-left worksheet
$spreadsheet->getActiveSheet()->setRightToLeft(true);
```
diff --git a/docs/topics/settings.md b/docs/topics/settings.md
index a9aae9f9..4463ceeb 100644
--- a/docs/topics/settings.md
+++ b/docs/topics/settings.md
@@ -13,7 +13,7 @@ Read more about [memory saving](./memory_saving.md).
To enable cell caching, you must provide your own implementation of cache like so:
-``` php
+```php
$cache = new MyCustomPsr16Implementation();
\PhpOffice\PhpSpreadsheet\Settings::setCache($cache);
@@ -25,7 +25,7 @@ Some localisation elements have been included in PhpSpreadsheet. You can
set a locale by changing the settings. To set the locale to Brazilian
Portuguese you would use:
-``` php
+```php
$locale = 'pt_br';
$validLocale = \PhpOffice\PhpSpreadsheet\Settings::setLocale($locale);
if (!$validLocale) {
diff --git a/docs/topics/worksheets.md b/docs/topics/worksheets.md
index f97a0066..0199f13c 100644
--- a/docs/topics/worksheets.md
+++ b/docs/topics/worksheets.md
@@ -25,7 +25,7 @@ each worksheet "tab" is shown when the workbook is opened in MS Excel
(or other appropriate Spreadsheet program). To access a sheet by its
index, use the `getSheet()` method.
-``` php
+```php
// Get the second sheet in the workbook
// Note that sheets are indexed from 0
$spreadsheet->getSheet(1);
@@ -38,7 +38,7 @@ workbook.
To access a sheet by name, use the `getSheetByName()` method, specifying
the name of the worksheet that you want to access.
-``` php
+```php
// Retrieve the worksheet called 'Worksheet 1'
$spreadsheet->getSheetByName('Worksheet 1');
```
@@ -48,7 +48,7 @@ and you can access that directly. The currently active worksheet is the
one that will be active when the workbook is opened in MS Excel (or
other appropriate Spreadsheet program).
-``` php
+```php
// Retrieve the current active worksheet
$spreadsheet->getActiveSheet();
```
@@ -64,7 +64,7 @@ a new "last" sheet; but you can also specify an index position as an
argument, and the worksheet will be inserted at that position, shuffling
all subsequent worksheets in the collection down a place.
-``` php
+```php
$spreadsheet->createSheet();
```
@@ -76,7 +76,7 @@ Alternatively, you can instantiate a new worksheet (setting the title to
whatever you choose) and then insert it into your workbook using the
`addSheet()` method.
-``` php
+```php
// Create a new worksheet called "My Data"
$myWorkSheet = new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet($spreadsheet, 'My Data');
@@ -93,7 +93,7 @@ Sheets within the same workbook can be copied by creating a clone of the
worksheet you wish to copy, and then using the `addSheet()` method to
insert the clone into the workbook.
-``` php
+```php
$clonedWorksheet = clone $spreadsheet->getSheetByName('Worksheet 1');
$clonedWorksheet->setTitle('Copy of Worksheet 1');
$spreadsheet->addSheet($clonedWorksheet);
@@ -117,7 +117,7 @@ duplicate name.
You can delete a worksheet from a workbook, identified by its index
position, using the `removeSheetByIndex()` method
-``` php
+```php
$sheetIndex = $spreadsheet->getIndex(
$spreadsheet->getSheetByName('Worksheet 1')
);
diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php
index cdfe7b53..2d67a134 100644
--- a/src/PhpSpreadsheet/Calculation/Calculation.php
+++ b/src/PhpSpreadsheet/Calculation/Calculation.php
@@ -3456,10 +3456,8 @@ class Calculation
if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
$opCharacter .= $formula[++$index];
}
-
// Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
$isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
-
if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus?
// Put a negation on the stack
$stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
@@ -3627,7 +3625,6 @@ class Calculation
$expectingOperand = false;
$val = $match[1];
$length = strlen($val);
-
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/i', $val, $matches)) {
$val = preg_replace('/\s/u', '', $val);
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
@@ -3662,7 +3659,6 @@ class Calculation
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $val, $matches)) {
// Watch for this case-change when modifying to allow cell references in different worksheets...
// Should only be applied to the actual cell column, not the worksheet name
-
// If the last entry on the stack was a : operator, then we have a cell range reference
$testPrevOp = $stack->last(1);
if ($testPrevOp !== null && $testPrevOp['value'] == ':') {
@@ -3719,6 +3715,8 @@ class Calculation
}
$localeConstant = false;
+ $stackItemType = 'Value';
+ $stackItemReference = null;
if ($opCharacter == self::FORMULA_STRING_QUOTE) {
// UnEscape any quotes within the string
$val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
@@ -3729,12 +3727,17 @@ class Calculation
$val = (int) $val;
}
} elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
+ $stackItemType = 'Constant';
$excelConstant = trim(strtoupper($val));
$val = self::$excelConstants[$excelConstant];
} elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
+ $stackItemType = 'Constant';
$val = self::$excelConstants[$localeConstant];
+ } elseif (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '.*/Ui', $val, $match)) {
+ $stackItemType = 'Named Range';
+ $stackItemReference = $val;
}
- $details = $stack->getStackItem('Value', $val, null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
+ $details = $stack->getStackItem($stackItemType, $val, $stackItemReference, $currentCondition, $currentOnlyIf, $currentOnlyIfNot);
if ($localeConstant) {
$details['localeValue'] = $localeConstant;
}
@@ -3776,8 +3779,12 @@ class Calculation
}
// If we're expecting an operator, but only have a space between the previous and next operands (and both are
// Cell References) then we have an INTERSECTION operator
- if (($expectingOperator) && (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
- ($output[count($output) - 1]['type'] == 'Cell Reference')) {
+ if (($expectingOperator) &&
+ ((preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/Ui', substr($formula, $index), $match)) &&
+ ($output[count($output) - 1]['type'] == 'Cell Reference') ||
+ (preg_match('/^' . self::CALCULATION_REGEXP_NAMEDRANGE . '.*/Ui', substr($formula, $index), $match)) &&
+ ($output[count($output) - 1]['type'] == 'Named Range' || $output[count($output) - 1]['type'] == 'Value')
+ )) {
while ($stack->count() > 0 &&
($o2 = $stack->last()) &&
isset(self::$operators[$o2['value']]) &&
@@ -3840,7 +3847,6 @@ class Calculation
$fakedForBranchPruning = [];
// help us to know when pruning ['branchTestId' => true/false]
$branchStore = [];
-
// Loop through each token in turn
foreach ($tokens as $tokenData) {
$token = $tokenData['value'];
diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php
index d08ab543..19860794 100644
--- a/src/PhpSpreadsheet/Calculation/DateTime.php
+++ b/src/PhpSpreadsheet/Calculation/DateTime.php
@@ -668,30 +668,19 @@ class DateTime
$endMonths = $PHPEndDateObject->format('n');
$endYears = $PHPEndDateObject->format('Y');
+ $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
+
switch ($unit) {
case 'D':
$retVal = (int) $difference;
break;
case 'M':
- $retVal = (int) ($endMonths - $startMonths) + ((int) ($endYears - $startYears) * 12);
- // We're only interested in full months
- if ($endDays < $startDays) {
- --$retVal;
- }
+ $retVal = (int) 12 * $PHPDiffDateObject->format('%y') + $PHPDiffDateObject->format('%m');
break;
case 'Y':
- $retVal = (int) ($endYears - $startYears);
- // We're only interested in full months
- if ($endMonths < $startMonths) {
- --$retVal;
- } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
- // Remove start month
- --$retVal;
- // Remove end month
- --$retVal;
- }
+ $retVal = (int) $PHPDiffDateObject->format('%y');
break;
case 'MD':
@@ -701,19 +690,12 @@ class DateTime
$adjustDays = $PHPEndDateObject->format('j');
$retVal += ($adjustDays - $startDays);
} else {
- $retVal = $endDays - $startDays;
+ $retVal = (int) $PHPDiffDateObject->format('%d');
}
break;
case 'YM':
- $retVal = (int) ($endMonths - $startMonths);
- if ($retVal < 0) {
- $retVal += 12;
- }
- // We're only interested in full months
- if ($endDays < $startDays) {
- --$retVal;
- }
+ $retVal = (int) $PHPDiffDateObject->format('%m');
break;
case 'YD':
diff --git a/src/PhpSpreadsheet/Cell/Coordinate.php b/src/PhpSpreadsheet/Cell/Coordinate.php
index 8c679913..2afeebe9 100644
--- a/src/PhpSpreadsheet/Cell/Coordinate.php
+++ b/src/PhpSpreadsheet/Cell/Coordinate.php
@@ -312,32 +312,59 @@ abstract class Coordinate
/**
* Extract all cell references in range, which may be comprised of multiple cell ranges.
*
- * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25)
+ * @param string $cellRange Range: e.g. 'A1' or 'A1:C10' or 'A1:E10,A20:E25' or 'A1:E5 C3:G7' or 'A1:C1,A3:C3 B1:C3'
*
* @return array Array containing single cell references
*/
- public static function extractAllCellReferencesInRange($pRange)
+ public static function extractAllCellReferencesInRange($cellRange): array
{
- $returnValue = [];
+ [$ranges, $operators] = self::getCellBlocksFromRangeString($cellRange);
- // Explode spaces
- $cellBlocks = self::getCellBlocksFromRangeString($pRange);
- foreach ($cellBlocks as $cellBlock) {
- $returnValue = array_merge($returnValue, self::getReferencesForCellBlock($cellBlock));
+ $cells = [];
+ foreach ($ranges as $range) {
+ $cells[] = self::getReferencesForCellBlock($range);
}
+ $cells = self::processRangeSetOperators($operators, $cells);
+
+ if (empty($cells)) {
+ return [];
+ }
+
+ $cellList = array_merge(...$cells);
+ $cellList = self::sortCellReferenceArray($cellList);
+
+ return $cellList;
+ }
+
+ private static function processRangeSetOperators(array $operators, array $cells): array
+ {
+ for ($offset = 0; $offset < count($operators); ++$offset) {
+ $operator = $operators[$offset];
+ if ($operator !== ' ') {
+ continue;
+ }
+
+ $cells[$offset] = array_intersect($cells[$offset], $cells[$offset + 1]);
+ unset($operators[$offset], $cells[$offset + 1]);
+ $operators = array_values($operators);
+ $cells = array_values($cells);
+ --$offset;
+ }
+
+ return $cells;
+ }
+
+ private static function sortCellReferenceArray(array $cellList): array
+ {
// Sort the result by column and row
$sortKeys = [];
- foreach (array_unique($returnValue) as $coord) {
- $column = '';
- $row = 0;
-
- sscanf($coord, '%[A-Z]%d', $column, $row);
+ foreach ($cellList as $coord) {
+ [$column, $row] = sscanf($coord, '%[A-Z]%d');
$sortKeys[sprintf('%3s%09d', $column, $row)] = $coord;
}
ksort($sortKeys);
- // Return value
return array_values($sortKeys);
}
@@ -482,15 +509,25 @@ abstract class Coordinate
}
/**
- * Get the individual cell blocks from a range string, splitting by space and removing any $ characters.
+ * Get the individual cell blocks from a range string, removing any $ characters.
+ * then splitting by operators and returning an array with ranges and operators.
*
- * @param string $pRange
+ * @param string $rangeString
*
- * @return string[]
+ * @return array[]
*/
- private static function getCellBlocksFromRangeString($pRange)
+ private static function getCellBlocksFromRangeString($rangeString)
{
- return explode(' ', str_replace('$', '', strtoupper($pRange)));
+ $rangeString = str_replace('$', '', strtoupper($rangeString));
+
+ // split range sets on intersection (space) or union (,) operators
+ $tokens = preg_split('/([ ,])/', $rangeString, -1, PREG_SPLIT_DELIM_CAPTURE);
+ // separate the range sets and the operators into arrays
+ $split = array_chunk($tokens, 2);
+ $ranges = array_column($split, 0);
+ $operators = array_column($split, 1);
+
+ return [$ranges, $operators];
}
/**
diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php
index 797e59ea..fb7f0645 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -763,13 +763,8 @@ class Xlsx extends BaseReader
}
}
- if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) {
- $docSheet->getProtection()->setPassword((string) $xmlSheet->sheetProtection['password'], true);
- if ($xmlSheet->protectedRanges->protectedRange) {
- foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
- $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
- }
- }
+ if ($xmlSheet) {
+ $this->readSheetProtection($docSheet, $xmlSheet);
}
if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
@@ -2031,4 +2026,29 @@ class Xlsx extends BaseReader
return $workbookBasename;
}
+
+ private function readSheetProtection(Worksheet $docSheet, SimpleXMLElement $xmlSheet): void
+ {
+ if ($this->readDataOnly || !$xmlSheet->sheetProtection) {
+ return;
+ }
+
+ $algorithmName = (string) $xmlSheet->sheetProtection['algorithmName'];
+ $protection = $docSheet->getProtection();
+ $protection->setAlgorithm($algorithmName);
+
+ if ($algorithmName) {
+ $protection->setPassword((string) $xmlSheet->sheetProtection['hashValue'], true);
+ $protection->setSalt((string) $xmlSheet->sheetProtection['saltValue']);
+ $protection->setSpinCount((int) $xmlSheet->sheetProtection['spinCount']);
+ } else {
+ $protection->setPassword((string) $xmlSheet->sheetProtection['password'], true);
+ }
+
+ if ($xmlSheet->protectedRanges->protectedRange) {
+ foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
+ $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
+ }
+ }
+ }
}
diff --git a/src/PhpSpreadsheet/Shared/PasswordHasher.php b/src/PhpSpreadsheet/Shared/PasswordHasher.php
index 9b0080b9..9fefe88f 100644
--- a/src/PhpSpreadsheet/Shared/PasswordHasher.php
+++ b/src/PhpSpreadsheet/Shared/PasswordHasher.php
@@ -2,8 +2,41 @@
namespace PhpOffice\PhpSpreadsheet\Shared;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Worksheet\Protection;
+
class PasswordHasher
{
+ /**
+ * Get algorithm name for PHP.
+ */
+ private static function getAlgorithm(string $algorithmName): string
+ {
+ if (!$algorithmName) {
+ return '';
+ }
+
+ // Mapping between algorithm name in Excel and algorithm name in PHP
+ $mapping = [
+ Protection::ALGORITHM_MD2 => 'md2',
+ Protection::ALGORITHM_MD4 => 'md4',
+ Protection::ALGORITHM_MD5 => 'md5',
+ Protection::ALGORITHM_SHA_1 => 'sha1',
+ Protection::ALGORITHM_SHA_256 => 'sha256',
+ Protection::ALGORITHM_SHA_384 => 'sha384',
+ Protection::ALGORITHM_SHA_512 => 'sha512',
+ Protection::ALGORITHM_RIPEMD_128 => 'ripemd128',
+ Protection::ALGORITHM_RIPEMD_160 => 'ripemd160',
+ Protection::ALGORITHM_WHIRLPOOL => 'whirlpool',
+ ];
+
+ if (array_key_exists($algorithmName, $mapping)) {
+ return $mapping[$algorithmName];
+ }
+
+ throw new Exception('Unsupported password algorithm: ' . $algorithmName);
+ }
+
/**
* Create a password hash from a given string.
*
@@ -12,10 +45,8 @@ class PasswordHasher
* Spreadsheet_Excel_Writer by Xavier Noguer .
*
* @param string $pPassword Password to hash
- *
- * @return string Hashed password
*/
- public static function hashPassword($pPassword)
+ private static function defaultHashPassword(string $pPassword): string
{
$password = 0x0000;
$charPos = 1; // char position
@@ -34,4 +65,36 @@ class PasswordHasher
return strtoupper(dechex($password));
}
+
+ /**
+ * Create a password hash from a given string by a specific algorithm.
+ *
+ * 2.4.2.4 ISO Write Protection Method
+ *
+ * @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
+ *
+ * @param string $password Password to hash
+ * @param string $algorithm Hash algorithm used to compute the password hash value
+ * @param string $salt Pseudorandom string
+ * @param int $spinCount Number of times to iterate on a hash of a password
+ *
+ * @return string Hashed password
+ */
+ public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string
+ {
+ $phpAlgorithm = self::getAlgorithm($algorithm);
+ if (!$phpAlgorithm) {
+ return self::defaultHashPassword($password);
+ }
+
+ $saltValue = base64_decode($salt);
+ $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
+
+ $hashValue = hash($phpAlgorithm, $saltValue . $encodedPassword, true);
+ for ($i = 0; $i < $spinCount; ++$i) {
+ $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
+ }
+
+ return base64_encode($hashValue);
+ }
}
diff --git a/src/PhpSpreadsheet/Worksheet/Protection.php b/src/PhpSpreadsheet/Worksheet/Protection.php
index 2fd3e919..81abc0b7 100644
--- a/src/PhpSpreadsheet/Worksheet/Protection.php
+++ b/src/PhpSpreadsheet/Worksheet/Protection.php
@@ -6,6 +6,17 @@ use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher;
class Protection
{
+ const ALGORITHM_MD2 = 'MD2';
+ const ALGORITHM_MD4 = 'MD4';
+ const ALGORITHM_MD5 = 'MD5';
+ const ALGORITHM_SHA_1 = 'SHA-1';
+ const ALGORITHM_SHA_256 = 'SHA-256';
+ const ALGORITHM_SHA_384 = 'SHA-384';
+ const ALGORITHM_SHA_512 = 'SHA-512';
+ const ALGORITHM_RIPEMD_128 = 'RIPEMD-128';
+ const ALGORITHM_RIPEMD_160 = 'RIPEMD-160';
+ const ALGORITHM_WHIRLPOOL = 'WHIRLPOOL';
+
/**
* Sheet.
*
@@ -119,12 +130,40 @@ class Protection
private $selectUnlockedCells = false;
/**
- * Password.
+ * Hashed password.
*
* @var string
*/
private $password = '';
+ /**
+ * Algorithm name.
+ *
+ * @var string
+ */
+ private $algorithm = '';
+
+ /**
+ * Hash value.
+ *
+ * @var string
+ */
+ private $hash = '';
+
+ /**
+ * Salt value.
+ *
+ * @var string
+ */
+ private $salt = '';
+
+ /**
+ * Spin count.
+ *
+ * @var int
+ */
+ private $spinCount = 10000;
+
/**
* Create a new Protection.
*/
@@ -542,7 +581,7 @@ class Protection
}
/**
- * Get Password (hashed).
+ * Get hashed password.
*
* @return string
*/
@@ -562,13 +601,86 @@ class Protection
public function setPassword($pValue, $pAlreadyHashed = false)
{
if (!$pAlreadyHashed) {
- $pValue = PasswordHasher::hashPassword($pValue);
+ $salt = $this->generateSalt();
+ $this->setSalt($salt);
+ $pValue = PasswordHasher::hashPassword($pValue, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
}
+
$this->password = $pValue;
return $this;
}
+ /**
+ * Create a pseudorandom string.
+ */
+ private function generateSalt(): string
+ {
+ return base64_encode(random_bytes(16));
+ }
+
+ /**
+ * Get algorithm name.
+ */
+ public function getAlgorithm(): string
+ {
+ return $this->algorithm;
+ }
+
+ /**
+ * Set algorithm name.
+ */
+ public function setAlgorithm(string $algorithm): void
+ {
+ $this->algorithm = $algorithm;
+ }
+
+ /**
+ * Get salt value.
+ */
+ public function getSalt(): string
+ {
+ return $this->salt;
+ }
+
+ /**
+ * Set salt value.
+ */
+ public function setSalt(string $salt): void
+ {
+ $this->salt = $salt;
+ }
+
+ /**
+ * Get spin count.
+ */
+ public function getSpinCount(): int
+ {
+ return $this->spinCount;
+ }
+
+ /**
+ * Set spin count.
+ */
+ public function setSpinCount(int $spinCount): void
+ {
+ $this->spinCount = $spinCount;
+ }
+
+ /**
+ * Verify that the given non-hashed password can "unlock" the protection.
+ */
+ public function verify(string $password): bool
+ {
+ if (!$this->isProtectionEnabled()) {
+ return true;
+ }
+
+ $hash = PasswordHasher::hashPassword($password, $this->getAlgorithm(), $this->getSalt(), $this->getSpinCount());
+
+ return $this->getPassword() === $hash;
+ }
+
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php
index 1ed77e05..e2b0dd87 100644
--- a/src/PhpSpreadsheet/Worksheet/Worksheet.php
+++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -187,7 +187,7 @@ class Worksheet implements IComparable
/**
* Collection of merged cell ranges.
*
- * @var array
+ * @var string[]
*/
private $mergeCells = [];
@@ -1747,7 +1747,7 @@ class Worksheet implements IComparable
/**
* Get merge cells array.
*
- * @return array[]
+ * @return string[]
*/
public function getMergeCells()
{
@@ -1758,6 +1758,8 @@ class Worksheet implements IComparable
* Set merge cells array for the entire sheet. Use instead mergeCells() to merge
* a single cell range.
*
+ * @param string[] $pValue
+ *
* @return $this
*/
public function setMergeCells(array $pValue)
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
index 3d47eeaa..d101bb40 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
@@ -420,26 +420,33 @@ class Worksheet extends WriterPart
// sheetProtection
$objWriter->startElement('sheetProtection');
- if ($pSheet->getProtection()->getPassword() !== '') {
- $objWriter->writeAttribute('password', $pSheet->getProtection()->getPassword());
+ $protection = $pSheet->getProtection();
+
+ if ($protection->getAlgorithm()) {
+ $objWriter->writeAttribute('algorithmName', $protection->getAlgorithm());
+ $objWriter->writeAttribute('hashValue', $protection->getPassword());
+ $objWriter->writeAttribute('saltValue', $protection->getSalt());
+ $objWriter->writeAttribute('spinCount', $protection->getSpinCount());
+ } elseif ($protection->getPassword() !== '') {
+ $objWriter->writeAttribute('password', $protection->getPassword());
}
- $objWriter->writeAttribute('sheet', ($pSheet->getProtection()->getSheet() ? 'true' : 'false'));
- $objWriter->writeAttribute('objects', ($pSheet->getProtection()->getObjects() ? 'true' : 'false'));
- $objWriter->writeAttribute('scenarios', ($pSheet->getProtection()->getScenarios() ? 'true' : 'false'));
- $objWriter->writeAttribute('formatCells', ($pSheet->getProtection()->getFormatCells() ? 'true' : 'false'));
- $objWriter->writeAttribute('formatColumns', ($pSheet->getProtection()->getFormatColumns() ? 'true' : 'false'));
- $objWriter->writeAttribute('formatRows', ($pSheet->getProtection()->getFormatRows() ? 'true' : 'false'));
- $objWriter->writeAttribute('insertColumns', ($pSheet->getProtection()->getInsertColumns() ? 'true' : 'false'));
- $objWriter->writeAttribute('insertRows', ($pSheet->getProtection()->getInsertRows() ? 'true' : 'false'));
- $objWriter->writeAttribute('insertHyperlinks', ($pSheet->getProtection()->getInsertHyperlinks() ? 'true' : 'false'));
- $objWriter->writeAttribute('deleteColumns', ($pSheet->getProtection()->getDeleteColumns() ? 'true' : 'false'));
- $objWriter->writeAttribute('deleteRows', ($pSheet->getProtection()->getDeleteRows() ? 'true' : 'false'));
- $objWriter->writeAttribute('selectLockedCells', ($pSheet->getProtection()->getSelectLockedCells() ? 'true' : 'false'));
- $objWriter->writeAttribute('sort', ($pSheet->getProtection()->getSort() ? 'true' : 'false'));
- $objWriter->writeAttribute('autoFilter', ($pSheet->getProtection()->getAutoFilter() ? 'true' : 'false'));
- $objWriter->writeAttribute('pivotTables', ($pSheet->getProtection()->getPivotTables() ? 'true' : 'false'));
- $objWriter->writeAttribute('selectUnlockedCells', ($pSheet->getProtection()->getSelectUnlockedCells() ? 'true' : 'false'));
+ $objWriter->writeAttribute('sheet', ($protection->getSheet() ? 'true' : 'false'));
+ $objWriter->writeAttribute('objects', ($protection->getObjects() ? 'true' : 'false'));
+ $objWriter->writeAttribute('scenarios', ($protection->getScenarios() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatCells', ($protection->getFormatCells() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatColumns', ($protection->getFormatColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatRows', ($protection->getFormatRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertColumns', ($protection->getInsertColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertRows', ($protection->getInsertRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertHyperlinks', ($protection->getInsertHyperlinks() ? 'true' : 'false'));
+ $objWriter->writeAttribute('deleteColumns', ($protection->getDeleteColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('deleteRows', ($protection->getDeleteRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('selectLockedCells', ($protection->getSelectLockedCells() ? 'true' : 'false'));
+ $objWriter->writeAttribute('sort', ($protection->getSort() ? 'true' : 'false'));
+ $objWriter->writeAttribute('autoFilter', ($protection->getAutoFilter() ? 'true' : 'false'));
+ $objWriter->writeAttribute('pivotTables', ($protection->getPivotTables() ? 'true' : 'false'));
+ $objWriter->writeAttribute('selectUnlockedCells', ($protection->getSelectUnlockedCells() ? 'true' : 'false'));
$objWriter->endElement();
}
@@ -1133,7 +1140,7 @@ class Worksheet extends WriterPart
$this->getParentWriter()->getOffice2003Compatibility() === false,
'v',
($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#')
- ? StringHelper::formatNumber($calculatedValue) : '0'
+ ? StringHelper::formatNumber($calculatedValue) : '0'
);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php b/tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php
index 84cac747..ee566db2 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php
@@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Engine;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
@@ -28,7 +29,7 @@ class RangeTest extends TestCase
/**
* @dataProvider providerRangeEvaluation
*
- * @param mixed $formula
+ * @param string $formula
* @param int $expectedResult
*/
public function testRangeEvaluation($formula, $expectedResult): void
@@ -44,11 +45,93 @@ class RangeTest extends TestCase
{
return[
['=SUM(A1:B3,A1:C2)', 48],
+ ['=COUNT(A1:B3,A1:C2)', 12],
['=SUM(A1:B3 A1:C2)', 12],
+ ['=COUNT(A1:B3 A1:C2)', 4],
['=SUM(A1:A3,C1:C3)', 30],
+ ['=COUNT(A1:A3,C1:C3)', 6],
['=SUM(A1:A3 C1:C3)', Functions::null()],
+ ['=COUNT(A1:A3 C1:C3)', 0],
['=SUM(A1:B2,B2:C3)', 40],
+ ['=COUNT(A1:B2,B2:C3)', 8],
['=SUM(A1:B2 B2:C3)', 5],
+ ['=COUNT(A1:B2 B2:C3)', 1],
+ ['=SUM(A1:C1,A3:C3,B1:C3)', 63],
+ ['=COUNT(A1:C1,A3:C3,B1:C3)', 12],
+ ['=SUM(A1:C1,A3:C3 B1:C3)', 23],
+ ['=COUNT(A1:C1,A3:C3 B1:C3)', 5],
+ ];
+ }
+
+ /**
+ * @dataProvider providerNamedRangeEvaluation
+ *
+ * @param string $group1
+ * @param string $group2
+ * @param string $formula
+ * @param int $expectedResult
+ */
+ public function testNamedRangeEvaluation($group1, $group2, $formula, $expectedResult): void
+ {
+ $workSheet = $this->spreadSheet->getActiveSheet();
+ $this->spreadSheet->addNamedRange(new NamedRange('GROUP1', $workSheet, $group1));
+ $this->spreadSheet->addNamedRange(new NamedRange('GROUP2', $workSheet, $group2));
+
+ $workSheet->setCellValue('E1', $formula);
+
+ $sumRresult = $workSheet->getCell('E1')->getCalculatedValue();
+ self::assertSame($expectedResult, $sumRresult);
+ }
+
+ public function providerNamedRangeEvaluation()
+ {
+ return[
+ ['A1:B3', 'A1:C2', '=SUM(GROUP1,GROUP2)', 48],
+ ['A1:B3', 'A1:C2', '=COUNT(GROUP1,GROUP2)', 12],
+ ['A1:B3', 'A1:C2', '=SUM(GROUP1 GROUP2)', 12],
+ ['A1:B3', 'A1:C2', '=COUNT(GROUP1 GROUP2)', 4],
+ ['A1:B2', 'B2:C3', '=SUM(GROUP1,GROUP2)', 40],
+ ['A1:B2', 'B2:C3', '=COUNT(GROUP1,GROUP2)', 8],
+ ['A1:B2', 'B2:C3', '=SUM(GROUP1 GROUP2)', 5],
+ ['A1:B2', 'B2:C3', '=COUNT(GROUP1 GROUP2)', 1],
+ ];
+ }
+
+ /**
+ * @dataProvider providerCompositeNamedRangeEvaluation
+ *
+ * @param string $composite
+ * @param int $expectedSum
+ * @param int $expectedCount
+ */
+ public function testCompositeNamedRangeEvaluation($composite, $expectedSum, $expectedCount): void
+ {
+ $workSheet = $this->spreadSheet->getActiveSheet();
+ $this->spreadSheet->addNamedRange(new NamedRange('COMPOSITE', $workSheet, $composite));
+
+ $workSheet->setCellValue('E1', '=SUM(COMPOSITE)');
+ $workSheet->setCellValue('E2', '=COUNT(COMPOSITE)');
+
+ $actualSum = $workSheet->getCell('E1')->getCalculatedValue();
+ self::assertSame($expectedSum, $actualSum);
+ $actualCount = $workSheet->getCell('E2')->getCalculatedValue();
+ self::assertSame($expectedCount, $actualCount);
+ }
+
+ public function providerCompositeNamedRangeEvaluation()
+ {
+ return[
+ // Calculation engine doesn't yet handle union ranges with overlap
+ // 'Union with overlap' => [
+ // 'A1:C1,A3:C3,B1:C3',
+ // 63,
+ // 12,
+ // ],
+ 'Intersection' => [
+ 'A1:C1,A3:C3 B1:C3',
+ 23,
+ 5,
+ ],
];
}
}
diff --git a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
index 37579e80..8e0e98a9 100644
--- a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
+++ b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
@@ -83,10 +83,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerCoordinates
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testCoordinateFromString($expectedResult, ...$args): void
+ public function testCoordinateFromString($expectedResult, $rangeSet): void
{
- $result = Coordinate::coordinateFromString(...$args);
+ $result = Coordinate::coordinateFromString($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -143,11 +144,12 @@ class CoordinateTest extends TestCase
/**
* @dataProvider providerAbsoluteCoordinates
*
- * @param mixed $expectedResult
+ * @param string $expectedResult
+ * @param string $rangeSet
*/
- public function testAbsoluteCoordinateFromString($expectedResult, ...$args): void
+ public function testAbsoluteCoordinateFromString($expectedResult, $rangeSet): void
{
- $result = Coordinate::absoluteCoordinate(...$args);
+ $result = Coordinate::absoluteCoordinate($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -175,10 +177,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerAbsoluteReferences
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testAbsoluteReferenceFromString($expectedResult, ...$args): void
+ public function testAbsoluteReferenceFromString($expectedResult, $rangeSet): void
{
- $result = Coordinate::absoluteReference(...$args);
+ $result = Coordinate::absoluteReference($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -206,10 +209,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerSplitRange
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testSplitRange($expectedResult, ...$args): void
+ public function testSplitRange($expectedResult, $rangeSet): void
{
- $result = Coordinate::splitRange(...$args);
+ $result = Coordinate::splitRange($rangeSet);
foreach ($result as $key => $split) {
if (!is_array($expectedResult[$key])) {
self::assertEquals($expectedResult[$key], $split[0]);
@@ -252,10 +256,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerRangeBoundaries
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testRangeBoundaries($expectedResult, ...$args): void
+ public function testRangeBoundaries($expectedResult, $rangeSet): void
{
- $result = Coordinate::rangeBoundaries(...$args);
+ $result = Coordinate::rangeBoundaries($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -268,10 +273,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerRangeDimension
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testRangeDimension($expectedResult, ...$args): void
+ public function testRangeDimension($expectedResult, $rangeSet): void
{
- $result = Coordinate::rangeDimension(...$args);
+ $result = Coordinate::rangeDimension($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -284,10 +290,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerGetRangeBoundaries
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testGetRangeBoundaries($expectedResult, ...$args): void
+ public function testGetRangeBoundaries($expectedResult, $rangeSet): void
{
- $result = Coordinate::getRangeBoundaries(...$args);
+ $result = Coordinate::getRangeBoundaries($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -299,11 +306,12 @@ class CoordinateTest extends TestCase
/**
* @dataProvider providerExtractAllCellReferencesInRange
*
- * @param mixed $expectedResult
+ * @param array $expectedResult
+ * @param string $rangeSet
*/
- public function testExtractAllCellReferencesInRange($expectedResult, ...$args): void
+ public function testExtractAllCellReferencesInRange($expectedResult, $rangeSet): void
{
- $result = Coordinate::extractAllCellReferencesInRange(...$args);
+ $result = Coordinate::extractAllCellReferencesInRange($rangeSet);
self::assertEquals($expectedResult, $result);
}
@@ -350,10 +358,11 @@ class CoordinateTest extends TestCase
* @dataProvider providerCoordinateIsRange
*
* @param mixed $expectedResult
+ * @param string $rangeSet
*/
- public function testCoordinateIsRange($expectedResult, ...$args): void
+ public function testCoordinateIsRange($expectedResult, $rangeSet): void
{
- $result = Coordinate::coordinateIsRange(...$args);
+ $result = Coordinate::coordinateIsRange($rangeSet);
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Worksheet/ProtectionTest.php b/tests/PhpSpreadsheetTests/Worksheet/ProtectionTest.php
new file mode 100644
index 00000000..1cc1ed32
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Worksheet/ProtectionTest.php
@@ -0,0 +1,39 @@
+verify('foo'), 'non-protected always pass');
+
+ $protection->setSheet(true);
+ self::assertFalse($protection->verify('foo'), 'protected will fail');
+
+ $protection->setPassword('foo', true);
+ self::assertSame('foo', $protection->getPassword(), 'was not stored as-is, without hashing');
+ self::assertFalse($protection->verify('foo'), 'setting already hashed password will not match');
+
+ $protection->setPassword('foo');
+ self::assertSame('CC40', $protection->getPassword(), 'was hashed');
+ self::assertTrue($protection->verify('foo'), 'setting non-hashed password will hash it and not match');
+
+ $protection->setAlgorithm(Protection::ALGORITHM_MD5);
+ self::assertFalse($protection->verify('foo'), 'changing algorithm will not match anymore');
+
+ $protection->setPassword('foo');
+ $hash1 = $protection->getPassword();
+ $protection->setPassword('foo');
+ $hash2 = $protection->getPassword();
+
+ self::assertSame(24, mb_strlen($hash1));
+ self::assertSame(24, mb_strlen($hash2));
+ self::assertNotSame($hash1, $hash2, 'was hashed with automatic salt');
+ self::assertTrue($protection->verify('foo'), 'setting password again, will hash with proper algorithm and will match');
+ }
+}
diff --git a/tests/data/Calculation/DateTime/DATEDIF.php b/tests/data/Calculation/DateTime/DATEDIF.php
index d113d3aa..a6d2d761 100644
--- a/tests/data/Calculation/DateTime/DATEDIF.php
+++ b/tests/data/Calculation/DateTime/DATEDIF.php
@@ -393,6 +393,10 @@ return [
1,
'19-12-1960', '26-01-2012', 'YM',
],
+ [
+ 11,
+ '19-12-1960', '26-11-1962', 'YM',
+ ],
[
38,
'19-12-1960', '26-01-2012', 'YD',
@@ -402,7 +406,15 @@ return [
'19-12-1960', '26-01-2012', 'MD',
],
[
- 50,
+ 0,
+ '19-12-1960', '12-12-1961', 'Y',
+ ],
+ [
+ 1,
+ '19-12-1960', '12-12-1962', 'Y',
+ ],
+ [
+ 51,
'19-12-1960', '12-12-2012', 'Y',
],
[
diff --git a/tests/data/CellExtractAllCellReferencesInRange.php b/tests/data/CellExtractAllCellReferencesInRange.php
index cf093289..b005b1fe 100644
--- a/tests/data/CellExtractAllCellReferencesInRange.php
+++ b/tests/data/CellExtractAllCellReferencesInRange.php
@@ -22,12 +22,6 @@ return [
],
[
[
- 'B4',
- 'B5',
- 'B6',
- 'D4',
- 'D5',
- 'D6',
],
'B4:B6 D4:D6',
],
@@ -66,20 +60,10 @@ return [
],
[
[
- 'B4',
- 'B5',
- 'B6',
- 'C4',
'C5',
'C6',
- 'C7',
- 'D4',
'D5',
'D6',
- 'D7',
- 'E5',
- 'E6',
- 'E7',
],
'B4:D6 C5:E7',
],
@@ -105,7 +89,7 @@ return [
'F5',
'F6',
],
- 'B2:D4 C5:D5 E3:E5 D6:E6 F4:F6',
+ 'B2:D4,C5:D5,E3:E5,D6:E6,F4:F6',
],
[
[
@@ -129,16 +113,13 @@ return [
'F5',
'F6',
],
- 'B2:D4 C3:E5 D4:F6',
+ 'B2:D4,C3:E5,D4:F6',
],
[
[
- 'B4',
'B5',
- 'B6',
- 'B8',
],
- 'B4:B6 B8',
+ 'B4:B6 B5',
],
[
[
diff --git a/tests/data/Shared/PasswordHashes.php b/tests/data/Shared/PasswordHashes.php
index b4f348ca..34c25cef 100644
--- a/tests/data/Shared/PasswordHashes.php
+++ b/tests/data/Shared/PasswordHashes.php
@@ -25,4 +25,30 @@ return [
'CE4B',
'',
],
+ [
+ 'O6EXRLpLEDNJDL/AzYtnnA4O4bY=',
+ '',
+ 'SHA-1',
+ ],
+ [
+ 'GYvlIMljDI1Czc4jfWrGaxU5pxl9n5Og0KUzyAfYxwk=',
+ 'PhpSpreadsheet',
+ 'SHA-256',
+ 'Php_salt',
+ 1000,
+ ],
+ [
+ 'sSHdxQv9qgpkr4LDT0bYQxM9hOQJFRhJ4D752/NHQtDDR1EVy67NCEW9cPd6oWvCoBGd96MqKpuma1A7pN1nEA==',
+ 'Mark Baker',
+ 'SHA-512',
+ 'Mark_salt',
+ 10000,
+ ],
+ [
+ 'r9KVLLCKIYOILvE2rcby+g==',
+ '!+&=()~§±æþ',
+ 'MD5',
+ 'Symbols_salt',
+ 100000,
+ ],
];