diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index a393043d..8a8886c2 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -26,7 +26,7 @@ $config
'combine_consecutive_issets' => true,
'combine_consecutive_unsets' => true,
'combine_nested_dirname' => true,
- 'comment_to_phpdoc' => true,
+ 'comment_to_phpdoc' => false, // interferes with annotations
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'constant_case' => true,
@@ -171,7 +171,7 @@ $config
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => true,
- 'phpdoc_to_comment' => true,
+ 'phpdoc_to_comment' => false, // interferes with annotations
'phpdoc_to_param_type' => false, // Because experimental, but interesting for one shot use
'phpdoc_to_return_type' => false, // idem
'phpdoc_trim' => true,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ff894ab..c7ae1854 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added
-- Implementation of the ISREF() information function.
+- Implementation of the FILTER(), SORT(), SORTBY() and UNIQUE() Lookup/Reference (array) functions
+- Implementation of the ISREF() Information function.
- Added support for reading "formatted" numeric values from Csv files; although default behaviour of reading these values as strings is preserved.
(i.e a value of "12,345.67" can be read as numeric `1235.67`, not simply as a string `"12,345.67"`, if the `castFormattedNumberToNumeric()` setting is enabled.
@@ -17,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org).
This functionality is locale-aware, using the server's locale settings to identify the thousands and decimal separators.
- Support for two cell anchor drawing of images. [#2532](https://github.com/PHPOffice/PhpSpreadsheet/pull/2532)
+- Limited support for Xls Reader to handle Conditional Formatting:
+
+ Ranges and Rules are read, but style is currently limited to font size, weight and color; and to fill style and color.
### Changed
@@ -28,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
This is determined by the Calculation Engine locale setting.
(i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.)
+- Allow `psr/simple-cache` 2.x
### Deprecated
@@ -39,6 +44,11 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
+- Update Conditional Formatting ranges and rule conditions when inserting/deleting rows/columns [Issue #2678](https://github.com/PHPOffice/PhpSpreadsheet/issues/2678) [PR #2689](https://github.com/PHPOffice/PhpSpreadsheet/pull/2689)
+- Allow `INDIRECT()` to accept row/column ranges as well as cell ranges [PR #2687](https://github.com/PHPOffice/PhpSpreadsheet/pull/2687)
+- Fix bug when deleting cells with hyperlinks, where the hyperlink was then being "inherited" by whatever cell moved to that cell address.
+- Fix bug in Conditional Formatting in the Xls Writer that resulted in a broken file when there were multiple conditional ranges in a worksheet.
+- Fix Conditional Formatting in the Xls Writer to work with rules that contain string literals, cell references and formulae.
- Fix for setting Active Sheet to the first loaded worksheet when bookViews element isn't defined [Issue #2666](https://github.com/PHPOffice/PhpSpreadsheet/issues/2666) [PR #2669](https://github.com/PHPOffice/PhpSpreadsheet/pull/2669)
- Fixed behaviour of XLSX font style vertical align settings.
- Resolved formula translations to handle separators (row and column) for array functions as well as for function argument separators; and cleanly handle nesting levels.
diff --git a/composer.json b/composer.json
index c0664de3..a0064df2 100644
--- a/composer.json
+++ b/composer.json
@@ -75,7 +75,7 @@
"markbaker/matrix": "^3.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
- "psr/simple-cache": "^1.0"
+ "psr/simple-cache": "^1.0 || ^2.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
diff --git a/composer.lock b/composer.lock
index 5434aa3a..7b76a2c4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "9dadb265f548dd4ddbfd2ee97d5a6aa6",
+ "content-hash": "ed42c40a4281d97171980367f24d0a25",
"packages": [
{
"name": "ezyang/htmlpurifier",
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 84af87e7..669c512a 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -2300,16 +2300,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Reader/Xls.php
- -
- message: "#^Parameter \\#1 \\$errorStyle of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setErrorStyle\\(\\) expects string, int\\|string given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Reader/Xls.php
-
- -
- message: "#^Parameter \\#1 \\$operator of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setOperator\\(\\) expects string, int\\|string given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Reader/Xls.php
-
-
message: "#^Parameter \\#1 \\$showSummaryBelow of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:setShowSummaryBelow\\(\\) expects bool, int given\\.$#"
count: 1
@@ -2320,11 +2310,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Xls.php
- -
- message: "#^Parameter \\#1 \\$type of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setType\\(\\) expects string, int\\|string given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Reader/Xls.php
-
-
message: "#^Parameter \\#2 \\$row of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReadFilter\\:\\:readCell\\(\\) expects int, string given\\.$#"
count: 1
@@ -3165,31 +3150,11 @@ parameters:
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
- -
- message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\:\\:setRowIndex\\(\\) expects int, string given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/ReferenceHelper.php
-
- -
- message: "#^Parameter \\#2 \\$callback of function uksort expects callable\\(\\(int\\|string\\), \\(int\\|string\\)\\)\\: int, array\\{'self', 'cellReverseSort'\\} given\\.$#"
- count: 4
- path: src/PhpSpreadsheet/ReferenceHelper.php
-
- -
- message: "#^Parameter \\#2 \\$callback of function uksort expects callable\\(\\(int\\|string\\), \\(int\\|string\\)\\)\\: int, array\\{'self', 'cellSort'\\} given\\.$#"
- count: 4
- path: src/PhpSpreadsheet/ReferenceHelper.php
-
-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
- -
- message: "#^Static property PhpOffice\\\\PhpSpreadsheet\\\\ReferenceHelper\\:\\:\\$instance \\(PhpOffice\\\\PhpSpreadsheet\\\\ReferenceHelper\\) in isset\\(\\) is not nullable\\.$#"
- count: 1
- path: src/PhpSpreadsheet/ReferenceHelper.php
-
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\:\\:\\$font \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
@@ -4240,26 +4205,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
- -
- message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
- -
- message: "#^Cannot call method getDrawingCollection\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
- -
- message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
- -
- message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\BaseDrawing\\:\\:\\$shadow \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\|null\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Worksheet/BaseDrawing.php
-
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\CellIterator\\:\\:adjustForExistingOnlyRange\\(\\) has no return type specified\\.$#"
count: 1
@@ -5270,36 +5215,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
- -
- message: "#^Parameter \\#1 \\$coordinates of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:indexesFromString\\(\\) expects string, string\\|null given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
- -
- message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getChartByIndex\\(\\) expects string, int\\<0, max\\> given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
- -
- message: "#^Parameter \\#2 \\$chart of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Drawing\\:\\:writeChart\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\|false given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
- -
- message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int given\\.$#"
- count: 20
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
- -
- message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#"
- count: 10
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
- -
- message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int\\<0, max\\> given\\.$#"
- count: 1
- path: src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
-
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Rels\\:\\:writeUnparsedRelationship\\(\\) has parameter \\$relationship with no type specified\\.$#"
count: 1
@@ -5554,3 +5469,4 @@ parameters:
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Xlfn\\:\\:addXlfn\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php
+
diff --git a/samples/Basic/44_Worksheet_info.php b/samples/Basic/44_Worksheet_info.php
index 57822369..406c7be2 100644
--- a/samples/Basic/44_Worksheet_info.php
+++ b/samples/Basic/44_Worksheet_info.php
@@ -1,18 +1,19 @@
getTemporaryFilename();
-$writer = new Xlsx($sampleSpreadsheet);
+$writer = new Writer($sampleSpreadsheet);
$writer->save($filename);
$inputFileType = IOFactory::identify($filename);
-$reader = IOFactory::createReader($inputFileType);
+$reader = new Reader();
$sheetList = $reader->listWorksheetNames($filename);
$sheetInfo = $reader->listWorksheetInfo($filename);
diff --git a/samples/Basic/48_Image_move_size_with_cells.php b/samples/Basic/48_Image_move_size_with_cells.php
new file mode 100644
index 00000000..abf7ee34
--- /dev/null
+++ b/samples/Basic/48_Image_move_size_with_cells.php
@@ -0,0 +1,78 @@
+log('Create new Spreadsheet object');
+$spreadsheet = new Spreadsheet();
+$sheet = $spreadsheet->getActiveSheet();
+$sheet->getCell('A1')->setValue('twocell');
+$sheet->getCell('A2')->setValue('twocell');
+$sheet->getCell('A3')->setValue('onecell');
+$sheet->getCell('A6')->setValue('absolute');
+
+// Add a drawing to the worksheet
+$helper->log('Add a drawing to the worksheet two-cell anchor not resized');
+$drawing = new Drawing();
+$drawing->setName('PhpSpreadsheet');
+$drawing->setDescription('PhpSpreadsheet');
+$drawing->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
+// anchor type will be two-cell because Coordinates2 is set
+//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
+$drawing->setCoordinates('B1');
+$drawing->setCoordinates2('B1');
+$drawing->setOffsetX2($drawing->getImageWidth());
+$drawing->setOffsetY2($drawing->getImageHeight());
+$drawing->setWorksheet($spreadsheet->getActiveSheet());
+
+// Add a drawing to the worksheet
+$helper->log('Add a drawing to the worksheet two-cell anchor resized');
+$drawing2 = new Drawing();
+$drawing2->setName('PhpSpreadsheet');
+$drawing2->setDescription('PhpSpreadsheet');
+$drawing2->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
+// anchor type will be two-cell because Coordinates2 is set
+//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
+$drawing2->setCoordinates('C2');
+$drawing2->setCoordinates2('C2');
+$drawing2->setOffsetX2($drawing->getImageWidth());
+$drawing2->setOffsetY2($drawing->getImageHeight());
+$drawing2->setWorksheet($spreadsheet->getActiveSheet());
+
+$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth($drawing->getImageWidth(), Dimension::UOM_PIXELS);
+$spreadsheet->getActiveSheet()->getRowDimension(2)->setRowHeight($drawing->getImageHeight(), Dimension::UOM_PIXELS);
+
+// Add a drawing to the worksheet one cell anchor
+$helper->log('Add a drawing to the worksheet one-cell anchor');
+$drawing3 = new Drawing();
+$drawing3->setName('PhpSpreadsheet');
+$drawing3->setDescription('PhpSpreadsheet');
+$drawing3->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
+// anchor type will be one-cell because Coordinates2 is not set
+//$drawing->setAnchorType(Drawing::ANCHORTYPE_ONECELL);
+$drawing3->setCoordinates('D3');
+$drawing3->setWorksheet($spreadsheet->getActiveSheet());
+
+// Add a drawing to the worksheet
+$helper->log('Add a drawing to the worksheet two-cell anchor resized absolute');
+$drawing4 = new Drawing();
+$drawing4->setName('PhpSpreadsheet');
+$drawing4->setDescription('PhpSpreadsheet');
+$drawing4->setPath(__DIR__ . '/../images/PhpSpreadsheet_logo.png');
+// anchor type will be two-cell because Coordinates2 is set
+//$drawing->setAnchorType(Drawing::ANCHORTYPE_TWOCELL);
+$drawing4->setCoordinates('C6');
+$drawing4->setCoordinates2('C6');
+$drawing4->setOffsetX2($drawing->getImageWidth());
+$drawing4->setOffsetY2($drawing->getImageHeight());
+$drawing4->setWorksheet($spreadsheet->getActiveSheet());
+$drawing4->setEditAs(Drawing::EDIT_AS_ABSOLUTE);
+
+//$spreadsheet->getActiveSheet()->getColumnDimension('C')->setWidth($drawing->getImageWidth(), Dimension::UOM_PIXELS);
+$spreadsheet->getActiveSheet()->getRowDimension(6)->setRowHeight($drawing->getImageHeight(), Dimension::UOM_PIXELS);
+
+$helper->write($spreadsheet, __FILE__, ['Xlsx']);
diff --git a/samples/Chart/33_Chart_create_area.php b/samples/Chart/33_Chart_create_area.php
index 57db90fc..95d9149c 100644
--- a/samples/Chart/33_Chart_create_area.php
+++ b/samples/Chart/33_Chart_create_area.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -71,7 +71,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_bar_stacked.php b/samples/Chart/33_Chart_create_bar_stacked.php
index 0c87224e..bb2d8f6d 100644
--- a/samples/Chart/33_Chart_create_bar_stacked.php
+++ b/samples/Chart/33_Chart_create_bar_stacked.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -74,7 +74,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_BAR);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_column.php b/samples/Chart/33_Chart_create_column.php
index 5af0908c..68aa983d 100644
--- a/samples/Chart/33_Chart_create_column.php
+++ b/samples/Chart/33_Chart_create_column.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -74,7 +74,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Column Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_column_2.php b/samples/Chart/33_Chart_create_column_2.php
index a62b4906..96f5e316 100644
--- a/samples/Chart/33_Chart_create_column_2.php
+++ b/samples/Chart/33_Chart_create_column_2.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -82,7 +82,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_BOTTOM, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_BOTTOM, null, false);
$title = new Title('Test Grouped Column Chart');
$xAxisLabel = new Title('Financial Period');
diff --git a/samples/Chart/33_Chart_create_composite.php b/samples/Chart/33_Chart_create_composite.php
index ce42d2fc..b2952420 100644
--- a/samples/Chart/33_Chart_create_composite.php
+++ b/samples/Chart/33_Chart_create_composite.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -128,7 +128,7 @@ $series3 = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series1, $series2, $series3]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Average Weather Chart for Crete');
diff --git a/samples/Chart/33_Chart_create_line.php b/samples/Chart/33_Chart_create_line.php
index feae2f27..fee2a284 100644
--- a/samples/Chart/33_Chart_create_line.php
+++ b/samples/Chart/33_Chart_create_line.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -72,7 +72,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test Stacked Line Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_multiple_charts.php b/samples/Chart/33_Chart_create_multiple_charts.php
index 608ffc53..3032bc28 100644
--- a/samples/Chart/33_Chart_create_multiple_charts.php
+++ b/samples/Chart/33_Chart_create_multiple_charts.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -71,7 +71,7 @@ $series1 = new DataSeries(
// Set the series in the plot area
$plotArea1 = new PlotArea(null, [$series1]);
// Set the chart legend
-$legend1 = new Legend(Legend::POSITION_TOPRIGHT, null, false);
+$legend1 = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title1 = new Title('Test %age-Stacked Area Chart');
$yAxisLabel1 = new Title('Value ($k)');
@@ -146,7 +146,7 @@ $series2->setPlotDirection(DataSeries::DIRECTION_COL);
// Set the series in the plot area
$plotArea2 = new PlotArea(null, [$series2]);
// Set the chart legend
-$legend2 = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend2 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title2 = new Title('Test Column Chart');
$yAxisLabel2 = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_pie.php b/samples/Chart/33_Chart_create_pie.php
index 5480a18a..4b35b24e 100644
--- a/samples/Chart/33_Chart_create_pie.php
+++ b/samples/Chart/33_Chart_create_pie.php
@@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -73,7 +73,7 @@ $layout1->setShowPercent(true);
// Set the series in the plot area
$plotArea1 = new PlotArea($layout1, [$series1]);
// Set the chart legend
-$legend1 = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title1 = new Title('Test Pie Chart');
diff --git a/samples/Chart/33_Chart_create_pie_custom_colors.php b/samples/Chart/33_Chart_create_pie_custom_colors.php
index ca5397a1..91f7e51b 100644
--- a/samples/Chart/33_Chart_create_pie_custom_colors.php
+++ b/samples/Chart/33_Chart_create_pie_custom_colors.php
@@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -79,7 +79,7 @@ $layout1->setShowPercent(true);
// Set the series in the plot area
$plotArea1 = new PlotArea($layout1, [$series1]);
// Set the chart legend
-$legend1 = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title1 = new Title('Test Pie Chart');
diff --git a/samples/Chart/33_Chart_create_radar.php b/samples/Chart/33_Chart_create_radar.php
index eba4dc39..59a8a5d9 100644
--- a/samples/Chart/33_Chart_create_radar.php
+++ b/samples/Chart/33_Chart_create_radar.php
@@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -85,7 +85,7 @@ $layout = new Layout();
// Set the series in the plot area
$plotArea = new PlotArea($layout, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Radar Chart');
diff --git a/samples/Chart/33_Chart_create_scatter.php b/samples/Chart/33_Chart_create_scatter.php
index c67e4e95..9a54c18b 100644
--- a/samples/Chart/33_Chart_create_scatter.php
+++ b/samples/Chart/33_Chart_create_scatter.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -68,7 +68,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_TOPRIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test Scatter Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/samples/Chart/33_Chart_create_stock.php b/samples/Chart/33_Chart_create_stock.php
index 58686784..34fa3a6c 100644
--- a/samples/Chart/33_Chart_create_stock.php
+++ b/samples/Chart/33_Chart_create_stock.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -79,7 +79,7 @@ $series = new DataSeries(
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Stock Chart');
$xAxisLabel = new Title('Counts');
diff --git a/samples/ConditionalFormatting/01_Basic_Comparisons.php b/samples/ConditionalFormatting/01_Basic_Comparisons.php
index b201005b..5c5381ae 100644
--- a/samples/ConditionalFormatting/01_Basic_Comparisons.php
+++ b/samples/ConditionalFormatting/01_Basic_Comparisons.php
@@ -30,7 +30,8 @@ $spreadsheet->getActiveSheet()
->setCellValue('A1', 'Literal Value Comparison')
->setCellValue('A9', 'Value Comparison with Absolute Cell Reference $H$9')
->setCellValue('A17', 'Value Comparison with Relative Cell References')
- ->setCellValue('A23', 'Value Comparison with Formula based on AVERAGE() ± STDEV()');
+ ->setCellValue('A23', 'Value Comparison with Formula based on AVERAGE() ± STDEV()')
+ ->setCellValue('A30', 'Literal String Value Comparison');
$dataArray = [
[-2, -1, 0, 1, 2],
@@ -45,11 +46,18 @@ $betweenDataArray = [
[4, 3, 8],
];
+$stringArray = [
+ ['I'],
+ ['Love'],
+ ['PHP'],
+];
+
$spreadsheet->getActiveSheet()
->fromArray($dataArray, null, 'A2', true)
->fromArray($dataArray, null, 'A10', true)
->fromArray($betweenDataArray, null, 'A18', true)
->fromArray($dataArray, null, 'A24', true)
+ ->fromArray($stringArray, null, 'A31', true)
->setCellValue('H9', 1);
// Set title row bold
@@ -58,21 +66,31 @@ $spreadsheet->getActiveSheet()->getStyle('A1:E1')->getFont()->setBold(true);
$spreadsheet->getActiveSheet()->getStyle('A9:E9')->getFont()->setBold(true);
$spreadsheet->getActiveSheet()->getStyle('A17:E17')->getFont()->setBold(true);
$spreadsheet->getActiveSheet()->getStyle('A23:E23')->getFont()->setBold(true);
+$spreadsheet->getActiveSheet()->getStyle('A30:E30')->getFont()->setBold(true);
// Define some styles for our Conditionals
$helper->log('Define some styles for our Conditionals');
$yellowStyle = new Style(false, true);
$yellowStyle->getFill()
->setFillType(Fill::FILL_SOLID)
+ ->getStartColor()->setARGB(Color::COLOR_YELLOW);
+$yellowStyle->getFill()
->getEndColor()->setARGB(Color::COLOR_YELLOW);
+$yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE));
$greenStyle = new Style(false, true);
$greenStyle->getFill()
->setFillType(Fill::FILL_SOLID)
+ ->getStartColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFill()
->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
$redStyle = new Style(false, true);
$redStyle->getFill()
->setFillType(Fill::FILL_SOLID)
+ ->getStartColor()->setARGB(Color::COLOR_RED);
+$redStyle->getFill()
->getEndColor()->setARGB(Color::COLOR_RED);
+$redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
@@ -166,6 +184,32 @@ $cellWizard->lessThan('AVERAGE(' . $formulaRange . ')-STDEV(' . $formulaRange .
->setStyle($redStyle);
$conditionalStyles[] = $cellWizard->getConditional();
+$spreadsheet->getActiveSheet()
+ ->getStyle($cellWizard->getCellRange())
+ ->setConditionalStyles($conditionalStyles);
+
+// Set rules for Value Comparison with String Literal
+$cellRange = 'A31:A33';
+$formulaRange = implode(
+ ':',
+ array_map(
+ [Coordinate::class, 'absoluteCoordinate'],
+ Coordinate::splitRange($cellRange)[0]
+ )
+);
+$conditionalStyles = [];
+$wizardFactory = new Wizard($cellRange);
+/** @var Wizard\CellValue $cellWizard */
+$cellWizard = $wizardFactory->newRule(Wizard::CELL_VALUE);
+
+$cellWizard->equals('LOVE')
+ ->setStyle($redStyle);
+$conditionalStyles[] = $cellWizard->getConditional();
+
+$cellWizard->equals('PHP')
+ ->setStyle($greenStyle);
+$conditionalStyles[] = $cellWizard->getConditional();
+
$spreadsheet->getActiveSheet()
->getStyle($cellWizard->getCellRange())
->setConditionalStyles($conditionalStyles);
diff --git a/samples/ConditionalFormatting/02_Text_Comparisons.php b/samples/ConditionalFormatting/02_Text_Comparisons.php
index 53aa84ad..10cd25b8 100644
--- a/samples/ConditionalFormatting/02_Text_Comparisons.php
+++ b/samples/ConditionalFormatting/02_Text_Comparisons.php
@@ -74,14 +74,17 @@ $yellowStyle = new Style(false, true);
$yellowStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_YELLOW);
+$yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE));
$greenStyle = new Style(false, true);
$greenStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
$redStyle = new Style(false, true);
$redStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_RED);
+$redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
diff --git a/samples/ConditionalFormatting/03_Blank_Comparisons.php b/samples/ConditionalFormatting/03_Blank_Comparisons.php
index c8584c3a..33b17ef6 100644
--- a/samples/ConditionalFormatting/03_Blank_Comparisons.php
+++ b/samples/ConditionalFormatting/03_Blank_Comparisons.php
@@ -46,10 +46,12 @@ $greenStyle = new Style(false, true);
$greenStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
$redStyle = new Style(false, true);
$redStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_RED);
+$redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
diff --git a/samples/ConditionalFormatting/04_Error_Comparisons.php b/samples/ConditionalFormatting/04_Error_Comparisons.php
index 957569cf..1c9c7b58 100644
--- a/samples/ConditionalFormatting/04_Error_Comparisons.php
+++ b/samples/ConditionalFormatting/04_Error_Comparisons.php
@@ -49,10 +49,12 @@ $greenStyle = new Style(false, true);
$greenStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
$redStyle = new Style(false, true);
$redStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_RED);
+$redStyle->getFont()->setColor(new Color(Color::COLOR_GREEN));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
diff --git a/samples/ConditionalFormatting/05_Date_Comparisons.php b/samples/ConditionalFormatting/05_Date_Comparisons.php
index ef9ad405..0834939d 100644
--- a/samples/ConditionalFormatting/05_Date_Comparisons.php
+++ b/samples/ConditionalFormatting/05_Date_Comparisons.php
@@ -108,12 +108,11 @@ $spreadsheet->getActiveSheet()->getStyle('B1:K1')->getAlignment()->setHorizontal
// Define some styles for our Conditionals
$helper->log('Define some styles for our Conditionals');
-
$yellowStyle = new Style(false, true);
$yellowStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_YELLOW);
-$yellowStyle->getNumberFormat()->setFormatCode('ddd dd-mmm-yyyy');
+$yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
diff --git a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php
index 0b94bd47..cbed0eb2 100644
--- a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php
+++ b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php
@@ -51,14 +51,16 @@ $spreadsheet->getActiveSheet()->getStyle('A1:C1')->getFont()->setBold(true);
// Define some styles for our Conditionals
$helper->log('Define some styles for our Conditionals');
-$greenStyle = new Style(false, true);
-$greenStyle->getFill()
- ->setFillType(Fill::FILL_SOLID)
- ->getEndColor()->setARGB(Color::COLOR_GREEN);
$yellowStyle = new Style(false, true);
$yellowStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_YELLOW);
+$yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE));
+$greenStyle = new Style(false, true);
+$greenStyle->getFill()
+ ->setFillType(Fill::FILL_SOLID)
+ ->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
// Set conditional formatting rules and styles
$helper->log('Define conditional formatting and set styles');
diff --git a/samples/ConditionalFormatting/07_Expression_Comparisons.php b/samples/ConditionalFormatting/07_Expression_Comparisons.php
index 6ea16bcc..0f2b1a84 100644
--- a/samples/ConditionalFormatting/07_Expression_Comparisons.php
+++ b/samples/ConditionalFormatting/07_Expression_Comparisons.php
@@ -28,6 +28,7 @@ $helper->log('Add data');
$spreadsheet->setActiveSheetIndex(0);
$spreadsheet->getActiveSheet()
->setCellValue('A1', 'Odd/Even Expression Comparison')
+ ->setCellValue('A4', 'Note that these functions are not available for Xls files')
->setCellValue('A15', 'Sales Grid Expression Comparison')
->setCellValue('A25', 'Sales Grid Multiple Expression Comparison');
@@ -69,14 +70,16 @@ $spreadsheet->getActiveSheet()->getStyle('A25:D26')->getFont()->setBold(true);
// Define some styles for our Conditionals
$helper->log('Define some styles for our Conditionals');
-$greenStyle = new Style(false, true);
-$greenStyle->getFill()
- ->setFillType(Fill::FILL_SOLID)
- ->getEndColor()->setARGB(Color::COLOR_GREEN);
$yellowStyle = new Style(false, true);
$yellowStyle->getFill()
->setFillType(Fill::FILL_SOLID)
->getEndColor()->setARGB(Color::COLOR_YELLOW);
+$yellowStyle->getFont()->setColor(new Color(Color::COLOR_BLUE));
+$greenStyle = new Style(false, true);
+$greenStyle->getFill()
+ ->setFillType(Fill::FILL_SOLID)
+ ->getEndColor()->setARGB(Color::COLOR_GREEN);
+$greenStyle->getFont()->setColor(new Color(Color::COLOR_DARKRED));
$greenStyleMoney = clone $greenStyle;
$greenStyleMoney->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_ACCOUNTING_USD);
diff --git a/samples/Reader/01_Simple_file_reader_using_IOFactory.php b/samples/Reader/01_Simple_file_reader_using_IOFactory.php
index 584fd5be..622df231 100644
--- a/samples/Reader/01_Simple_file_reader_using_IOFactory.php
+++ b/samples/Reader/01_Simple_file_reader_using_IOFactory.php
@@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
require __DIR__ . '/../Header.php';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format');
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format');
$spreadsheet = IOFactory::load($inputFileName);
$sheetData = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
var_dump($sheetData);
diff --git a/samples/Reader/02_Simple_file_reader_using_a_specified_reader.php b/samples/Reader/02_Simple_file_reader_using_a_specified_reader.php
index 9a705123..9d4ea686 100644
--- a/samples/Reader/02_Simple_file_reader_using_a_specified_reader.php
+++ b/samples/Reader/02_Simple_file_reader_using_a_specified_reader.php
@@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xls;
require __DIR__ . '/../Header.php';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using ' . Xls::class);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using ' . Xls::class);
$reader = new Xls();
$spreadsheet = $reader->load($inputFileName);
diff --git a/samples/Reader/03_Simple_file_reader_using_the_IOFactory_to_return_a_reader.php b/samples/Reader/03_Simple_file_reader_using_the_IOFactory_to_return_a_reader.php
index 305651de..47b5264c 100644
--- a/samples/Reader/03_Simple_file_reader_using_the_IOFactory_to_return_a_reader.php
+++ b/samples/Reader/03_Simple_file_reader_using_the_IOFactory_to_return_a_reader.php
@@ -7,7 +7,7 @@ require __DIR__ . '/../Header.php';
$inputFileType = 'Xls';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$spreadsheet = $reader->load($inputFileName);
diff --git a/samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php b/samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php
index 98aabfc6..3a7dd065 100644
--- a/samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php
+++ b/samples/Reader/04_Simple_file_reader_using_the_IOFactory_to_identify_a_reader_to_use.php
@@ -7,9 +7,9 @@ require __DIR__ . '/../Header.php';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
$inputFileType = IOFactory::identify($inputFileName);
-$helper->log('File ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' has been identified as an ' . $inputFileType . ' file');
+$helper->log('File ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' has been identified as an ' . $inputFileType . ' file');
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with the identified reader type');
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with the identified reader type');
$reader = IOFactory::createReader($inputFileType);
$spreadsheet = $reader->load($inputFileName);
diff --git a/samples/Reader/05_Simple_file_reader_using_the_read_data_only_option.php b/samples/Reader/05_Simple_file_reader_using_the_read_data_only_option.php
index d3ce9d82..912040a1 100644
--- a/samples/Reader/05_Simple_file_reader_using_the_read_data_only_option.php
+++ b/samples/Reader/05_Simple_file_reader_using_the_read_data_only_option.php
@@ -7,7 +7,7 @@ require __DIR__ . '/../Header.php';
$inputFileType = 'Xls';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Turning Formatting off for Load');
$reader->setReadDataOnly(true);
diff --git a/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php b/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php
index 5507c52b..247fef11 100644
--- a/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php
+++ b/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php
@@ -7,7 +7,7 @@ require __DIR__ . '/../Header.php';
$inputFileType = 'Xls';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading all WorkSheets');
$reader->setLoadAllSheets();
diff --git a/samples/Reader/07_Simple_file_reader_loading_a_single_named_worksheet.php b/samples/Reader/07_Simple_file_reader_loading_a_single_named_worksheet.php
index 142a17f8..a78de203 100644
--- a/samples/Reader/07_Simple_file_reader_loading_a_single_named_worksheet.php
+++ b/samples/Reader/07_Simple_file_reader_loading_a_single_named_worksheet.php
@@ -8,7 +8,7 @@ $inputFileType = 'Xls';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
$sheetname = 'Data Sheet #2';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading Sheet "' . $sheetname . '" only');
$reader->setLoadSheetsOnly($sheetname);
diff --git a/samples/Reader/08_Simple_file_reader_loading_several_named_worksheets.php b/samples/Reader/08_Simple_file_reader_loading_several_named_worksheets.php
index 66efc3e0..0cc58518 100644
--- a/samples/Reader/08_Simple_file_reader_loading_several_named_worksheets.php
+++ b/samples/Reader/08_Simple_file_reader_loading_several_named_worksheets.php
@@ -8,7 +8,7 @@ $inputFileType = 'Xls';
$inputFileName = __DIR__ . '/sampleData/example1.xls';
$sheetnames = ['Data Sheet #1', 'Data Sheet #3'];
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading Sheet' . ((count($sheetnames) == 1) ? '' : 's') . ' "' . implode('" and "', $sheetnames) . '" only');
$reader->setLoadSheetsOnly($sheetnames);
diff --git a/samples/Reader/09_Simple_file_reader_using_a_read_filter.php b/samples/Reader/09_Simple_file_reader_using_a_read_filter.php
index 04c47c64..4603164c 100644
--- a/samples/Reader/09_Simple_file_reader_using_a_read_filter.php
+++ b/samples/Reader/09_Simple_file_reader_using_a_read_filter.php
@@ -28,7 +28,7 @@ class MyReadFilter implements IReadFilter
$filterSubset = new MyReadFilter();
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading Sheet "' . $sheetname . '" only');
$reader->setLoadSheetsOnly($sheetname);
diff --git a/samples/Reader/10_Simple_file_reader_using_a_configurable_read_filter.php b/samples/Reader/10_Simple_file_reader_using_a_configurable_read_filter.php
index 6a600d43..82ca2234 100644
--- a/samples/Reader/10_Simple_file_reader_using_a_configurable_read_filter.php
+++ b/samples/Reader/10_Simple_file_reader_using_a_configurable_read_filter.php
@@ -40,7 +40,7 @@ class MyReadFilter implements IReadFilter
$filterSubset = new MyReadFilter(9, 15, range('G', 'K'));
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading Sheet "' . $sheetname . '" only');
$reader->setLoadSheetsOnly($sheetname);
diff --git a/samples/Reader/11_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_1).php b/samples/Reader/11_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_1).php
index 6c908703..cf070054 100644
--- a/samples/Reader/11_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_1).php
+++ b/samples/Reader/11_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_1).php
@@ -40,7 +40,7 @@ class ChunkReadFilter implements IReadFilter
}
}
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
// Create a new Reader of the type defined in $inputFileType
$reader = IOFactory::createReader($inputFileType);
diff --git a/samples/Reader/12_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_2).php b/samples/Reader/12_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_2).php
index c594c798..13692e3a 100644
--- a/samples/Reader/12_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_2).php
+++ b/samples/Reader/12_Reading_a_workbook_in_chunks_using_a_configurable_read_filter_(version_2).php
@@ -40,7 +40,7 @@ class ChunkReadFilter implements IReadFilter
}
}
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
// Create a new Reader of the type defined in $inputFileType
$reader = IOFactory::createReader($inputFileType);
diff --git a/samples/Reader/13_Simple_file_reader_for_multiple_CSV_files.php b/samples/Reader/13_Simple_file_reader_for_multiple_CSV_files.php
index d4817e30..9cba25a2 100644
--- a/samples/Reader/13_Simple_file_reader_for_multiple_CSV_files.php
+++ b/samples/Reader/13_Simple_file_reader_for_multiple_CSV_files.php
@@ -1,22 +1,21 @@
log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using Csv Reader');
$spreadsheet = $reader->load($inputFileName);
-$spreadsheet->getActiveSheet()->setTitle(pathinfo($inputFileName, PATHINFO_BASENAME));
+$spreadsheet->getActiveSheet()->setTitle(/** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME));
foreach ($inputFileNames as $sheet => $inputFileName) {
- $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #' . ($sheet + 2) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+ $helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #' . ($sheet + 2) . ' using Csv Reader');
$reader->setSheetIndex($sheet + 1);
$reader->loadIntoExisting($inputFileName, $spreadsheet);
- $spreadsheet->getActiveSheet()->setTitle(pathinfo($inputFileName, PATHINFO_BASENAME));
+ $spreadsheet->getActiveSheet()->setTitle(/** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME));
}
$helper->log($spreadsheet->getSheetCount() . ' worksheet' . (($spreadsheet->getSheetCount() == 1) ? '' : 's') . ' loaded');
diff --git a/samples/Reader/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php b/samples/Reader/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php
index 87fbb225..eab7ec01 100644
--- a/samples/Reader/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php
+++ b/samples/Reader/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php
@@ -2,13 +2,12 @@
namespace Samples\Sample14;
-use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../Header.php';
-$inputFileType = 'Csv';
$inputFileName = __DIR__ . '/sampleData/example2.csv';
/** Define a Read Filter class implementing IReadFilter */
@@ -41,9 +40,9 @@ class ChunkReadFilter implements IReadFilter
}
}
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using Csv reader');
// Create a new Reader of the type defined in $inputFileType
-$reader = IOFactory::createReader($inputFileType);
+$reader = new Csv();
// Define how many rows we want to read for each "chunk"
$chunkSize = 100;
diff --git a/samples/Reader/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php b/samples/Reader/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php
index 8213678a..848452c7 100644
--- a/samples/Reader/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php
+++ b/samples/Reader/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php
@@ -2,20 +2,19 @@
use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
-use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Reader\Csv;
require __DIR__ . '/../Header.php';
Cell::setValueBinder(new AdvancedValueBinder());
-$inputFileType = 'Csv';
$inputFileName = __DIR__ . '/sampleData/example1.tsv';
-$reader = IOFactory::createReader($inputFileType);
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using IOFactory with a defined reader type of ' . $inputFileType);
+$reader = new Csv();
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using Csv reader');
$reader->setDelimiter("\t");
$spreadsheet = $reader->load($inputFileName);
-$spreadsheet->getActiveSheet()->setTitle(pathinfo($inputFileName, PATHINFO_BASENAME));
+$spreadsheet->getActiveSheet()->setTitle(/** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME));
$helper->log($spreadsheet->getSheetCount() . ' worksheet' . (($spreadsheet->getSheetCount() == 1) ? '' : 's') . ' loaded');
$loadedSheetNames = $spreadsheet->getSheetNames();
diff --git a/samples/Reader/16_Handling_loader_exceptions_using_TryCatch.php b/samples/Reader/16_Handling_loader_exceptions_using_TryCatch.php
index 5b102967..603f6cb8 100644
--- a/samples/Reader/16_Handling_loader_exceptions_using_TryCatch.php
+++ b/samples/Reader/16_Handling_loader_exceptions_using_TryCatch.php
@@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
require __DIR__ . '/../Header.php';
$inputFileName = __DIR__ . '/sampleData/non-existing-file.xls';
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format');
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format');
try {
$spreadsheet = IOFactory::load($inputFileName);
diff --git a/samples/Reader/17_Simple_file_reader_loading_several_named_worksheets.php b/samples/Reader/17_Simple_file_reader_loading_several_named_worksheets.php
index db30bff8..12c3c113 100644
--- a/samples/Reader/17_Simple_file_reader_loading_several_named_worksheets.php
+++ b/samples/Reader/17_Simple_file_reader_loading_several_named_worksheets.php
@@ -1,14 +1,13 @@
log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType);
-$reader = IOFactory::createReader($inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' using Xls reader');
+$reader = new Xls();
// Read the list of Worksheet Names from the Workbook file
$helper->log('Read the list of Worksheets in the WorkBook');
diff --git a/samples/Reader/18_Reading_list_of_worksheets_without_loading_entire_file.php b/samples/Reader/18_Reading_list_of_worksheets_without_loading_entire_file.php
index bb58a2d5..6d2c3db9 100644
--- a/samples/Reader/18_Reading_list_of_worksheets_without_loading_entire_file.php
+++ b/samples/Reader/18_Reading_list_of_worksheets_without_loading_entire_file.php
@@ -1,15 +1,14 @@
log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' information using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' information using Xls reader');
-$reader = IOFactory::createReader($inputFileType);
+$reader = new Xls();
$worksheetNames = $reader->listWorksheetNames($inputFileName);
$helper->log('
Worksheet Names
');
diff --git a/samples/Reader/19_Reading_worksheet_information_without_loading_entire_file.php b/samples/Reader/19_Reading_worksheet_information_without_loading_entire_file.php
index 5cdc4988..369fff9a 100644
--- a/samples/Reader/19_Reading_worksheet_information_without_loading_entire_file.php
+++ b/samples/Reader/19_Reading_worksheet_information_without_loading_entire_file.php
@@ -1,15 +1,15 @@
log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' information using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' information using Xls reader');
-$reader = IOFactory::createReader($inputFileType);
+$reader = new Xls();
$worksheetData = $reader->listWorksheetInfo($inputFileName);
$helper->log('Worksheet Information
');
diff --git a/samples/Reader/20_Reader_worksheet_hyperlink_image.php b/samples/Reader/20_Reader_worksheet_hyperlink_image.php
index 19d837a5..2b3f294a 100644
--- a/samples/Reader/20_Reader_worksheet_hyperlink_image.php
+++ b/samples/Reader/20_Reader_worksheet_hyperlink_image.php
@@ -13,6 +13,9 @@ $spreadsheet = new Spreadsheet();
$aSheet = $spreadsheet->getActiveSheet();
$gdImage = @imagecreatetruecolor(120, 20);
+if ($gdImage === false) {
+ throw new \Exception('imagecreatetruecolor failed');
+}
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);
diff --git a/samples/Reader/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php b/samples/Reader/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php
index 2c80de3b..ec37b39a 100644
--- a/samples/Reader/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php
+++ b/samples/Reader/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php
@@ -12,10 +12,10 @@ $inputFileType = 'Csv';
$inputFileName = __DIR__ . '/sampleData/longIntegers.csv';
$reader = IOFactory::createReader($inputFileType);
-$helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using IOFactory with a defined reader type of ' . $inputFileType);
+$helper->log('Loading file ' . /** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME) . ' into WorkSheet #1 using IOFactory with a defined reader type of ' . $inputFileType);
$spreadsheet = $reader->load($inputFileName);
-$spreadsheet->getActiveSheet()->setTitle(pathinfo($inputFileName, PATHINFO_BASENAME));
+$spreadsheet->getActiveSheet()->setTitle(/** @scrutinizer ignore-type */ pathinfo($inputFileName, PATHINFO_BASENAME));
$helper->log($spreadsheet->getSheetCount() . ' worksheet' . (($spreadsheet->getSheetCount() == 1) ? '' : 's') . ' loaded');
$loadedSheetNames = $spreadsheet->getSheetNames();
diff --git a/samples/templates/chartSpreadsheet.php b/samples/templates/chartSpreadsheet.php
index 2ad61d32..a5b1b882 100644
--- a/samples/templates/chartSpreadsheet.php
+++ b/samples/templates/chartSpreadsheet.php
@@ -3,7 +3,7 @@
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
-use PhpOffice\PhpSpreadsheet\Chart\Legend;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -71,7 +71,7 @@ $series->setPlotDirection(DataSeries::DIRECTION_BAR);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
-$legend = new Legend(Legend::POSITION_RIGHT, null, false);
+$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title = new Title('Test Bar Chart');
$yAxisLabel = new Title('Value ($k)');
diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php
index 36adef01..b336920c 100644
--- a/src/PhpSpreadsheet/Calculation/Calculation.php
+++ b/src/PhpSpreadsheet/Calculation/Calculation.php
@@ -1040,7 +1040,7 @@ class Calculation
],
'FILTER' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [LookupRef\Filter::class, 'filter'],
'argumentCount' => '2-3',
],
'FILTERXML' => [
@@ -2282,12 +2282,12 @@ class Calculation
],
'SORT' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
- 'argumentCount' => '1+',
+ 'functionCall' => [LookupRef\Sort::class, 'sort'],
+ 'argumentCount' => '1-4',
],
'SORTBY' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [LookupRef\Sort::class, 'sortBy'],
'argumentCount' => '2+',
],
'SQRT' => [
@@ -2583,7 +2583,7 @@ class Calculation
],
'UNIQUE' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
- 'functionCall' => [Functions::class, 'DUMMY'],
+ 'functionCall' => [LookupRef\Unique::class, 'unique'],
'argumentCount' => '1+',
],
'UPPER' => [
diff --git a/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php b/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
index dabe7d1c..cffad6a6 100644
--- a/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
+++ b/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
@@ -47,7 +47,7 @@ class ErrorValue
return false;
}
- return in_array($value, ExcelError::$errorCodes);
+ return in_array($value, ExcelError::$errorCodes) || $value === ExcelError::CALC();
}
/**
diff --git a/src/PhpSpreadsheet/Calculation/Information/ExcelError.php b/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
index 98242eb6..585dfdc8 100644
--- a/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
+++ b/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
@@ -22,6 +22,7 @@ class ExcelError
'num' => '#NUM!',
'na' => '#N/A',
'gettingdata' => '#GETTING_DATA',
+ 'spill' => '#SPILL!',
];
/**
@@ -45,6 +46,10 @@ class ExcelError
++$i;
}
+ if ($value === self::CALC()) {
+ return 14;
+ }
+
return self::NA();
}
@@ -127,10 +132,20 @@ class ExcelError
/**
* DIV0.
*
- * @return string #Not Yet Implemented
+ * @return string #DIV/0!
*/
public static function DIV0()
{
return self::$errorCodes['divisionbyzero'];
}
+
+ /**
+ * CALC.
+ *
+ * @return string #Not Yet Implemented
+ */
+ public static function CALC()
+ {
+ return '#CALC!';
+ }
}
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php b/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
new file mode 100644
index 00000000..74fa8321
--- /dev/null
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
@@ -0,0 +1,81 @@
+getMessage();
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
index d0c13a5c..417a1f79 100644
--- a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
@@ -72,11 +72,17 @@ class Indirect
[$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
+ if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
+ $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
+ }
+
[$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName);
if (
- (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress1, $matches)) ||
- (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellAddress2, $matches)))
+ (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
+ (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
) {
return ExcelError::REF();
}
@@ -95,4 +101,22 @@ class Indirect
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
->extractCellRange($cellAddress, $worksheet, false);
}
+
+ private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
+ {
+ // Being lazy, we're only checking a single row/column to get the max
+ if (ctype_digit($start) && $start <= 1048576) {
+ // Max 16,384 columns for Excel2007
+ $endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : 'XFD';
+
+ return "A{$start}:{$endColRef}{$end}";
+ } elseif (ctype_alpha($start) && strlen($start) <= 3) {
+ // Max 1,048,576 rows for Excel2007
+ $endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : 1048576;
+
+ return "{$start}1:{$end}{$endRowRef}";
+ }
+
+ return "{$start}:{$end}";
+ }
}
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php b/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
index 6a0933d6..8e451fe4 100644
--- a/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
@@ -7,6 +7,16 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
abstract class LookupBase
{
+ /**
+ * @param mixed $lookup_array
+ */
+ protected static function validateLookupArray($lookup_array): void
+ {
+ if (!is_array($lookup_array)) {
+ throw new Exception(ExcelError::REF());
+ }
+ }
+
protected static function validateIndexLookup(array $lookup_array, $index_number): int
{
// index_number must be a number greater than or equal to 1
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
index 27e6f8eb..a447e203 100644
--- a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
@@ -10,6 +10,23 @@ class Matrix
{
use ArrayEnabled;
+ /**
+ * Helper function; NOT an implementation of any Excel Function.
+ */
+ public static function isColumnVector(array $values): bool
+ {
+ return count($values, COUNT_RECURSIVE) === (count($values, COUNT_NORMAL) * 2);
+ }
+
+ /**
+ * Helper function; NOT an implementation of any Excel Function.
+ */
+ public static function isRowVector(array $values): bool
+ {
+ return count($values, COUNT_RECURSIVE) > 1 &&
+ (count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
+ }
+
/**
* TRANSPOSE.
*
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php b/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
new file mode 100644
index 00000000..ff78fbea
--- /dev/null
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
@@ -0,0 +1,342 @@
+getMessage();
+ }
+
+ // We want a simple, enumrated array of arrays where we can reference column by its index number.
+ $sortArray = array_values(array_map('array_values', $sortArray));
+
+ return ($byColumn === true)
+ ? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
+ : self::sortByRow($sortArray, $sortIndex, $sortOrder);
+ }
+
+ /**
+ * SORTBY
+ * The SORTBY function sorts the contents of a range or array based on the values in a corresponding range or array.
+ * The returned array is the same shape as the provided array argument.
+ * Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
+ *
+ * @param mixed $sortArray The range of cells being sorted
+ * @param mixed $args
+ * At least one additional argument must be provided, The vector or range to sort on
+ * After that, arguments are passed as pairs:
+ * sort order: ascending or descending
+ * Ascending = 1 (self::ORDER_ASCENDING)
+ * Descending = -1 (self::ORDER_DESCENDING)
+ * additional arrays or ranges for multi-level sorting
+ *
+ * @return mixed The sorted values from the sort range
+ */
+ public static function sortBy($sortArray, ...$args)
+ {
+ if (!is_array($sortArray)) {
+ // Scalars are always returned "as is"
+ return $sortArray;
+ }
+
+ $sortArray = self::enumerateArrayKeys($sortArray);
+
+ $lookupArraySize = count($sortArray);
+ $argumentCount = count($args);
+
+ try {
+ $sortBy = $sortOrder = [];
+ for ($i = 0; $i < $argumentCount; $i += 2) {
+ $sortBy[] = self::validateSortVector($args[$i], $lookupArraySize);
+ $sortOrder[] = self::validateSortOrder($args[$i + 1] ?? self::ORDER_ASCENDING);
+ }
+ } catch (Exception $e) {
+ return $e->getMessage();
+ }
+
+ return self::processSortBy($sortArray, $sortBy, $sortOrder);
+ }
+
+ private static function enumerateArrayKeys(array $sortArray): array
+ {
+ array_walk(
+ $sortArray,
+ function (&$columns): void {
+ if (is_array($columns)) {
+ $columns = array_values($columns);
+ }
+ }
+ );
+
+ return array_values($sortArray);
+ }
+
+ /**
+ * @param mixed $sortIndex
+ * @param mixed $sortOrder
+ */
+ private static function validateScalarArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+ {
+ if (is_array($sortIndex) || is_array($sortOrder)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortIndex = self::validatePositiveInt($sortIndex, false);
+
+ if ($sortIndex > $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ $sortOrder = self::validateSortOrder($sortOrder);
+ }
+
+ /**
+ * @param mixed $sortVector
+ */
+ private static function validateSortVector($sortVector, int $sortArraySize): array
+ {
+ if (!is_array($sortVector)) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ // It doesn't matter if it's a row or a column vectors, it works either way
+ $sortVector = Functions::flattenArray($sortVector);
+ if (count($sortVector) !== $sortArraySize) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortVector;
+ }
+
+ /**
+ * @param mixed $sortOrder
+ */
+ private static function validateSortOrder($sortOrder): int
+ {
+ $sortOrder = self::validateInt($sortOrder);
+ if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ return $sortOrder;
+ }
+
+ /**
+ * @param array $sortIndex
+ * @param mixed $sortOrder
+ */
+ private static function validateArrayArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+ {
+ // It doesn't matter if they're row or column vectors, it works either way
+ $sortIndex = Functions::flattenArray($sortIndex);
+ $sortOrder = Functions::flattenArray($sortOrder);
+
+ if (
+ count($sortOrder) === 0 || count($sortOrder) > $sortArraySize ||
+ (count($sortOrder) > count($sortIndex))
+ ) {
+ throw new Exception(ExcelError::VALUE());
+ }
+
+ if (count($sortIndex) > count($sortOrder)) {
+ // If $sortOrder has fewer elements than $sortIndex, then the last order element is repeated.
+ $sortOrder = array_merge(
+ $sortOrder,
+ array_fill(0, count($sortIndex) - count($sortOrder), array_pop($sortOrder))
+ );
+ }
+
+ foreach ($sortIndex as $key => &$value) {
+ self::validateScalarArgumentsForSort($value, $sortOrder[$key], $sortArraySize);
+ }
+ }
+
+ private static function prepareSortVectorValues(array $sortVector): array
+ {
+ // Strings should be sorted case-insensitive; with booleans converted to locale-strings
+ return array_map(
+ function ($value) {
+ if (is_bool($value)) {
+ return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
+ } elseif (is_string($value)) {
+ return StringHelper::strToLower($value);
+ }
+
+ return $value;
+ },
+ $sortVector
+ );
+ }
+
+ /**
+ * @param array[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function processSortBy(array $sortArray, array $sortIndex, $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortValues) {
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+ $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+ $sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortVector = self::buildVectorForSort($sortArray, $sortIndex, $sortOrder);
+
+ return self::sortLookupArrayFromVector($sortArray, $sortVector);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArray = Matrix::transpose($sortArray);
+ $result = self::sortByRow($sortArray, $sortIndex, $sortOrder);
+
+ return Matrix::transpose($result);
+ }
+
+ /**
+ * @param int[] $sortIndex
+ * @param int[] $sortOrder
+ */
+ private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
+ {
+ $sortArguments = [];
+ $sortData = [];
+ foreach ($sortIndex as $index => $sortIndexValue) {
+ $sortValues = array_column($sortArray, $sortIndexValue - 1);
+ $sortData[] = $sortValues;
+ $sortArguments[] = self::prepareSortVectorValues($sortValues);
+ $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
+ }
+ $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
+
+ $sortData = self::executeVectorSortQuery($sortData, $sortArguments);
+
+ return $sortData;
+ }
+
+ private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
+ {
+ $sortData = Matrix::transpose($sortData);
+
+ // We need to set an index that can be retained, as array_multisort doesn't maintain numeric keys.
+ $sortDataIndexed = [];
+ foreach ($sortData as $key => $value) {
+ $sortDataIndexed[Coordinate::stringFromColumnIndex($key + 1)] = $value;
+ }
+ unset($sortData);
+
+ $sortArguments[] = &$sortDataIndexed;
+
+ array_multisort(...$sortArguments);
+
+ // After the sort, we restore the numeric keys that will now be in the correct, sorted order
+ $sortedData = [];
+ foreach (array_keys($sortDataIndexed) as $key) {
+ $sortedData[] = Coordinate::columnIndexFromString($key) - 1;
+ }
+
+ return $sortedData;
+ }
+
+ private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
+ {
+ // Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
+ $sortedArray = [];
+ foreach ($sortVector as $index) {
+ $sortedArray[] = $sortArray[$index];
+ }
+
+ return $sortedArray;
+
+// uksort(
+// $lookupArray,
+// function (int $a, int $b) use (array $sortVector) {
+// return $sortVector[$a] <=> $sortVector[$b];
+// }
+// );
+//
+// return $lookupArray;
+ }
+
+ /**
+ * Hack to handle PHP 7:
+ * From PHP 8.0.0, If two members compare as equal in a sort, they retain their original order;
+ * but prior to PHP 8.0.0, their relative order in the sorted array was undefined.
+ * MS Excel replicates the PHP 8.0.0 behaviour, retaining the original order of matching elements.
+ * To replicate that behaviour with PHP 7, we add an extra sort based on the row index.
+ */
+ private static function applyPHP7Patch(array $sortArray, array $sortArguments): array
+ {
+ if (PHP_VERSION_ID < 80000) {
+ $sortArguments[] = range(1, count($sortArray));
+ $sortArguments[] = SORT_ASC;
+ }
+
+ return $sortArguments;
+ }
+}
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php b/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
new file mode 100644
index 00000000..2ba51281
--- /dev/null
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
@@ -0,0 +1,141 @@
+ count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
+ // We're looking at a full column check (multiple rows)
+ $transpose = Matrix::transpose($lookupVector);
+ $result = self::uniqueByRow($transpose, $exactlyOnce);
+
+ return (is_array($result)) ? Matrix::transpose($result) : $result;
+ }
+
+ $result = self::countValuesCaseInsensitive($flattenedLookupVector);
+
+ if ($exactlyOnce === true) {
+ $result = self::exactlyOnceFilter($result);
+ }
+
+ if (count($result) === 0) {
+ return ExcelError::CALC();
+ }
+
+ $result = array_keys($result);
+
+ return $result;
+ }
+
+ private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
+ {
+ $caseInsensitiveCounts = array_count_values(
+ array_map(
+ function (string $value) {
+ return StringHelper::strToUpper($value);
+ },
+ $caseSensitiveLookupValues
+ )
+ );
+
+ $caseSensitiveCounts = [];
+ foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
+ if (is_numeric($caseInsensitiveKey)) {
+ $caseSensitiveCounts[$caseInsensitiveKey] = $count;
+ } else {
+ foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
+ if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
+ $caseSensitiveCounts[$caseSensitiveValue] = $count;
+
+ break;
+ }
+ }
+ }
+ }
+
+ return $caseSensitiveCounts;
+ }
+
+ private static function exactlyOnceFilter(array $values): array
+ {
+ return array_filter(
+ $values,
+ function ($value) {
+ return $value === 1;
+ }
+ );
+ }
+}
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
index 26f42eba..bc8624f3 100644
--- a/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
+++ b/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
@@ -33,6 +33,7 @@ class VLookup extends LookupBase
$notExactMatch = (bool) ($notExactMatch ?? true);
try {
+ self::validateLookupArray($lookupArray);
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();
diff --git a/src/PhpSpreadsheet/CellReferenceHelper.php b/src/PhpSpreadsheet/CellReferenceHelper.php
new file mode 100644
index 00000000..0f079d69
--- /dev/null
+++ b/src/PhpSpreadsheet/CellReferenceHelper.php
@@ -0,0 +1,119 @@
+beforeCellAddress = str_replace('$', '', $beforeCellAddress);
+ $this->numberOfColumns = $numberOfColumns;
+ $this->numberOfRows = $numberOfRows;
+
+ // Get coordinate of $beforeCellAddress
+ [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
+ $this->beforeColumn = (int) Coordinate::columnIndexFromString($beforeColumn);
+ $this->beforeRow = (int) $beforeRow;
+ }
+
+ public function beforeCellAddress(): string
+ {
+ return $this->beforeCellAddress;
+ }
+
+ public function refreshRequired(string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): bool
+ {
+ return $this->beforeCellAddress !== $beforeCellAddress ||
+ $this->numberOfColumns !== $numberOfColumns ||
+ $this->numberOfRows !== $numberOfRows;
+ }
+
+ public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false): string
+ {
+ if (Coordinate::coordinateIsRange($cellReference)) {
+ throw new Exception('Only single cell references may be passed to this method.');
+ }
+
+ // Get coordinate of $cellReference
+ [$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
+ $newColumnIndex = (int) Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
+ $newRowIndex = (int) str_replace('$', '', $newRow);
+
+ $absoluteColumn = $newColumn[0] === '$' ? '$' : '';
+ $absoluteRow = $newRow[0] === '$' ? '$' : '';
+ // Verify which parts should be updated
+ if ($includeAbsoluteReferences === false) {
+ $updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
+ $updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
+ } else {
+ $updateColumn = ($newColumnIndex >= $this->beforeColumn);
+ $updateRow = ($newRowIndex >= $this->beforeRow);
+ }
+
+ // Create new column reference
+ if ($updateColumn) {
+ $newColumn = ($includeAbsoluteReferences === false)
+ ? Coordinate::stringFromColumnIndex($newColumnIndex + $this->numberOfColumns)
+ : $absoluteColumn . Coordinate::stringFromColumnIndex($newColumnIndex + $this->numberOfColumns);
+ }
+
+ // Create new row reference
+ if ($updateRow) {
+ $newRow = ($includeAbsoluteReferences === false)
+ ? $newRowIndex + $this->numberOfRows
+ : $absoluteRow . (string) ($newRowIndex + $this->numberOfRows);
+ }
+
+ // Return new reference
+ return "{$newColumn}{$newRow}";
+ }
+
+ public function cellAddressInDeleteRange(string $cellAddress): bool
+ {
+ [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
+ $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
+ // Is cell within the range of rows/columns if we're deleting
+ if (
+ $this->numberOfRows < 0 &&
+ ($cellRow >= ($this->beforeRow + $this->numberOfRows)) &&
+ ($cellRow < $this->beforeRow)
+ ) {
+ return true;
+ } elseif (
+ $this->numberOfColumns < 0 &&
+ ($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns)) &&
+ ($cellColumnIndex < $this->beforeColumn)
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/PhpSpreadsheet/Helper/Html.php b/src/PhpSpreadsheet/Helper/Html.php
index 26526605..4737379a 100644
--- a/src/PhpSpreadsheet/Helper/Html.php
+++ b/src/PhpSpreadsheet/Helper/Html.php
@@ -619,6 +619,7 @@ class Html
// Load the HTML file into the DOM object
// Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
$prefix = '';
+ /** @scrutinizer ignore-unhandled */
@$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// Discard excess white space
$dom->preserveWhiteSpace = false;
@@ -808,7 +809,7 @@ class Html
if (isset($callbacks[$callbackTag])) {
$elementHandler = $callbacks[$callbackTag];
if (method_exists($this, $elementHandler)) {
- // @phpstan-ignore-next-line
+ /** @phpstan-ignore-next-line */
call_user_func([$this, $elementHandler], $element);
}
}
diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php
index 0fd05c87..402fea9f 100644
--- a/src/PhpSpreadsheet/Reader/Xls.php
+++ b/src/PhpSpreadsheet/Reader/Xls.php
@@ -7,7 +7,9 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\NamedRange;
+use PhpOffice\PhpSpreadsheet\Reader\Xls\ConditionalFormatting;
use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\CellFont;
+use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\FillPattern;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Shared\CodePage;
use PhpOffice\PhpSpreadsheet\Shared\Date;
@@ -21,6 +23,8 @@ use PhpOffice\PhpSpreadsheet\Shared\Xls as SharedXls;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
@@ -142,6 +146,8 @@ class Xls extends BaseReader
const XLS_TYPE_SHEETLAYOUT = 0x0862;
const XLS_TYPE_XFEXT = 0x087d;
const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
+ const XLS_TYPE_CFHEADER = 0x01b0;
+ const XLS_TYPE_CFRULE = 0x01b1;
const XLS_TYPE_UNKNOWN = 0xffff;
// Encryption type
@@ -1031,6 +1037,14 @@ class Xls extends BaseReader
case self::XLS_TYPE_DATAVALIDATION:
$this->readDataValidation();
+ break;
+ case self::XLS_TYPE_CFHEADER:
+ $cellRangeAddresses = $this->readCFHeader();
+
+ break;
+ case self::XLS_TYPE_CFRULE:
+ $this->readCFRule($cellRangeAddresses ?? []);
+
break;
case self::XLS_TYPE_SHEETLAYOUT:
$this->readSheetLayout();
@@ -4776,57 +4790,11 @@ class Xls extends BaseReader
// bit: 0-3; mask: 0x0000000F; type
$type = (0x0000000F & $options) >> 0;
- switch ($type) {
- case 0x00:
- $type = DataValidation::TYPE_NONE;
-
- break;
- case 0x01:
- $type = DataValidation::TYPE_WHOLE;
-
- break;
- case 0x02:
- $type = DataValidation::TYPE_DECIMAL;
-
- break;
- case 0x03:
- $type = DataValidation::TYPE_LIST;
-
- break;
- case 0x04:
- $type = DataValidation::TYPE_DATE;
-
- break;
- case 0x05:
- $type = DataValidation::TYPE_TIME;
-
- break;
- case 0x06:
- $type = DataValidation::TYPE_TEXTLENGTH;
-
- break;
- case 0x07:
- $type = DataValidation::TYPE_CUSTOM;
-
- break;
- }
+ $type = Xls\DataValidationHelper::type($type);
// bit: 4-6; mask: 0x00000070; error type
$errorStyle = (0x00000070 & $options) >> 4;
- switch ($errorStyle) {
- case 0x00:
- $errorStyle = DataValidation::STYLE_STOP;
-
- break;
- case 0x01:
- $errorStyle = DataValidation::STYLE_WARNING;
-
- break;
- case 0x02:
- $errorStyle = DataValidation::STYLE_INFORMATION;
-
- break;
- }
+ $errorStyle = Xls\DataValidationHelper::errorStyle($errorStyle);
// bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
// I have only seen cases where this is 1
@@ -4846,39 +4814,10 @@ class Xls extends BaseReader
// bit: 20-23; mask: 0x00F00000; condition operator
$operator = (0x00F00000 & $options) >> 20;
- switch ($operator) {
- case 0x00:
- $operator = DataValidation::OPERATOR_BETWEEN;
+ $operator = Xls\DataValidationHelper::operator($operator);
- break;
- case 0x01:
- $operator = DataValidation::OPERATOR_NOTBETWEEN;
-
- break;
- case 0x02:
- $operator = DataValidation::OPERATOR_EQUAL;
-
- break;
- case 0x03:
- $operator = DataValidation::OPERATOR_NOTEQUAL;
-
- break;
- case 0x04:
- $operator = DataValidation::OPERATOR_GREATERTHAN;
-
- break;
- case 0x05:
- $operator = DataValidation::OPERATOR_LESSTHAN;
-
- break;
- case 0x06:
- $operator = DataValidation::OPERATOR_GREATERTHANOREQUAL;
-
- break;
- case 0x07:
- $operator = DataValidation::OPERATOR_LESSTHANOREQUAL;
-
- break;
+ if ($type === null || $errorStyle === null || $operator === null) {
+ return;
}
// offset: 4; size: var; title of the prompt box
@@ -7921,4 +7860,230 @@ class Xls extends BaseReader
{
return $this->mapCellStyleXfIndex;
}
+
+ private function readCFHeader(): array
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return [];
+ }
+
+ // offset: 0; size: 2; Rule Count
+// $ruleCount = self::getUInt2d($recordData, 0);
+
+ // offset: var; size: var; cell range address list with
+ $cellRangeAddressList = ($this->version == self::XLS_BIFF8)
+ ? $this->readBIFF8CellRangeAddressList(substr($recordData, 12))
+ : $this->readBIFF5CellRangeAddressList(substr($recordData, 12));
+ $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
+
+ return $cellRangeAddresses;
+ }
+
+ private function readCFRule(array $cellRangeAddresses): void
+ {
+ $length = self::getUInt2d($this->data, $this->pos + 2);
+ $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+
+ // move stream pointer forward to next record
+ $this->pos += 4 + $length;
+
+ if ($this->readDataOnly) {
+ return;
+ }
+
+ // offset: 0; size: 2; Options
+ $cfRule = self::getUInt2d($recordData, 0);
+
+ // bit: 8-15; mask: 0x00FF; type
+ $type = (0x00FF & $cfRule) >> 0;
+ $type = ConditionalFormatting::type($type);
+
+ // bit: 0-7; mask: 0xFF00; type
+ $operator = (0xFF00 & $cfRule) >> 8;
+ $operator = ConditionalFormatting::operator($operator);
+
+ if ($type === null || $operator === null) {
+ return;
+ }
+
+ // offset: 2; size: 2; Size1
+ $size1 = self::getUInt2d($recordData, 2);
+
+ // offset: 4; size: 2; Size2
+ $size2 = self::getUInt2d($recordData, 4);
+
+ // offset: 6; size: 4; Options
+ $options = self::getInt4d($recordData, 6);
+
+ $style = new Style();
+ $this->getCFStyleOptions($options, $style);
+
+ $hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
+ $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
+ $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
+ $hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
+ $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
+
+ $offset = 12;
+
+ if ($hasFontRecord === true) {
+ $fontStyle = substr($recordData, $offset, 118);
+ $this->getCFFontStyle($fontStyle, $style);
+ $offset += 118;
+ }
+
+ if ($hasAlignmentRecord === true) {
+ $alignmentStyle = substr($recordData, $offset, 8);
+ $this->getCFAlignmentStyle($alignmentStyle, $style);
+ $offset += 8;
+ }
+
+ if ($hasBorderRecord === true) {
+ $borderStyle = substr($recordData, $offset, 8);
+ $this->getCFBorderStyle($borderStyle, $style);
+ $offset += 8;
+ }
+
+ if ($hasFillRecord === true) {
+ $fillStyle = substr($recordData, $offset, 4);
+ $this->getCFFillStyle($fillStyle, $style);
+ $offset += 4;
+ }
+
+ if ($hasProtectionRecord === true) {
+ $protectionStyle = substr($recordData, $offset, 4);
+ $this->getCFProtectionStyle($protectionStyle, $style);
+ $offset += 2;
+ }
+
+ $formula1 = $formula2 = null;
+ if ($size1 > 0) {
+ $formula1 = $this->readCFFormula($recordData, $offset, $size1);
+ if ($formula1 === null) {
+ return;
+ }
+
+ $offset += $size1;
+ }
+
+ if ($size2 > 0) {
+ $formula2 = $this->readCFFormula($recordData, $offset, $size2);
+ if ($formula2 === null) {
+ return;
+ }
+
+ $offset += $size2;
+ }
+
+ $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style);
+ }
+
+ private function getCFStyleOptions(int $options, Style $style): void
+ {
+ }
+
+ private function getCFFontStyle(string $options, Style $style): void
+ {
+ $fontSize = self::getInt4d($options, 64);
+ if ($fontSize !== -1) {
+ $style->getFont()->setSize($fontSize / 20); // Convert twips to points
+ }
+
+ $bold = self::getUInt2d($options, 72) === 700; // 400 = normal, 700 = bold
+ $style->getFont()->setBold($bold);
+
+ $color = self::getInt4d($options, 80);
+
+ if ($color !== -1) {
+ $style->getFont()->getColor()->setRGB(Xls\Color::map($color, $this->palette, $this->version)['rgb']);
+ }
+ }
+
+ private function getCFAlignmentStyle(string $options, Style $style): void
+ {
+ }
+
+ private function getCFBorderStyle(string $options, Style $style): void
+ {
+ }
+
+ private function getCFFillStyle(string $options, Style $style): void
+ {
+ $fillPattern = self::getUInt2d($options, 0);
+ // bit: 10-15; mask: 0xFC00; type
+ $fillPattern = (0xFC00 & $fillPattern) >> 10;
+ $fillPattern = FillPattern::lookup($fillPattern);
+ $fillPattern = $fillPattern === Fill::FILL_NONE ? Fill::FILL_SOLID : $fillPattern;
+
+ if ($fillPattern !== Fill::FILL_NONE) {
+ $style->getFill()->setFillType($fillPattern);
+
+ $fillColors = self::getUInt2d($options, 2);
+
+ // bit: 0-6; mask: 0x007F; type
+ $color1 = (0x007F & $fillColors) >> 0;
+ $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']);
+
+ // bit: 7-13; mask: 0x3F80; type
+ $color2 = (0x3F80 & $fillColors) >> 7;
+ $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
+ }
+ }
+
+ private function getCFProtectionStyle(string $options, Style $style): void
+ {
+ }
+
+ /**
+ * @return null|float|int|string
+ */
+ private function readCFFormula(string $recordData, int $offset, int $size)
+ {
+ try {
+ $formula = substr($recordData, $offset, $size);
+ $formula = pack('v', $size) . $formula; // prepend the length
+
+ $formula = $this->getFormulaFromStructure($formula);
+ if (is_numeric($formula)) {
+ return (strpos($formula, '.') !== false) ? (float) $formula : (int) $formula;
+ }
+
+ return $formula;
+ } catch (PhpSpreadsheetException $e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * @param null|float|int|string $formula1
+ * @param null|float|int|string $formula2
+ */
+ private function setCFRules(array $cellRanges, string $type, string $operator, $formula1, $formula2, Style $style): void
+ {
+ foreach ($cellRanges as $cellRange) {
+ $conditional = new Conditional();
+ $conditional->setConditionType($type);
+ $conditional->setOperatorType($operator);
+ if ($formula1 !== null) {
+ $conditional->addCondition($formula1);
+ }
+ if ($formula2 !== null) {
+ $conditional->addCondition($formula2);
+ }
+ $conditional->setStyle($style);
+
+ $conditionalStyles = $this->phpSheet->getStyle($cellRange)->getConditionalStyles();
+ $conditionalStyles[] = $conditional;
+
+ $this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
+ $this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
+ }
+ }
}
diff --git a/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php b/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
new file mode 100644
index 00000000..8400efb9
--- /dev/null
+++ b/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
@@ -0,0 +1,49 @@
+
+ */
+ private static $types = [
+ 0x01 => Conditional::CONDITION_CELLIS,
+ 0x02 => Conditional::CONDITION_EXPRESSION,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $operators = [
+ 0x00 => Conditional::OPERATOR_NONE,
+ 0x01 => Conditional::OPERATOR_BETWEEN,
+ 0x02 => Conditional::OPERATOR_NOTBETWEEN,
+ 0x03 => Conditional::OPERATOR_EQUAL,
+ 0x04 => Conditional::OPERATOR_NOTEQUAL,
+ 0x05 => Conditional::OPERATOR_GREATERTHAN,
+ 0x06 => Conditional::OPERATOR_LESSTHAN,
+ 0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
+ 0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
+ ];
+
+ public static function type(int $type): ?string
+ {
+ if (isset(self::$types[$type])) {
+ return self::$types[$type];
+ }
+
+ return null;
+ }
+
+ public static function operator(int $operator): ?string
+ {
+ if (isset(self::$operators[$operator])) {
+ return self::$operators[$operator];
+ }
+
+ return null;
+ }
+}
diff --git a/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php b/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
new file mode 100644
index 00000000..02f844e3
--- /dev/null
+++ b/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
@@ -0,0 +1,72 @@
+
+ */
+ private static $types = [
+ 0x00 => DataValidation::TYPE_NONE,
+ 0x01 => DataValidation::TYPE_WHOLE,
+ 0x02 => DataValidation::TYPE_DECIMAL,
+ 0x03 => DataValidation::TYPE_LIST,
+ 0x04 => DataValidation::TYPE_DATE,
+ 0x05 => DataValidation::TYPE_TIME,
+ 0x06 => DataValidation::TYPE_TEXTLENGTH,
+ 0x07 => DataValidation::TYPE_CUSTOM,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $errorStyles = [
+ 0x00 => DataValidation::STYLE_STOP,
+ 0x01 => DataValidation::STYLE_WARNING,
+ 0x02 => DataValidation::STYLE_INFORMATION,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $operators = [
+ 0x00 => DataValidation::OPERATOR_BETWEEN,
+ 0x01 => DataValidation::OPERATOR_NOTBETWEEN,
+ 0x02 => DataValidation::OPERATOR_EQUAL,
+ 0x03 => DataValidation::OPERATOR_NOTEQUAL,
+ 0x04 => DataValidation::OPERATOR_GREATERTHAN,
+ 0x05 => DataValidation::OPERATOR_LESSTHAN,
+ 0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL,
+ 0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL,
+ ];
+
+ public static function type(int $type): ?string
+ {
+ if (isset(self::$types[$type])) {
+ return self::$types[$type];
+ }
+
+ return null;
+ }
+
+ public static function errorStyle(int $errorStyle): ?string
+ {
+ if (isset(self::$errorStyles[$errorStyle])) {
+ return self::$errorStyles[$errorStyle];
+ }
+
+ return null;
+ }
+
+ public static function operator(int $operator): ?string
+ {
+ if (isset(self::$operators[$operator])) {
+ return self::$operators[$operator];
+ }
+
+ return null;
+ }
+}
diff --git a/src/PhpSpreadsheet/Reader/Xls/MD5.php b/src/PhpSpreadsheet/Reader/Xls/MD5.php
index a1108f92..14b6bc54 100644
--- a/src/PhpSpreadsheet/Reader/Xls/MD5.php
+++ b/src/PhpSpreadsheet/Reader/Xls/MD5.php
@@ -71,7 +71,7 @@ class MD5
*/
public function add(string $data): void
{
- /** @phpstan-ignore-next-line */
+ // @phpstan-ignore-next-line
$words = array_values(unpack('V16', $data));
$A = $this->a;
diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php
index c0b0c5ee..992002bb 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -1315,6 +1315,11 @@ class Xlsx extends BaseReader
$outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
$hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+ /** @scrutinizer ignore-call */
+ $editAs = $twoCellAnchor->attributes();
+ if (isset($editAs, $editAs['editAs'])) {
+ $objDrawing->setEditAs($editAs['editAs']);
+ }
$objDrawing->setName((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'name'));
$objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
$embedImageKey = (string) self::getArrayItem(
diff --git a/src/PhpSpreadsheet/ReferenceHelper.php b/src/PhpSpreadsheet/ReferenceHelper.php
index 98c4807a..665b2e18 100644
--- a/src/PhpSpreadsheet/ReferenceHelper.php
+++ b/src/PhpSpreadsheet/ReferenceHelper.php
@@ -5,6 +5,8 @@ namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class ReferenceHelper
@@ -19,10 +21,15 @@ class ReferenceHelper
/**
* Instance of this class.
*
- * @var ReferenceHelper
+ * @var ?ReferenceHelper
*/
private static $instance;
+ /**
+ * @var CellReferenceHelper
+ */
+ private $cellReferenceHelper;
+
/**
* Get an instance of this class.
*
@@ -30,7 +37,7 @@ class ReferenceHelper
*/
public static function getInstance()
{
- if (!isset(self::$instance) || (self::$instance === null)) {
+ if (self::$instance === null) {
self::$instance = new self();
}
@@ -114,67 +121,32 @@ class ReferenceHelper
return ($ar < $br) ? 1 : -1;
}
- /**
- * Test whether a cell address falls within a defined range of cells.
- *
- * @param string $cellAddress Address of the cell we're testing
- * @param int $beforeRow Number of the row we're inserting/deleting before
- * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
- * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
- * @param int $numberOfCols Number of columns to insert/delete (negative values indicate deletion)
- *
- * @return bool
- */
- private static function cellAddressInDeleteRange($cellAddress, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfCols)
- {
- [$cellColumn, $cellRow] = Coordinate::coordinateFromString($cellAddress);
- $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
- // Is cell within the range of rows/columns if we're deleting
- if (
- $numberOfRows < 0 &&
- ($cellRow >= ($beforeRow + $numberOfRows)) &&
- ($cellRow < $beforeRow)
- ) {
- return true;
- } elseif (
- $numberOfCols < 0 &&
- ($cellColumnIndex >= ($beforeColumnIndex + $numberOfCols)) &&
- ($cellColumnIndex < $beforeColumnIndex)
- ) {
- return true;
- }
-
- return false;
- }
-
/**
* Update page breaks when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
- * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
- * @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustPageBreaks(Worksheet $worksheet, $beforeCellAddress, $beforeColumnIndex, $numberOfColumns, $beforeRow, $numberOfRows): void
+ protected function adjustPageBreaks(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aBreaks = $worksheet->getBreaks();
- ($numberOfColumns > 0 || $numberOfRows > 0) ?
- uksort($aBreaks, ['self', 'cellReverseSort']) : uksort($aBreaks, ['self', 'cellSort']);
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aBreaks, [self::class, 'cellReverseSort'])
+ : uksort($aBreaks, [self::class, 'cellSort']);
- foreach ($aBreaks as $key => $value) {
- if (self::cellAddressInDeleteRange($key, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfColumns)) {
+ foreach ($aBreaks as $cellAddress => $value) {
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
// If we're deleting, then clear any defined breaks that are within the range
// of rows/columns that we're deleting
- $worksheet->setBreak($key, Worksheet::BREAK_NONE);
+ $worksheet->setBreak($cellAddress, Worksheet::BREAK_NONE);
} else {
// Otherwise update any affected breaks by inserting a new break at the appropriate point
// and removing the old affected break
- $newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
- if ($key != $newReference) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
$worksheet->setBreak($newReference, $value)
- ->setBreak($key, Worksheet::BREAK_NONE);
+ ->setBreak($cellAddress, Worksheet::BREAK_NONE);
}
}
}
@@ -184,22 +156,17 @@ class ReferenceHelper
* Update cell comments when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
- * @param int $beforeColumnIndex Index number of the column we're inserting/deleting before
- * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
- * @param int $beforeRow Number of the row we're inserting/deleting before
- * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustComments($worksheet, $beforeCellAddress, $beforeColumnIndex, $numberOfColumns, $beforeRow, $numberOfRows): void
+ protected function adjustComments($worksheet): void
{
$aComments = $worksheet->getComments();
$aNewComments = []; // the new array of all comments
- foreach ($aComments as $key => &$value) {
+ foreach ($aComments as $cellAddress => &$value) {
// Any comments inside a deleted range will be ignored
- if (!self::cellAddressInDeleteRange($key, $beforeRow, $numberOfRows, $beforeColumnIndex, $numberOfColumns)) {
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === false) {
// Otherwise build a new array of comments indexed by the adjusted cell reference
- $newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $newReference = $this->updateCellReference($cellAddress);
$aNewComments[$newReference] = $value;
}
}
@@ -211,44 +178,85 @@ class ReferenceHelper
* Update hyperlinks when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustHyperlinks($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
+ protected function adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows): void
{
$aHyperlinkCollection = $worksheet->getHyperlinkCollection();
- ($numberOfColumns > 0 || $numberOfRows > 0) ?
- uksort($aHyperlinkCollection, ['self', 'cellReverseSort']) : uksort($aHyperlinkCollection, ['self', 'cellSort']);
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aHyperlinkCollection, [self::class, 'cellReverseSort'])
+ : uksort($aHyperlinkCollection, [self::class, 'cellSort']);
- foreach ($aHyperlinkCollection as $key => $value) {
- $newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
- if ($key != $newReference) {
+ foreach ($aHyperlinkCollection as $cellAddress => $value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
+ $worksheet->setHyperlink($cellAddress, null);
+ } elseif ($cellAddress !== $newReference) {
$worksheet->setHyperlink($newReference, $value);
- $worksheet->setHyperlink($key, null);
+ $worksheet->setHyperlink($cellAddress, null);
}
}
}
+ /**
+ * Update conditional formatting styles when inserting/deleting rows/columns.
+ *
+ * @param Worksheet $worksheet The worksheet that we're editing
+ * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
+ * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
+ */
+ protected function adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows): void
+ {
+ $aStyles = $worksheet->getConditionalStylesCollection();
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aStyles, [self::class, 'cellReverseSort'])
+ : uksort($aStyles, [self::class, 'cellSort']);
+
+ foreach ($aStyles as $cellAddress => $cfRules) {
+ $worksheet->removeConditionalStyles($cellAddress);
+ $newReference = $this->updateCellReference($cellAddress);
+
+ foreach ($cfRules as &$cfRule) {
+ /** @var Conditional $cfRule */
+ $conditions = $cfRule->getConditions();
+ foreach ($conditions as &$condition) {
+ if (is_string($condition)) {
+ $condition = $this->updateFormulaReferences(
+ $condition,
+ $this->cellReferenceHelper->beforeCellAddress(),
+ $numberOfColumns,
+ $numberOfRows,
+ $worksheet->getTitle(),
+ true
+ );
+ }
+ }
+ $cfRule->setConditions($conditions);
+ }
+ $worksheet->setConditionalStyles($newReference, $cfRules);
+ }
+ }
+
/**
* Update data validations when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $before Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustDataValidations(Worksheet $worksheet, $before, $numberOfColumns, $numberOfRows): void
+ protected function adjustDataValidations(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aDataValidationCollection = $worksheet->getDataValidationCollection();
- ($numberOfColumns > 0 || $numberOfRows > 0) ?
- uksort($aDataValidationCollection, ['self', 'cellReverseSort']) : uksort($aDataValidationCollection, ['self', 'cellSort']);
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aDataValidationCollection, [self::class, 'cellReverseSort'])
+ : uksort($aDataValidationCollection, [self::class, 'cellSort']);
- foreach ($aDataValidationCollection as $key => $value) {
- $newReference = $this->updateCellReference($key, $before, $numberOfColumns, $numberOfRows);
- if ($key != $newReference) {
+ foreach ($aDataValidationCollection as $cellAddress => $value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
$worksheet->setDataValidation($newReference, $value);
- $worksheet->setDataValidation($key, null);
+ $worksheet->setDataValidation($cellAddress, null);
}
}
}
@@ -257,16 +265,13 @@ class ReferenceHelper
* Update merged cells when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
- * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
- * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustMergeCells(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
+ protected function adjustMergeCells(Worksheet $worksheet): void
{
$aMergeCells = $worksheet->getMergeCells();
$aNewMergeCells = []; // the new array of all merge cells
- foreach ($aMergeCells as $key => &$value) {
- $newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ foreach ($aMergeCells as $cellAddress => &$value) {
+ $newReference = $this->updateCellReference($cellAddress);
$aNewMergeCells[$newReference] = $newReference;
}
$worksheet->setMergeCells($aNewMergeCells); // replace the merge cells array
@@ -276,20 +281,20 @@ class ReferenceHelper
* Update protected cells when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
* @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustProtectedCells(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
+ protected function adjustProtectedCells(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
{
$aProtectedCells = $worksheet->getProtectedCells();
- ($numberOfColumns > 0 || $numberOfRows > 0) ?
- uksort($aProtectedCells, ['self', 'cellReverseSort']) : uksort($aProtectedCells, ['self', 'cellSort']);
- foreach ($aProtectedCells as $key => $value) {
- $newReference = $this->updateCellReference($key, $beforeCellAddress, $numberOfColumns, $numberOfRows);
- if ($key != $newReference) {
+ ($numberOfColumns > 0 || $numberOfRows > 0)
+ ? uksort($aProtectedCells, [self::class, 'cellReverseSort'])
+ : uksort($aProtectedCells, [self::class, 'cellSort']);
+ foreach ($aProtectedCells as $cellAddress => $value) {
+ $newReference = $this->updateCellReference($cellAddress);
+ if ($cellAddress !== $newReference) {
$worksheet->protectCells($newReference, $value, true);
- $worksheet->unprotectCells($key);
+ $worksheet->unprotectCells($cellAddress);
}
}
}
@@ -298,18 +303,15 @@ class ReferenceHelper
* Update column dimensions when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
- * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
- * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustColumnDimensions(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows): void
+ protected function adjustColumnDimensions(Worksheet $worksheet): void
{
$aColumnDimensions = array_reverse($worksheet->getColumnDimensions(), true);
if (!empty($aColumnDimensions)) {
foreach ($aColumnDimensions as $objColumnDimension) {
- $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1', $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $newReference = $this->updateCellReference($objColumnDimension->getColumnIndex() . '1');
[$newReference] = Coordinate::coordinateFromString($newReference);
- if ($objColumnDimension->getColumnIndex() != $newReference) {
+ if ($objColumnDimension->getColumnIndex() !== $newReference) {
$objColumnDimension->setColumnIndex($newReference);
}
}
@@ -321,20 +323,19 @@ class ReferenceHelper
* Update row dimensions when inserting/deleting rows/columns.
*
* @param Worksheet $worksheet The worksheet that we're editing
- * @param string $beforeCellAddress Insert/Delete before this cell address (e.g. 'A1')
- * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
* @param int $beforeRow Number of the row we're inserting/deleting before
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
*/
- protected function adjustRowDimensions(Worksheet $worksheet, $beforeCellAddress, $numberOfColumns, $beforeRow, $numberOfRows): void
+ protected function adjustRowDimensions(Worksheet $worksheet, $beforeRow, $numberOfRows): void
{
$aRowDimensions = array_reverse($worksheet->getRowDimensions(), true);
if (!empty($aRowDimensions)) {
foreach ($aRowDimensions as $objRowDimension) {
- $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex(), $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $newReference = $this->updateCellReference('A' . $objRowDimension->getRowIndex());
[, $newReference] = Coordinate::coordinateFromString($newReference);
- if ($objRowDimension->getRowIndex() != $newReference) {
- $objRowDimension->setRowIndex($newReference);
+ $newRoweference = (int) $newReference;
+ if ($objRowDimension->getRowIndex() !== $newRoweference) {
+ $objRowDimension->setRowIndex($newRoweference);
}
}
$worksheet->refreshRowDimensions();
@@ -358,11 +359,22 @@ class ReferenceHelper
* @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
* @param Worksheet $worksheet The worksheet that we're editing
*/
- public function insertNewBefore($beforeCellAddress, $numberOfColumns, $numberOfRows, Worksheet $worksheet): void
- {
+ public function insertNewBefore(
+ string $beforeCellAddress,
+ int $numberOfColumns,
+ int $numberOfRows,
+ Worksheet $worksheet
+ ): void {
$remove = ($numberOfColumns < 0 || $numberOfRows < 0);
$allCoordinates = $worksheet->getCoordinates();
+ if (
+ $this->cellReferenceHelper === null ||
+ $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+ ) {
+ $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+
// Get coordinate of $beforeCellAddress
[$beforeColumn, $beforeRow] = Coordinate::indexesFromString($beforeCellAddress);
@@ -372,30 +384,12 @@ class ReferenceHelper
// 1. Clear column strips if we are removing columns
if ($numberOfColumns < 0 && $beforeColumn - 2 + $numberOfColumns > 0) {
- for ($i = 1; $i <= $highestRow - 1; ++$i) {
- for ($j = $beforeColumn - 1 + $numberOfColumns; $j <= $beforeColumn - 2; ++$j) {
- $coordinate = Coordinate::stringFromColumnIndex($j + 1) . $i;
- $worksheet->removeConditionalStyles($coordinate);
- if ($worksheet->cellExists($coordinate)) {
- $worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
- $worksheet->getCell($coordinate)->setXfIndex(0);
- }
- }
- }
+ $this->clearColumnStrips($highestRow, $beforeColumn, $numberOfColumns, $worksheet);
}
// 2. Clear row strips if we are removing rows
if ($numberOfRows < 0 && $beforeRow - 1 + $numberOfRows > 0) {
- for ($i = $beforeColumn - 1; $i <= Coordinate::columnIndexFromString($highestColumn) - 1; ++$i) {
- for ($j = $beforeRow + $numberOfRows; $j <= $beforeRow - 1; ++$j) {
- $coordinate = Coordinate::stringFromColumnIndex($i + 1) . $j;
- $worksheet->removeConditionalStyles($coordinate);
- if ($worksheet->cellExists($coordinate)) {
- $worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
- $worksheet->getCell($coordinate)->setXfIndex(0);
- }
- }
- }
+ $this->clearRowStrips($highestColumn, $beforeColumn, $beforeRow, $numberOfRows, $worksheet);
}
// Find missing coordinates. This is important when inserting column before the last column
@@ -440,7 +434,7 @@ class ReferenceHelper
$worksheet->getCell($newCoordinate)->setXfIndex($cell->getXfIndex());
// Insert this cell at its new location
- if ($cell->getDataType() == DataType::TYPE_FORMULA) {
+ if ($cell->getDataType() === DataType::TYPE_FORMULA) {
// Formula should be adjusted
$worksheet->getCell($newCoordinate)
->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
@@ -454,7 +448,7 @@ class ReferenceHelper
} else {
/* We don't need to update styles for rows/columns before our insertion position,
but we do still need to adjust any formulae in those cells */
- if ($cell->getDataType() == DataType::TYPE_FORMULA) {
+ if ($cell->getDataType() === DataType::TYPE_FORMULA) {
// Formula should be adjusted
$cell->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
}
@@ -466,148 +460,65 @@ class ReferenceHelper
$highestRow = $worksheet->getHighestRow();
if ($numberOfColumns > 0 && $beforeColumn - 2 > 0) {
- for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
- // Style
- $coordinate = Coordinate::stringFromColumnIndex($beforeColumn - 1) . $i;
- if ($worksheet->cellExists($coordinate)) {
- $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
- $conditionalStyles = $worksheet->conditionalStylesExists($coordinate) ?
- $worksheet->getConditionalStyles($coordinate) : false;
- for ($j = $beforeColumn; $j <= $beforeColumn - 1 + $numberOfColumns; ++$j) {
- $worksheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
- if ($conditionalStyles) {
- $cloned = [];
- foreach ($conditionalStyles as $conditionalStyle) {
- $cloned[] = clone $conditionalStyle;
- }
- $worksheet->setConditionalStyles(Coordinate::stringFromColumnIndex($j) . $i, $cloned);
- }
- }
- }
- }
+ $this->duplicateStylesByColumn($worksheet, $beforeColumn, $beforeRow, $highestRow, $numberOfColumns);
}
if ($numberOfRows > 0 && $beforeRow - 1 > 0) {
- for ($i = $beforeColumn; $i <= Coordinate::columnIndexFromString($highestColumn); ++$i) {
- // Style
- $coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
- if ($worksheet->cellExists($coordinate)) {
- $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
- $conditionalStyles = $worksheet->conditionalStylesExists($coordinate) ?
- $worksheet->getConditionalStyles($coordinate) : false;
- for ($j = $beforeRow; $j <= $beforeRow - 1 + $numberOfRows; ++$j) {
- $worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
- if ($conditionalStyles) {
- $cloned = [];
- foreach ($conditionalStyles as $conditionalStyle) {
- $cloned[] = clone $conditionalStyle;
- }
- $worksheet->setConditionalStyles(Coordinate::stringFromColumnIndex($i) . $j, $cloned);
- }
- }
- }
- }
+ $this->duplicateStylesByRow($worksheet, $beforeColumn, $beforeRow, $highestColumn, $numberOfRows);
}
// Update worksheet: column dimensions
- $this->adjustColumnDimensions($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $this->adjustColumnDimensions($worksheet);
// Update worksheet: row dimensions
- $this->adjustRowDimensions($worksheet, $beforeCellAddress, $numberOfColumns, $beforeRow, $numberOfRows);
+ $this->adjustRowDimensions($worksheet, $beforeRow, $numberOfRows);
// Update worksheet: page breaks
- $this->adjustPageBreaks($worksheet, $beforeCellAddress, $beforeColumn, $numberOfColumns, $beforeRow, $numberOfRows);
+ $this->adjustPageBreaks($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: comments
- $this->adjustComments($worksheet, $beforeCellAddress, $beforeColumn, $numberOfColumns, $beforeRow, $numberOfRows);
+ $this->adjustComments($worksheet);
// Update worksheet: hyperlinks
- $this->adjustHyperlinks($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $this->adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows);
+
+ // Update worksheet: conditional formatting styles
+ $this->adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: data validations
- $this->adjustDataValidations($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $this->adjustDataValidations($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: merge cells
- $this->adjustMergeCells($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $this->adjustMergeCells($worksheet);
// Update worksheet: protected cells
- $this->adjustProtectedCells($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $this->adjustProtectedCells($worksheet, $numberOfColumns, $numberOfRows);
// Update worksheet: autofilter
- $autoFilter = $worksheet->getAutoFilter();
- $autoFilterRange = $autoFilter->getRange();
- if (!empty($autoFilterRange)) {
- if ($numberOfColumns != 0) {
- $autoFilterColumns = $autoFilter->getColumns();
- if (count($autoFilterColumns) > 0) {
- $column = '';
- $row = 0;
- sscanf($beforeCellAddress, '%[A-Z]%d', $column, $row);
- $columnIndex = Coordinate::columnIndexFromString($column);
- [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
- if ($columnIndex <= $rangeEnd[0]) {
- if ($numberOfColumns < 0) {
- // If we're actually deleting any columns that fall within the autofilter range,
- // then we delete any rules for those columns
- $deleteColumn = $columnIndex + $numberOfColumns - 1;
- $deleteCount = abs($numberOfColumns);
- for ($i = 1; $i <= $deleteCount; ++$i) {
- if (isset($autoFilterColumns[Coordinate::stringFromColumnIndex($deleteColumn + 1)])) {
- $autoFilter->clearColumn(Coordinate::stringFromColumnIndex($deleteColumn + 1));
- }
- ++$deleteColumn;
- }
- }
- $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
-
- // Shuffle columns in autofilter range
- if ($numberOfColumns > 0) {
- $startColRef = $startCol;
- $endColRef = $rangeEnd[0];
- $toColRef = $rangeEnd[0] + $numberOfColumns;
-
- do {
- $autoFilter->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
- --$endColRef;
- --$toColRef;
- } while ($startColRef <= $endColRef);
- } else {
- // For delete, we shuffle from beginning to end to avoid overwriting
- $startColID = Coordinate::stringFromColumnIndex($startCol);
- $toColID = Coordinate::stringFromColumnIndex($startCol + $numberOfColumns);
- $endColID = Coordinate::stringFromColumnIndex($rangeEnd[0] + 1);
- do {
- $autoFilter->shiftColumn($startColID, $toColID);
- ++$startColID;
- ++$toColID;
- } while ($startColID != $endColID);
- }
- }
- }
- }
- $worksheet->setAutoFilter($this->updateCellReference($autoFilterRange, $beforeCellAddress, $numberOfColumns, $numberOfRows));
- }
+ $this->adjustAutoFilter($worksheet, $beforeCellAddress, $numberOfColumns);
// Update worksheet: freeze pane
if ($worksheet->getFreezePane()) {
$splitCell = $worksheet->getFreezePane() ?? '';
$topLeftCell = $worksheet->getTopLeftCell() ?? '';
- $splitCell = $this->updateCellReference($splitCell, $beforeCellAddress, $numberOfColumns, $numberOfRows);
- $topLeftCell = $this->updateCellReference($topLeftCell, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $splitCell = $this->updateCellReference($splitCell);
+ $topLeftCell = $this->updateCellReference($topLeftCell);
$worksheet->freezePane($splitCell, $topLeftCell);
}
// Page setup
if ($worksheet->getPageSetup()->isPrintAreaSet()) {
- $worksheet->getPageSetup()->setPrintArea($this->updateCellReference($worksheet->getPageSetup()->getPrintArea(), $beforeCellAddress, $numberOfColumns, $numberOfRows));
+ $worksheet->getPageSetup()->setPrintArea(
+ $this->updateCellReference($worksheet->getPageSetup()->getPrintArea())
+ );
}
// Update worksheet: drawings
$aDrawings = $worksheet->getDrawingCollection();
foreach ($aDrawings as $objDrawing) {
- $newReference = $this->updateCellReference($objDrawing->getCoordinates(), $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $newReference = $this->updateCellReference($objDrawing->getCoordinates());
if ($objDrawing->getCoordinates() != $newReference) {
$objDrawing->setCoordinates($newReference);
}
@@ -617,7 +528,7 @@ class ReferenceHelper
if (count($worksheet->getParent()->getDefinedNames()) > 0) {
foreach ($worksheet->getParent()->getDefinedNames() as $definedName) {
if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
- $definedName->setValue($this->updateCellReference($definedName->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows));
+ $definedName->setValue($this->updateCellReference($definedName->getValue()));
}
}
}
@@ -637,8 +548,21 @@ class ReferenceHelper
*
* @return string Updated formula
*/
- public function updateFormulaReferences($formula = '', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0, $worksheetName = '')
- {
+ public function updateFormulaReferences(
+ $formula = '',
+ $beforeCellAddress = 'A1',
+ $numberOfColumns = 0,
+ $numberOfRows = 0,
+ $worksheetName = '',
+ bool $includeAbsoluteReferences = false
+ ) {
+ if (
+ $this->cellReferenceHelper === null ||
+ $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+ ) {
+ $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
+ }
+
// Update cell references in the formula
$formulaBlocks = explode('"', $formula);
$i = false;
@@ -648,13 +572,13 @@ class ReferenceHelper
$adjustCount = 0;
$newCellTokens = $cellTokens = [];
// Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
- $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+ $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
- $modified3 = substr($this->updateCellReference('$A' . $match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows), 2);
- $modified4 = substr($this->updateCellReference('$A' . $match[4], $beforeCellAddress, $numberOfColumns, $numberOfRows), 2);
+ $modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences), 2);
+ $modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences), 2);
if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@@ -673,13 +597,13 @@ class ReferenceHelper
}
}
// Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
- $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+ $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
- $modified3 = substr($this->updateCellReference($match[3] . '$1', $beforeCellAddress, $numberOfColumns, $numberOfRows), 0, -2);
- $modified4 = substr($this->updateCellReference($match[4] . '$1', $beforeCellAddress, $numberOfColumns, $numberOfRows), 0, -2);
+ $modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences), 0, -2);
+ $modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences), 0, -2);
if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@@ -698,13 +622,13 @@ class ReferenceHelper
}
}
// Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
- $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+ $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3] . ':' . $match[4];
- $modified3 = $this->updateCellReference($match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows);
- $modified4 = $this->updateCellReference($match[4], $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
+ $modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences);
if ($match[3] . $match[4] !== $modified3 . $modified4) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
@@ -724,14 +648,14 @@ class ReferenceHelper
}
}
// Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
- $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/i', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+ $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = ($match[2] > '') ? $match[2] . '!' : '';
$fromString .= $match[3];
- $modified3 = $this->updateCellReference($match[3], $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
if ($match[3] !== $modified3) {
if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
$toString = ($match[2] > '') ? $match[2] . '!' : '';
@@ -908,13 +832,10 @@ class ReferenceHelper
* Update cell reference.
*
* @param string $cellReference Cell address or range of addresses
- * @param string $beforeCellAddress Insert before this one
- * @param int $numberOfColumns Number of columns to increment
- * @param int $numberOfRows Number of rows to increment
*
* @return string Updated cell range
*/
- public function updateCellReference($cellReference = 'A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
+ private function updateCellReference($cellReference = 'A1', bool $includeAbsoluteReferences = false)
{
// Is it in another worksheet? Will not have to update anything.
if (strpos($cellReference, '!') !== false) {
@@ -922,10 +843,10 @@ class ReferenceHelper
// Is it a range or a single cell?
} elseif (!Coordinate::coordinateIsRange($cellReference)) {
// Single cell
- return $this->updateSingleCellReference($cellReference, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ return $this->cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences);
} elseif (Coordinate::coordinateIsRange($cellReference)) {
// Range
- return $this->updateCellRange($cellReference, $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ return $this->updateCellRange($cellReference, $includeAbsoluteReferences);
}
// Return original
@@ -948,7 +869,7 @@ class ReferenceHelper
foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
foreach ($sheet->getCoordinates(false) as $coordinate) {
$cell = $sheet->getCell($coordinate);
- if (($cell !== null) && ($cell->getDataType() == DataType::TYPE_FORMULA)) {
+ if (($cell !== null) && ($cell->getDataType() === DataType::TYPE_FORMULA)) {
$formula = $cell->getValue();
if (strpos($formula, $oldName) !== false) {
$formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
@@ -964,13 +885,10 @@ class ReferenceHelper
* Update cell range.
*
* @param string $cellRange Cell range (e.g. 'B2:D4', 'B:C' or '2:3')
- * @param string $beforeCellAddress Insert before this one
- * @param int $numberOfColumns Number of columns to increment
- * @param int $numberOfRows Number of rows to increment
*
* @return string Updated cell range
*/
- private function updateCellRange($cellRange = 'A1:A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
+ private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsoluteReferences = false): string
{
if (!Coordinate::coordinateIsRange($cellRange)) {
throw new Exception('Only cell ranges may be passed to this method.');
@@ -983,13 +901,15 @@ class ReferenceHelper
$jc = count($range[$i]);
for ($j = 0; $j < $jc; ++$j) {
if (ctype_alpha($range[$i][$j])) {
- $r = Coordinate::coordinateFromString($this->updateSingleCellReference($range[$i][$j] . '1', $beforeCellAddress, $numberOfColumns, $numberOfRows));
- $range[$i][$j] = $r[0];
+ $range[$i][$j] = Coordinate::coordinateFromString(
+ $this->cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences)
+ )[0];
} elseif (ctype_digit($range[$i][$j])) {
- $r = Coordinate::coordinateFromString($this->updateSingleCellReference('A' . $range[$i][$j], $beforeCellAddress, $numberOfColumns, $numberOfRows));
- $range[$i][$j] = $r[1];
+ $range[$i][$j] = Coordinate::coordinateFromString(
+ $this->cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences)
+ )[1];
} else {
- $range[$i][$j] = $this->updateSingleCellReference($range[$i][$j], $beforeCellAddress, $numberOfColumns, $numberOfRows);
+ $range[$i][$j] = $this->cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences);
}
}
}
@@ -998,44 +918,142 @@ class ReferenceHelper
return Coordinate::buildRange($range);
}
- /**
- * Update single cell reference.
- *
- * @param string $cellReference Single cell reference
- * @param string $beforeCellAddress Insert before this one
- * @param int $numberOfColumns Number of columns to increment
- * @param int $numberOfRows Number of rows to increment
- *
- * @return string Updated cell reference
- */
- private function updateSingleCellReference($cellReference = 'A1', $beforeCellAddress = 'A1', $numberOfColumns = 0, $numberOfRows = 0)
+ private function clearColumnStrips(int $highestRow, int $beforeColumn, int $numberOfColumns, Worksheet $worksheet): void
{
- if (Coordinate::coordinateIsRange($cellReference)) {
- throw new Exception('Only single cell references may be passed to this method.');
+ for ($i = 1; $i <= $highestRow - 1; ++$i) {
+ for ($j = $beforeColumn - 1 + $numberOfColumns; $j <= $beforeColumn - 2; ++$j) {
+ $coordinate = Coordinate::stringFromColumnIndex($j + 1) . $i;
+ $worksheet->removeConditionalStyles($coordinate);
+ if ($worksheet->cellExists($coordinate)) {
+ $worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
+ $worksheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
}
+ }
- // Get coordinate of $beforeCellAddress
- [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
+ private function clearRowStrips(string $highestColumn, int $beforeColumn, int $beforeRow, int $numberOfRows, Worksheet $worksheet): void
+ {
+ $lastColumnIndex = Coordinate::columnIndexFromString($highestColumn) - 1;
- // Get coordinate of $cellReference
- [$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
-
- // Verify which parts should be updated
- $updateColumn = (($newColumn[0] != '$') && ($beforeColumn[0] != '$') && (Coordinate::columnIndexFromString($newColumn) >= Coordinate::columnIndexFromString($beforeColumn)));
- $updateRow = (($newRow[0] != '$') && ($beforeRow[0] != '$') && $newRow >= $beforeRow);
-
- // Create new column reference
- if ($updateColumn) {
- $newColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($newColumn) + $numberOfColumns);
+ for ($i = $beforeColumn - 1; $i <= $lastColumnIndex; ++$i) {
+ for ($j = $beforeRow + $numberOfRows; $j <= $beforeRow - 1; ++$j) {
+ $coordinate = Coordinate::stringFromColumnIndex($i + 1) . $j;
+ $worksheet->removeConditionalStyles($coordinate);
+ if ($worksheet->cellExists($coordinate)) {
+ $worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
+ $worksheet->getCell($coordinate)->setXfIndex(0);
+ }
+ }
}
+ }
- // Create new row reference
- if ($updateRow) {
- $newRow = (int) $newRow + $numberOfRows;
+ private function adjustAutoFilter(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns): void
+ {
+ $autoFilter = $worksheet->getAutoFilter();
+ $autoFilterRange = $autoFilter->getRange();
+ if (!empty($autoFilterRange)) {
+ if ($numberOfColumns !== 0) {
+ $autoFilterColumns = $autoFilter->getColumns();
+ if (count($autoFilterColumns) > 0) {
+ $column = '';
+ $row = 0;
+ sscanf($beforeCellAddress, '%[A-Z]%d', $column, $row);
+ $columnIndex = Coordinate::columnIndexFromString($column);
+ [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($autoFilterRange);
+ if ($columnIndex <= $rangeEnd[0]) {
+ if ($numberOfColumns < 0) {
+ $this->adjustAutoFilterDeleteRules($columnIndex, $numberOfColumns, $autoFilterColumns, $autoFilter);
+ }
+ $startCol = ($columnIndex > $rangeStart[0]) ? $columnIndex : $rangeStart[0];
+
+ // Shuffle columns in autofilter range
+ if ($numberOfColumns > 0) {
+ $this->adjustAutoFilterInsert($startCol, $numberOfColumns, $rangeEnd[0], $autoFilter);
+ } else {
+ $this->adjustAutoFilterDelete($startCol, $numberOfColumns, $rangeEnd[0], $autoFilter);
+ }
+ }
+ }
+ }
+
+ $worksheet->setAutoFilter(
+ $this->updateCellReference($autoFilterRange)
+ );
}
+ }
- // Return new reference
- return $newColumn . $newRow;
+ private function adjustAutoFilterDeleteRules(int $columnIndex, int $numberOfColumns, array $autoFilterColumns, AutoFilter $autoFilter): void
+ {
+ // If we're actually deleting any columns that fall within the autofilter range,
+ // then we delete any rules for those columns
+ $deleteColumn = $columnIndex + $numberOfColumns - 1;
+ $deleteCount = abs($numberOfColumns);
+
+ for ($i = 1; $i <= $deleteCount; ++$i) {
+ $columnName = Coordinate::stringFromColumnIndex($deleteColumn + 1);
+ if (isset($autoFilterColumns[$columnName])) {
+ $autoFilter->clearColumn($columnName);
+ }
+ ++$deleteColumn;
+ }
+ }
+
+ private function adjustAutoFilterInsert(int $startCol, int $numberOfColumns, int $rangeEnd, AutoFilter $autoFilter): void
+ {
+ $startColRef = $startCol;
+ $endColRef = $rangeEnd;
+ $toColRef = $rangeEnd + $numberOfColumns;
+
+ do {
+ $autoFilter->shiftColumn(Coordinate::stringFromColumnIndex($endColRef), Coordinate::stringFromColumnIndex($toColRef));
+ --$endColRef;
+ --$toColRef;
+ } while ($startColRef <= $endColRef);
+ }
+
+ private function adjustAutoFilterDelete(int $startCol, int $numberOfColumns, int $rangeEnd, AutoFilter $autoFilter): void
+ {
+ // For delete, we shuffle from beginning to end to avoid overwriting
+ $startColID = Coordinate::stringFromColumnIndex($startCol);
+ $toColID = Coordinate::stringFromColumnIndex($startCol + $numberOfColumns);
+ $endColID = Coordinate::stringFromColumnIndex($rangeEnd + 1);
+
+ do {
+ $autoFilter->shiftColumn($startColID, $toColID);
+ ++$startColID;
+ ++$toColID;
+ } while ($startColID !== $endColID);
+ }
+
+ private function duplicateStylesByColumn(Worksheet $worksheet, int $beforeColumn, int $beforeRow, int $highestRow, int $numberOfColumns): void
+ {
+ $beforeColumnName = Coordinate::stringFromColumnIndex($beforeColumn - 1);
+ for ($i = $beforeRow; $i <= $highestRow - 1; ++$i) {
+ // Style
+ $coordinate = $beforeColumnName . $i;
+ if ($worksheet->cellExists($coordinate)) {
+ $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
+ for ($j = $beforeColumn; $j <= $beforeColumn - 1 + $numberOfColumns; ++$j) {
+ $worksheet->getCellByColumnAndRow($j, $i)->setXfIndex($xfIndex);
+ }
+ }
+ }
+ }
+
+ private function duplicateStylesByRow(Worksheet $worksheet, int $beforeColumn, int $beforeRow, string $highestColumn, int $numberOfRows): void
+ {
+ $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
+ for ($i = $beforeColumn; $i <= $highestColumnIndex; ++$i) {
+ // Style
+ $coordinate = Coordinate::stringFromColumnIndex($i) . ($beforeRow - 1);
+ if ($worksheet->cellExists($coordinate)) {
+ $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
+ for ($j = $beforeRow; $j <= $beforeRow - 1 + $numberOfRows; ++$j) {
+ $worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+ }
+ }
+ }
}
/**
diff --git a/src/PhpSpreadsheet/Shared/XMLWriter.php b/src/PhpSpreadsheet/Shared/XMLWriter.php
index 84ad8a83..3dc0aad9 100644
--- a/src/PhpSpreadsheet/Shared/XMLWriter.php
+++ b/src/PhpSpreadsheet/Shared/XMLWriter.php
@@ -54,7 +54,9 @@ class XMLWriter extends \XMLWriter
public function __destruct()
{
// Unlink temporary files
+ // There is nothing reasonable to do if unlink fails.
if ($this->tempFileName != '') {
+ /** @scrutinizer ignore-unhandled */
@unlink($this->tempFileName);
}
}
diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php
index 2b8c8360..52d7fb55 100644
--- a/src/PhpSpreadsheet/Spreadsheet.php
+++ b/src/PhpSpreadsheet/Spreadsheet.php
@@ -1602,4 +1602,14 @@ class Spreadsheet
}
}
}
+
+ /**
+ * Silliness to mollify Scrutinizer.
+ *
+ * @codeCoverageIgnore
+ */
+ public function getSharedComponent(): Style
+ {
+ return new Style();
+ }
}
diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
index 75d6856e..df9daab3 100644
--- a/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
+++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
@@ -122,7 +122,7 @@ abstract class WizardAbstract
return "{$worksheet}{$column}{$row}";
}
- protected static function reverseAdjustCellRef(string $condition, string $cellRange): string
+ public static function reverseAdjustCellRef(string $condition, string $cellRange): string
{
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange)));
[$referenceCell] = $conditionalRange[0];
diff --git a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
index bd90b2bc..815536b5 100644
--- a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
+++ b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
@@ -8,6 +8,22 @@ use PhpOffice\PhpSpreadsheet\IComparable;
class BaseDrawing implements IComparable
{
+ const EDIT_AS_ABSOLUTE = 'absolute';
+ const EDIT_AS_ONECELL = 'onecell';
+ const EDIT_AS_TWOCELL = 'twocell';
+ private const VALID_EDIT_AS = [
+ self::EDIT_AS_ABSOLUTE,
+ self::EDIT_AS_ONECELL,
+ self::EDIT_AS_TWOCELL,
+ ];
+
+ /**
+ * The editAs attribute, used only with two cell anchor.
+ *
+ * @var string
+ */
+ protected $editAs = '';
+
/**
* Image counter.
*
@@ -27,14 +43,14 @@ class BaseDrawing implements IComparable
*
* @var string
*/
- protected $name;
+ protected $name = '';
/**
* Description.
*
* @var string
*/
- protected $description;
+ protected $description = '';
/**
* Worksheet.
@@ -48,70 +64,84 @@ class BaseDrawing implements IComparable
*
* @var string
*/
- protected $coordinates;
+ protected $coordinates = 'A1';
/**
* Offset X.
*
* @var int
*/
- protected $offsetX;
+ protected $offsetX = 0;
/**
* Offset Y.
*
* @var int
*/
- protected $offsetY;
+ protected $offsetY = 0;
/**
* Coordinates2.
*
- * @var null|string
+ * @var string
*/
- protected $coordinates2;
+ protected $coordinates2 = '';
/**
* Offset X2.
*
* @var int
*/
- protected $offsetX2;
+ protected $offsetX2 = 0;
/**
* Offset Y2.
*
* @var int
*/
- protected $offsetY2;
+ protected $offsetY2 = 0;
/**
* Width.
*
* @var int
*/
- protected $width;
+ protected $width = 0;
/**
* Height.
*
* @var int
*/
- protected $height;
+ protected $height = 0;
+
+ /**
+ * Pixel width of image. See $width for the size the Drawing will be in the sheet.
+ *
+ * @var int
+ */
+ protected $imageWidth = 0;
+
+ /**
+ * Pixel width of image. See $height for the size the Drawing will be in the sheet.
+ *
+ * @var int
+ */
+ protected $imageHeight = 0;
/**
* Proportional resize.
*
* @var bool
*/
- protected $resizeProportional;
+ protected $resizeProportional = true;
/**
* Rotation.
*
* @var int
*/
- protected $rotation;
+ protected $rotation = 0;
/**
* Shadow.
@@ -132,7 +162,7 @@ class BaseDrawing implements IComparable
*
* @var int
*/
- protected $type;
+ protected $type = IMAGETYPE_UNKNOWN;
/**
* Create a new BaseDrawing.
@@ -140,91 +170,43 @@ class BaseDrawing implements IComparable
public function __construct()
{
// Initialise values
- $this->name = '';
- $this->description = '';
- $this->worksheet = null;
- $this->coordinates = 'A1';
- $this->offsetX = 0;
- $this->offsetY = 0;
- $this->coordinates2 = null;
- $this->offsetX2 = 0;
- $this->offsetY2 = 0;
- $this->width = 0;
- $this->height = 0;
- $this->resizeProportional = true;
- $this->rotation = 0;
- $this->shadow = new Drawing\Shadow();
- $this->type = IMAGETYPE_UNKNOWN;
+ $this->setShadow();
// Set image index
++self::$imageCounter;
$this->imageIndex = self::$imageCounter;
}
- /**
- * Get image index.
- *
- * @return int
- */
- public function getImageIndex()
+ public function getImageIndex(): int
{
return $this->imageIndex;
}
- /**
- * Get Name.
- *
- * @return string
- */
- public function getName()
+ public function getName(): string
{
return $this->name;
}
- /**
- * Set Name.
- *
- * @param string $name
- *
- * @return $this
- */
- public function setName($name)
+ public function setName(string $name): self
{
$this->name = $name;
return $this;
}
- /**
- * Get Description.
- *
- * @return string
- */
- public function getDescription()
+ public function getDescription(): string
{
return $this->description;
}
- /**
- * Set Description.
- *
- * @param string $description
- *
- * @return $this
- */
- public function setDescription($description)
+ public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
- /**
- * Get Worksheet.
- *
- * @return null|Worksheet
- */
- public function getWorksheet()
+ public function getWorksheet(): ?Worksheet
{
return $this->worksheet;
}
@@ -233,16 +215,16 @@ class BaseDrawing implements IComparable
* Set Worksheet.
*
* @param bool $overrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet?
- *
- * @return $this
*/
- public function setWorksheet(?Worksheet $worksheet = null, $overrideOld = false)
+ public function setWorksheet(?Worksheet $worksheet = null, bool $overrideOld = false): self
{
if ($this->worksheet === null) {
// Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
- $this->worksheet = $worksheet;
- $this->worksheet->getCell($this->coordinates);
- $this->worksheet->getDrawingCollection()->append($this);
+ if ($worksheet !== null) {
+ $this->worksheet = $worksheet;
+ $this->worksheet->getCell($this->coordinates);
+ $this->worksheet->getDrawingCollection()->append($this);
+ }
} else {
if ($overrideOld) {
// Remove drawing from old \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
@@ -267,168 +249,84 @@ class BaseDrawing implements IComparable
return $this;
}
- /**
- * Get Coordinates.
- *
- * @return string
- */
- public function getCoordinates()
+ public function getCoordinates(): string
{
return $this->coordinates;
}
- /**
- * Set Coordinates.
- *
- * @param string $coordinates eg: 'A1'
- *
- * @return $this
- */
- public function setCoordinates($coordinates)
+ public function setCoordinates(string $coordinates): self
{
$this->coordinates = $coordinates;
return $this;
}
- /**
- * Get OffsetX.
- *
- * @return int
- */
- public function getOffsetX()
+ public function getOffsetX(): int
{
return $this->offsetX;
}
- /**
- * Set OffsetX.
- *
- * @param int $offsetX
- *
- * @return $this
- */
- public function setOffsetX($offsetX)
+ public function setOffsetX(int $offsetX): self
{
$this->offsetX = $offsetX;
return $this;
}
- /**
- * Get OffsetY.
- *
- * @return int
- */
- public function getOffsetY()
+ public function getOffsetY(): int
{
return $this->offsetY;
}
- /**
- * Get Coordinates2.
- *
- * @return null|string
- */
- public function getCoordinates2()
- {
- return $this->coordinates2;
- }
-
- /**
- * Set Coordinates2.
- *
- * @param null|string $coordinates2 eg: 'A1'
- *
- * @return $this
- */
- public function setCoordinates2($coordinates2)
- {
- $this->coordinates2 = $coordinates2;
-
- return $this;
- }
-
- /**
- * Get OffsetX2.
- *
- * @return int
- */
- public function getOffsetX2()
- {
- return $this->offsetX2;
- }
-
- /**
- * Set OffsetX2.
- *
- * @param int $offsetX2
- *
- * @return $this
- */
- public function setOffsetX2($offsetX2)
- {
- $this->offsetX2 = $offsetX2;
-
- return $this;
- }
-
- /**
- * Get OffsetY2.
- *
- * @return int
- */
- public function getOffsetY2()
- {
- return $this->offsetY2;
- }
-
- /**
- * Set OffsetY2.
- *
- * @param int $offsetY2
- *
- * @return $this
- */
- public function setOffsetY2($offsetY2)
- {
- $this->offsetY2 = $offsetY2;
-
- return $this;
- }
-
- /**
- * Set OffsetY.
- *
- * @param int $offsetY
- *
- * @return $this
- */
- public function setOffsetY($offsetY)
+ public function setOffsetY(int $offsetY): self
{
$this->offsetY = $offsetY;
return $this;
}
- /**
- * Get Width.
- *
- * @return int
- */
- public function getWidth()
+ public function getCoordinates2(): string
+ {
+ return $this->coordinates2;
+ }
+
+ public function setCoordinates2(string $coordinates2): self
+ {
+ $this->coordinates2 = $coordinates2;
+
+ return $this;
+ }
+
+ public function getOffsetX2(): int
+ {
+ return $this->offsetX2;
+ }
+
+ public function setOffsetX2(int $offsetX2): self
+ {
+ $this->offsetX2 = $offsetX2;
+
+ return $this;
+ }
+
+ public function getOffsetY2(): int
+ {
+ return $this->offsetY2;
+ }
+
+ public function setOffsetY2(int $offsetY2): self
+ {
+ $this->offsetY2 = $offsetY2;
+
+ return $this;
+ }
+
+ public function getWidth(): int
{
return $this->width;
}
- /**
- * Set Width.
- *
- * @param int $width
- *
- * @return $this
- */
- public function setWidth($width)
+ public function setWidth(int $width): self
{
// Resize proportional?
if ($this->resizeProportional && $width != 0) {
@@ -442,24 +340,12 @@ class BaseDrawing implements IComparable
return $this;
}
- /**
- * Get Height.
- *
- * @return int
- */
- public function getHeight()
+ public function getHeight(): int
{
return $this->height;
}
- /**
- * Set Height.
- *
- * @param int $height
- *
- * @return $this
- */
- public function setHeight($height)
+ public function setHeight(int $height): self
{
// Resize proportional?
if ($this->resizeProportional && $height != 0) {
@@ -482,14 +368,9 @@ class BaseDrawing implements IComparable
* $objDrawing->setWidthAndHeight(160,120);
*
*
- * @param int $width
- * @param int $height
- *
- * @return $this
- *
* @author Vincent@luo MSN:kele_100@hotmail.com
*/
- public function setWidthAndHeight($width, $height)
+ public function setWidthAndHeight(int $width, int $height): self
{
$xratio = $width / ($this->width != 0 ? $this->width : 1);
$yratio = $height / ($this->height != 0 ? $this->height : 1);
@@ -509,72 +390,38 @@ class BaseDrawing implements IComparable
return $this;
}
- /**
- * Get ResizeProportional.
- *
- * @return bool
- */
- public function getResizeProportional()
+ public function getResizeProportional(): bool
{
return $this->resizeProportional;
}
- /**
- * Set ResizeProportional.
- *
- * @param bool $resizeProportional
- *
- * @return $this
- */
- public function setResizeProportional($resizeProportional)
+ public function setResizeProportional(bool $resizeProportional): self
{
$this->resizeProportional = $resizeProportional;
return $this;
}
- /**
- * Get Rotation.
- *
- * @return int
- */
- public function getRotation()
+ public function getRotation(): int
{
return $this->rotation;
}
- /**
- * Set Rotation.
- *
- * @param int $rotation
- *
- * @return $this
- */
- public function setRotation($rotation)
+ public function setRotation(int $rotation): self
{
$this->rotation = $rotation;
return $this;
}
- /**
- * Get Shadow.
- *
- * @return Drawing\Shadow
- */
- public function getShadow()
+ public function getShadow(): Drawing\Shadow
{
return $this->shadow;
}
- /**
- * Set Shadow.
- *
- * @return $this
- */
- public function setShadow(?Drawing\Shadow $shadow = null)
+ public function setShadow(?Drawing\Shadow $shadow = null): self
{
- $this->shadow = $shadow;
+ $this->shadow = $shadow ?? new Drawing\Shadow();
return $this;
}
@@ -589,7 +436,7 @@ class BaseDrawing implements IComparable
return md5(
$this->name .
$this->description .
- $this->worksheet->getHashCode() .
+ (($this->worksheet === null) ? '' : $this->worksheet->getHashCode()) .
$this->coordinates .
$this->offsetX .
$this->offsetY .
@@ -626,10 +473,7 @@ class BaseDrawing implements IComparable
$this->hyperlink = $hyperlink;
}
- /**
- * @return null|Hyperlink
- */
- public function getHyperlink()
+ public function getHyperlink(): ?Hyperlink
{
return $this->hyperlink;
}
@@ -639,15 +483,19 @@ class BaseDrawing implements IComparable
*/
protected function setSizesAndType(string $path): void
{
- if ($this->width == 0 && $this->height == 0 && $this->type == IMAGETYPE_UNKNOWN) {
+ if ($this->imageWidth === 0 && $this->imageHeight === 0 && $this->type === IMAGETYPE_UNKNOWN) {
$imageData = getimagesize($path);
if (is_array($imageData)) {
- $this->width = $imageData[0];
- $this->height = $imageData[1];
+ $this->imageWidth = $imageData[0];
+ $this->imageHeight = $imageData[1];
$this->type = $imageData[2];
}
}
+ if ($this->width === 0 && $this->height === 0) {
+ $this->width = $this->imageWidth;
+ $this->height = $this->imageHeight;
+ }
}
/**
@@ -657,4 +505,31 @@ class BaseDrawing implements IComparable
{
return $this->type;
}
+
+ public function getImageWidth(): int
+ {
+ return $this->imageWidth;
+ }
+
+ public function getImageHeight(): int
+ {
+ return $this->imageHeight;
+ }
+
+ public function getEditAs(): string
+ {
+ return $this->editAs;
+ }
+
+ public function setEditAs(string $editAs): self
+ {
+ $this->editAs = $editAs;
+
+ return $this;
+ }
+
+ public function validEditAs(): bool
+ {
+ return in_array($this->editAs, self::VALID_EDIT_AS);
+ }
}
diff --git a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
index 91acbb7b..e65541dc 100644
--- a/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
+++ b/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
@@ -47,6 +47,9 @@ class MemoryDrawing extends BaseDrawing
*/
private $uniqueName;
+ /** @var null|resource */
+ private $alwaysNull;
+
/**
* Create a new MemoryDrawing.
*/
@@ -56,6 +59,7 @@ class MemoryDrawing extends BaseDrawing
$this->renderingFunction = self::RENDERING_DEFAULT;
$this->mimeType = self::MIMETYPE_DEFAULT;
$this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
+ $this->alwaysNull = null;
// Initialize parent
parent::__construct();
@@ -64,8 +68,9 @@ class MemoryDrawing extends BaseDrawing
public function __destruct()
{
if ($this->imageResource) {
- imagedestroy($this->imageResource);
- $this->imageResource = null;
+ $rslt = @imagedestroy($this->imageResource);
+ // "Fix" for Scrutinizer
+ $this->imageResource = $rslt ? null : $this->alwaysNull;
}
}
diff --git a/src/PhpSpreadsheet/Worksheet/Row.php b/src/PhpSpreadsheet/Worksheet/Row.php
index b5933356..a5f8f326 100644
--- a/src/PhpSpreadsheet/Worksheet/Row.php
+++ b/src/PhpSpreadsheet/Worksheet/Row.php
@@ -35,8 +35,7 @@ class Row
*/
public function __destruct()
{
- // @phpstan-ignore-next-line
- $this->worksheet = null;
+ $this->worksheet = null; // @phpstan-ignore-line
}
/**
diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php
index d83f512a..08423356 100644
--- a/src/PhpSpreadsheet/Worksheet/Worksheet.php
+++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -2279,16 +2279,18 @@ class Worksheet implements IComparable
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
$pColumnIndex = Coordinate::columnIndexFromString($column);
- if ($pColumnIndex > $highestColumnIndex) {
- return $this;
- }
-
$holdColumnDimensions = $this->removeColumnDimensions($pColumnIndex, $numberOfColumns);
$column = Coordinate::stringFromColumnIndex($pColumnIndex + $numberOfColumns);
$objReferenceHelper = ReferenceHelper::getInstance();
$objReferenceHelper->insertNewBefore($column . '1', -$numberOfColumns, 0, $this);
+ $this->columnDimensions = $holdColumnDimensions;
+
+ if ($pColumnIndex > $highestColumnIndex) {
+ return $this;
+ }
+
$maxPossibleColumnsToBeRemoved = $highestColumnIndex - $pColumnIndex + 1;
for ($c = 0, $n = min($maxPossibleColumnsToBeRemoved, $numberOfColumns); $c < $n; ++$c) {
@@ -2296,8 +2298,6 @@ class Worksheet implements IComparable
$highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
}
- $this->columnDimensions = $holdColumnDimensions;
-
$this->garbageCollect();
return $this;
diff --git a/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php b/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
new file mode 100644
index 00000000..0f78b8c5
--- /dev/null
+++ b/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
@@ -0,0 +1,76 @@
+parser = $parser;
+ }
+
+ /**
+ * @param mixed $condition
+ */
+ public function processCondition($condition, string $cellRange): void
+ {
+ $this->condition = $condition;
+ $this->cellRange = $cellRange;
+
+ if (is_int($condition) || is_float($condition)) {
+ $this->size = ($condition <= 65535 ? 3 : 0x0000);
+ $this->tokens = pack('Cv', 0x1E, $condition);
+ } else {
+ try {
+ $formula = Wizard\WizardAbstract::reverseAdjustCellRef((string) $condition, $cellRange);
+ $this->parser->parse($formula);
+ $this->tokens = $this->parser->toReversePolish();
+ $this->size = strlen($this->tokens ?? '');
+ } catch (PhpSpreadsheetException $e) {
+ // In the event of a parser error with a formula value, we set the expression to ptgInt + 0
+ $this->tokens = pack('Cv', 0x1E, 0);
+ $this->size = 3;
+ }
+ }
+ }
+
+ public function tokens(): ?string
+ {
+ return $this->tokens;
+ }
+
+ public function size(): int
+ {
+ return $this->size;
+ }
+}
diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
index fcf7789a..16059e28 100644
--- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
+++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
@@ -539,37 +539,44 @@ class Worksheet extends BIFFwriter
$this->writeSheetProtection();
$this->writeRangeProtection();
- $arrConditionalStyles = $phpSheet->getConditionalStylesCollection();
+ // Write Conditional Formatting Rules and Styles
+ $this->writeConditionalFormatting();
+
+ $this->storeEof();
+ }
+
+ private function writeConditionalFormatting(): void
+ {
+ $conditionalFormulaHelper = new ConditionalHelper($this->parser);
+
+ $arrConditionalStyles = $this->phpSheet->getConditionalStylesCollection();
if (!empty($arrConditionalStyles)) {
$arrConditional = [];
- $cfHeaderWritten = false;
// Write ConditionalFormattingTable records
foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
+ $cfHeaderWritten = false;
foreach ($conditionalStyles as $conditional) {
/** @var Conditional $conditional */
if (
- $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION ||
- $conditional->getConditionType() == Conditional::CONDITION_CELLIS
+ $conditional->getConditionType() === Conditional::CONDITION_EXPRESSION ||
+ $conditional->getConditionType() === Conditional::CONDITION_CELLIS
) {
// Write CFHEADER record (only if there are Conditional Styles that we are able to write)
if ($cfHeaderWritten === false) {
- $this->writeCFHeader();
- $cfHeaderWritten = true;
+ $cfHeaderWritten = $this->writeCFHeader($cellCoordinate, $conditionalStyles);
}
- if (!isset($arrConditional[$conditional->getHashCode()])) {
+ if ($cfHeaderWritten === true && !isset($arrConditional[$conditional->getHashCode()])) {
// This hash code has been handled
$arrConditional[$conditional->getHashCode()] = true;
// Write CFRULE record
- $this->writeCFRule($conditional);
+ $this->writeCFRule($conditionalFormulaHelper, $conditional, $cellCoordinate);
}
}
}
}
}
-
- $this->storeEof();
}
/**
@@ -2774,11 +2781,14 @@ class Worksheet extends BIFFwriter
/**
* Write CFRule Record.
*/
- private function writeCFRule(Conditional $conditional): void
- {
+ private function writeCFRule(
+ ConditionalHelper $conditionalFormulaHelper,
+ Conditional $conditional,
+ string $cellRange
+ ): void {
$record = 0x01B1; // Record identifier
- $type = null; // Type of the CF
- $operatorType = null; // Comparison operator
+ $type = null; // Type of the CF
+ $operatorType = null; // Comparison operator
if ($conditional->getConditionType() == Conditional::CONDITION_EXPRESSION) {
$type = 0x02;
@@ -2827,21 +2837,23 @@ class Worksheet extends BIFFwriter
// $szValue2 : size of the formula data for second value or formula
$arrConditions = $conditional->getConditions();
$numConditions = count($arrConditions);
- if ($numConditions == 1) {
- $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
- $szValue2 = 0x0000;
- $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
- $operand2 = null;
- } elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) {
- $szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
- $szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
- $operand1 = pack('Cv', 0x1E, $arrConditions[0]);
- $operand2 = pack('Cv', 0x1E, $arrConditions[1]);
- } else {
- $szValue1 = 0x0000;
- $szValue2 = 0x0000;
- $operand1 = null;
- $operand2 = null;
+
+ $szValue1 = 0x0000;
+ $szValue2 = 0x0000;
+ $operand1 = null;
+ $operand2 = null;
+
+ if ($numConditions === 1) {
+ $conditionalFormulaHelper->processCondition($arrConditions[0], $cellRange);
+ $szValue1 = $conditionalFormulaHelper->size();
+ $operand1 = $conditionalFormulaHelper->tokens();
+ } elseif ($numConditions === 2 && ($conditional->getOperatorType() === Conditional::OPERATOR_BETWEEN)) {
+ $conditionalFormulaHelper->processCondition($arrConditions[0], $cellRange);
+ $szValue1 = $conditionalFormulaHelper->size();
+ $operand1 = $conditionalFormulaHelper->tokens();
+ $conditionalFormulaHelper->processCondition($arrConditions[1], $cellRange);
+ $szValue2 = $conditionalFormulaHelper->size();
+ $operand2 = $conditionalFormulaHelper->tokens();
}
// $flags : Option flags
@@ -3127,8 +3139,10 @@ class Worksheet extends BIFFwriter
/**
* Write CFHeader record.
+ *
+ * @param Conditional[] $conditionalStyles
*/
- private function writeCFHeader(): void
+ private function writeCFHeader(string $cellCoordinate, array $conditionalStyles): bool
{
$record = 0x01B0; // Record identifier
$length = 0x0016; // Bytes to follow
@@ -3137,33 +3151,32 @@ class Worksheet extends BIFFwriter
$numColumnMax = null;
$numRowMin = null;
$numRowMax = null;
+
$arrConditional = [];
- foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
- foreach ($conditionalStyles as $conditional) {
- if (
- $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION ||
- $conditional->getConditionType() == Conditional::CONDITION_CELLIS
- ) {
- if (!in_array($conditional->getHashCode(), $arrConditional)) {
- $arrConditional[] = $conditional->getHashCode();
- }
- // Cells
- $rangeCoordinates = Coordinate::rangeBoundaries($cellCoordinate);
- if ($numColumnMin === null || ($numColumnMin > $rangeCoordinates[0][0])) {
- $numColumnMin = $rangeCoordinates[0][0];
- }
- if ($numColumnMax === null || ($numColumnMax < $rangeCoordinates[1][0])) {
- $numColumnMax = $rangeCoordinates[1][0];
- }
- if ($numRowMin === null || ($numRowMin > $rangeCoordinates[0][1])) {
- $numRowMin = (int) $rangeCoordinates[0][1];
- }
- if ($numRowMax === null || ($numRowMax < $rangeCoordinates[1][1])) {
- $numRowMax = (int) $rangeCoordinates[1][1];
- }
- }
+ foreach ($conditionalStyles as $conditional) {
+ if (!in_array($conditional->getHashCode(), $arrConditional)) {
+ $arrConditional[] = $conditional->getHashCode();
+ }
+ // Cells
+ $rangeCoordinates = Coordinate::rangeBoundaries($cellCoordinate);
+ if ($numColumnMin === null || ($numColumnMin > $rangeCoordinates[0][0])) {
+ $numColumnMin = $rangeCoordinates[0][0];
+ }
+ if ($numColumnMax === null || ($numColumnMax < $rangeCoordinates[1][0])) {
+ $numColumnMax = $rangeCoordinates[1][0];
+ }
+ if ($numRowMin === null || ($numRowMin > $rangeCoordinates[0][1])) {
+ $numRowMin = (int) $rangeCoordinates[0][1];
+ }
+ if ($numRowMax === null || ($numRowMax < $rangeCoordinates[1][1])) {
+ $numRowMax = (int) $rangeCoordinates[1][1];
}
}
+
+ if (count($arrConditional) === 0) {
+ return false;
+ }
+
$needRedraw = 1;
$cellRange = pack('vvvv', $numRowMin - 1, $numRowMax - 1, $numColumnMin - 1, $numColumnMax - 1);
@@ -3173,6 +3186,8 @@ class Worksheet extends BIFFwriter
$data .= pack('v', 0x0001);
$data .= $cellRange;
$this->append($header . $data);
+
+ return true;
}
private function getDataBlockProtection(Conditional $conditional): int
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
index 6868212a..816bb9d4 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
@@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
@@ -56,7 +57,10 @@ class Drawing extends WriterPart
// Loop through charts and write the chart position
if ($chartCount > 0) {
for ($c = 0; $c < $chartCount; ++$c) {
- $this->writeChart($objWriter, $worksheet->getChartByIndex($c), $c + $i);
+ $chart = $worksheet->getChartByIndex((string) $c);
+ if ($chart !== false) {
+ $this->writeChart($objWriter, $chart, $c + $i);
+ }
}
}
}
@@ -90,16 +94,16 @@ class Drawing extends WriterPart
$objWriter->startElement('xdr:twoCellAnchor');
$objWriter->startElement('xdr:from');
- $objWriter->writeElement('xdr:col', $tlColRow[0] - 1);
- $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['xOffset']));
- $objWriter->writeElement('xdr:row', $tlColRow[1] - 1);
- $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($tl['yOffset']));
+ $objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1));
+ $objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset']));
+ $objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1));
+ $objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:to');
- $objWriter->writeElement('xdr:col', $brColRow[0] - 1);
- $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['xOffset']));
- $objWriter->writeElement('xdr:row', $brColRow[1] - 1);
- $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($br['yOffset']));
+ $objWriter->writeElement('xdr:col', (string) ($brColRow[0] - 1));
+ $objWriter->writeElement('xdr:colOff', self::stringEmu($br['xOffset']));
+ $objWriter->writeElement('xdr:row', (string) ($brColRow[1] - 1));
+ $objWriter->writeElement('xdr:rowOff', self::stringEmu($br['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:graphicFrame');
@@ -107,7 +111,7 @@ class Drawing extends WriterPart
$objWriter->startElement('xdr:nvGraphicFramePr');
$objWriter->startElement('xdr:cNvPr');
$objWriter->writeAttribute('name', 'Chart ' . $relationId);
- $objWriter->writeAttribute('id', 1025 * $relationId);
+ $objWriter->writeAttribute('id', (string) (1025 * $relationId));
$objWriter->endElement();
$objWriter->startElement('xdr:cNvGraphicFramePr');
$objWriter->startElement('a:graphicFrameLocks');
@@ -153,28 +157,31 @@ class Drawing extends WriterPart
public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
{
if ($relationId >= 0) {
- $isTwoCellAnchor = $drawing->getCoordinates2() !== null;
+ $isTwoCellAnchor = $drawing->getCoordinates2() !== '';
if ($isTwoCellAnchor) {
// xdr:twoCellAnchor
$objWriter->startElement('xdr:twoCellAnchor');
+ if ($drawing->validEditAs()) {
+ $objWriter->writeAttribute('editAs', $drawing->getEditAs());
+ }
// Image location
$aCoordinates = Coordinate::indexesFromString($drawing->getCoordinates());
$aCoordinates2 = Coordinate::indexesFromString($drawing->getCoordinates2());
// xdr:from
$objWriter->startElement('xdr:from');
- $objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
- $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
- $objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
- $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
+ $objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1));
+ $objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX()));
+ $objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1));
+ $objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY()));
$objWriter->endElement();
// xdr:to
$objWriter->startElement('xdr:to');
- $objWriter->writeElement('xdr:col', $aCoordinates2[0] - 1);
- $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX2()));
- $objWriter->writeElement('xdr:row', $aCoordinates2[1] - 1);
- $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY2()));
+ $objWriter->writeElement('xdr:col', (string) ($aCoordinates2[0] - 1));
+ $objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX2()));
+ $objWriter->writeElement('xdr:row', (string) ($aCoordinates2[1] - 1));
+ $objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY2()));
$objWriter->endElement();
} else {
// xdr:oneCellAnchor
@@ -184,16 +191,16 @@ class Drawing extends WriterPart
// xdr:from
$objWriter->startElement('xdr:from');
- $objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
- $objWriter->writeElement('xdr:colOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetX()));
- $objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
- $objWriter->writeElement('xdr:rowOff', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getOffsetY()));
+ $objWriter->writeElement('xdr:col', (string) ($aCoordinates[0] - 1));
+ $objWriter->writeElement('xdr:colOff', self::stringEmu($drawing->getOffsetX()));
+ $objWriter->writeElement('xdr:row', (string) ($aCoordinates[1] - 1));
+ $objWriter->writeElement('xdr:rowOff', self::stringEmu($drawing->getOffsetY()));
$objWriter->endElement();
// xdr:ext
$objWriter->startElement('xdr:ext');
- $objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
- $objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
+ $objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
+ $objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
@@ -205,7 +212,7 @@ class Drawing extends WriterPart
// xdr:cNvPr
$objWriter->startElement('xdr:cNvPr');
- $objWriter->writeAttribute('id', $relationId);
+ $objWriter->writeAttribute('id', (string) $relationId);
$objWriter->writeAttribute('name', $drawing->getName());
$objWriter->writeAttribute('descr', $drawing->getDescription());
@@ -247,11 +254,11 @@ class Drawing extends WriterPart
// a:xfrm
$objWriter->startElement('a:xfrm');
- $objWriter->writeAttribute('rot', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($drawing->getRotation()));
+ $objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation()));
if ($isTwoCellAnchor) {
$objWriter->startElement('a:ext');
- $objWriter->writeAttribute('cx', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getWidth()));
- $objWriter->writeAttribute('cy', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getHeight()));
+ $objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
+ $objWriter->writeAttribute('cy', self::stringEmu($drawing->getHeight()));
$objWriter->endElement();
}
$objWriter->endElement();
@@ -271,9 +278,9 @@ class Drawing extends WriterPart
// a:outerShdw
$objWriter->startElement('a:outerShdw');
- $objWriter->writeAttribute('blurRad', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getShadow()->getBlurRadius()));
- $objWriter->writeAttribute('dist', \PhpOffice\PhpSpreadsheet\Shared\Drawing::pixelsToEMU($drawing->getShadow()->getDistance()));
- $objWriter->writeAttribute('dir', \PhpOffice\PhpSpreadsheet\Shared\Drawing::degreesToAngle($drawing->getShadow()->getDirection()));
+ $objWriter->writeAttribute('blurRad', self::stringEmu($drawing->getShadow()->getBlurRadius()));
+ $objWriter->writeAttribute('dist', self::stringEmu($drawing->getShadow()->getDistance()));
+ $objWriter->writeAttribute('dir', (string) SharedDrawing::degreesToAngle($drawing->getShadow()->getDirection()));
$objWriter->writeAttribute('algn', $drawing->getShadow()->getAlignment());
$objWriter->writeAttribute('rotWithShape', '0');
@@ -283,7 +290,7 @@ class Drawing extends WriterPart
// a:alpha
$objWriter->startElement('a:alpha');
- $objWriter->writeAttribute('val', $drawing->getShadow()->getAlpha() * 1000);
+ $objWriter->writeAttribute('val', (string) ($drawing->getShadow()->getAlpha() * 1000));
$objWriter->endElement();
$objWriter->endElement();
@@ -528,4 +535,9 @@ class Drawing extends WriterPart
$objWriter->writeAttribute('r:id', 'rId' . $hlinkClickId);
$objWriter->endElement();
}
+
+ private static function stringEmu(int $pixelValue): string
+ {
+ return (string) SharedDrawing::pixelsToEMU($pixelValue);
+ }
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/CalculationFunctionListTest.php b/tests/PhpSpreadsheetTests/Calculation/CalculationFunctionListTest.php
index bb71ee6e..e95c3267 100644
--- a/tests/PhpSpreadsheetTests/Calculation/CalculationFunctionListTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/CalculationFunctionListTest.php
@@ -41,7 +41,7 @@ class CalculationFunctionListTest extends TestCase
* @param array|string $functionCall
* @param string $argumentCount
*/
- public function testGetFunctions($category, $functionCall, $argumentCount): void
+ public function testGetFunctions(/** @scrutinizer ignore-unused */ $category, $functionCall, /** @scrutinizer ignore-unused */ $argumentCount): void
{
self::assertIsCallable($functionCall);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ComplexTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ComplexTest.php
index 451908e0..827654ad 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ComplexTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ComplexTest.php
@@ -4,16 +4,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Engineering;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class ComplexTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerCOMPLEX
*
@@ -21,7 +15,15 @@ class ComplexTest extends TestCase
*/
public function testCOMPLEX($expectedResult, ...$args): void
{
- $result = Engineering::COMPLEX(...$args);
+ if (count($args) === 0) {
+ $result = Engineering::COMPLEX();
+ } elseif (count($args) === 1) {
+ $result = Engineering::COMPLEX($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Engineering::COMPLEX($args[0], $args[1]);
+ } else {
+ $result = Engineering::COMPLEX($args[0], $args[1], $args[2]);
+ }
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarDeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarDeTest.php
index cbb8e031..22d99eca 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarDeTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarDeTest.php
@@ -4,16 +4,10 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class DollarDeTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerDOLLARDE
*
@@ -21,7 +15,13 @@ class DollarDeTest extends TestCase
*/
public function testDOLLARDE($expectedResult, ...$args): void
{
- $result = Financial::DOLLARDE(...$args);
+ if (count($args) === 0) {
+ $result = Financial::DOLLARDE();
+ } elseif (count($args) === 1) {
+ $result = Financial::DOLLARDE($args[0]);
+ } else {
+ $result = Financial::DOLLARDE($args[0], $args[1]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarFrTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarFrTest.php
index ce74595a..79e3d5ca 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarFrTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/DollarFrTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class DollarFrTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerDOLLARFR
*
@@ -20,7 +14,13 @@ class DollarFrTest extends TestCase
*/
public function testDOLLARFR($expectedResult, ...$args): void
{
- $result = Financial::DOLLARFR(...$args);
+ if (count($args) === 0) {
+ $result = Financial::DOLLARFR();
+ } elseif (count($args) === 1) {
+ $result = Financial::DOLLARFR($args[0]);
+ } else {
+ $result = Financial::DOLLARFR($args[0], $args[1]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvTest.php
index 8d17d852..0e621766 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/FvTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class FvTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerFV
*
@@ -20,7 +14,19 @@ class FvTest extends TestCase
*/
public function testFV($expectedResult, array $args): void
{
- $result = Financial::FV(...$args);
+ if (count($args) === 0) {
+ $result = Financial::FV();
+ } elseif (count($args) === 1) {
+ $result = Financial::FV($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Financial::FV($args[0], $args[1]);
+ } elseif (count($args) === 3) {
+ $result = Financial::FV($args[0], $args[1], $args[2]);
+ } elseif (count($args) === 4) {
+ $result = Financial::FV($args[0], $args[1], $args[2], $args[3]);
+ } else {
+ $result = Financial::FV($args[0], $args[1], $args[2], $args[3], $args[4]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NPerTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NPerTest.php
index 87230eca..2d7ded04 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NPerTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/NPerTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class NPerTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerNPER
*
@@ -20,7 +14,19 @@ class NPerTest extends TestCase
*/
public function testNPER($expectedResult, array $args): void
{
- $result = Financial::NPER(...$args);
+ if (count($args) === 0) {
+ $result = Financial::NPER();
+ } elseif (count($args) === 1) {
+ $result = Financial::NPER($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Financial::NPER($args[0], $args[1]);
+ } elseif (count($args) === 3) {
+ $result = Financial::NPER($args[0], $args[1], $args[2]);
+ } elseif (count($args) === 4) {
+ $result = Financial::NPER($args[0], $args[1], $args[2], $args[3]);
+ } else {
+ $result = Financial::NPER($args[0], $args[1], $args[2], $args[3], $args[4]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PDurationTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PDurationTest.php
index a8d3095a..dbe1b09a 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PDurationTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PDurationTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class PDurationTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerPDURATION
*
@@ -20,7 +14,15 @@ class PDurationTest extends TestCase
*/
public function testPDURATION($expectedResult, array $args): void
{
- $result = Financial::PDURATION(...$args);
+ if (count($args) === 0) {
+ $result = Financial::PDURATION();
+ } elseif (count($args) === 1) {
+ $result = Financial::PDURATION($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Financial::PDURATION($args[0], $args[1]);
+ } else {
+ $result = Financial::PDURATION($args[0], $args[1], $args[2]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PmtTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PmtTest.php
index d996db10..5c1f6556 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PmtTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PmtTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class PmtTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerPMT
*
@@ -23,7 +17,13 @@ class PmtTest extends TestCase
$interestRate = array_shift($args);
$numberOfPeriods = array_shift($args);
$presentValue = array_shift($args);
- $result = Financial::PMT($interestRate, $numberOfPeriods, $presentValue, ...$args);
+ if (count($args) === 0) {
+ $result = Financial::PMT($interestRate, $numberOfPeriods, $presentValue);
+ } elseif (count($args) === 1) {
+ $result = Financial::PMT($interestRate, $numberOfPeriods, $presentValue, $args[0]);
+ } else {
+ $result = Financial::PMT($interestRate, $numberOfPeriods, $presentValue, $args[0], $args[1]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PvTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PvTest.php
index c1b40231..4e4a8f80 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PvTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/PvTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class PvTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerPV
*
@@ -20,7 +14,19 @@ class PvTest extends TestCase
*/
public function testPV($expectedResult, array $args): void
{
- $result = Financial::PV(...$args);
+ if (count($args) === 0) {
+ $result = Financial::PV();
+ } elseif (count($args) === 1) {
+ $result = Financial::PV($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Financial::PV($args[0], $args[1]);
+ } elseif (count($args) === 3) {
+ $result = Financial::PV($args[0], $args[1], $args[2]);
+ } elseif (count($args) === 4) {
+ $result = Financial::PV($args[0], $args[1], $args[2], $args[3]);
+ } else {
+ $result = Financial::PV($args[0], $args[1], $args[2], $args[3], $args[4]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RriTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RriTest.php
index e641b590..9107ba96 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RriTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/RriTest.php
@@ -3,16 +3,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\Financial;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PHPUnit\Framework\TestCase;
class RriTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerRRI
*
@@ -20,7 +14,15 @@ class RriTest extends TestCase
*/
public function testRRI($expectedResult, array $args): void
{
- $result = Financial::RRI(...$args);
+ if (count($args) === 0) {
+ $result = Financial::RRI();
+ } elseif (count($args) === 1) {
+ $result = Financial::RRI($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Financial::RRI($args[0], $args[1]);
+ } else {
+ $result = Financial::RRI($args[0], $args[1], $args[2]);
+ }
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/UsDollarTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/UsDollarTest.php
index 770b8ffa..db8fa112 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/UsDollarTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Financial/UsDollarTest.php
@@ -12,9 +12,13 @@ class UsDollarTest extends TestCase
*
* @param mixed $expectedResult
*/
- public function testUSDOLLAR($expectedResult, ...$args): void
+ public function testUSDOLLAR($expectedResult, float $amount, ?int $precision = null): void
{
- $result = Dollar::format(...$args);
+ if ($precision === null) {
+ $result = Dollar::format($amount);
+ } else {
+ $result = Dollar::format($amount, $precision);
+ }
self::assertSame($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfTest.php
index b3e21986..58997161 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/IfTest.php
@@ -2,17 +2,11 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Logical;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Logical;
use PHPUnit\Framework\TestCase;
class IfTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerIF
*
@@ -20,7 +14,15 @@ class IfTest extends TestCase
*/
public function testIF($expectedResult, ...$args): void
{
- $result = Logical::statementIf(...$args);
+ if (count($args) === 0) {
+ $result = Logical::statementIf();
+ } elseif (count($args) === 1) {
+ $result = Logical::statementIf($args[0]);
+ } elseif (count($args) === 2) {
+ $result = Logical::statementIf($args[0], $args[1]);
+ } else {
+ $result = Logical::statementIf($args[0], $args[1], $args[2]);
+ }
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php
index 03eb15ba..6d8b2ca9 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Logical/NotTest.php
@@ -3,17 +3,11 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Logical;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Logical;
use PHPUnit\Framework\TestCase;
class NotTest extends TestCase
{
- protected function setUp(): void
- {
- Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL);
- }
-
/**
* @dataProvider providerNOT
*
@@ -21,7 +15,11 @@ class NotTest extends TestCase
*/
public function testNOT($expectedResult, ...$args): void
{
- $result = Logical::NOT(...$args);
+ if (count($args) === 0) {
+ $result = Logical::NOT();
+ } else {
+ $result = Logical::NOT($args[0]);
+ }
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/FilterTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/FilterTest.php
new file mode 100644
index 00000000..5a584c46
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/FilterTest.php
@@ -0,0 +1,85 @@
+sampleDataForRow(), $criteria);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function testFilterByColumn(): void
+ {
+ $criteria = [[false, false, true, false, true, false, false, false, true, true]];
+ $expectedResult = [
+ ['Betty', 'Charlotte', 'Oliver', 'Zoe'],
+ ['B', 'B', 'B', 'B'],
+ [1, 2, 4, 8],
+ ];
+ $result = Filter::filter($this->sampleDataForColumn(), $criteria);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function testFilterException(): void
+ {
+ $criteria = 'INVALID';
+ $result = Filter::filter($this->sampleDataForColumn(), $criteria);
+ self::assertSame(ExcelError::VALUE(), $result);
+ }
+
+ public function testFilterEmpty(): void
+ {
+ $criteria = [[false], [false], [false]];
+ $expectedResult = ExcelError::CALC();
+ $result = Filter::filter([[1], [2], [3]], $criteria);
+ self::assertSame($expectedResult, $result);
+
+ $expectedResult = 'Invalid Data';
+ $result = Filter::filter([[1], [2], [3]], $criteria, $expectedResult);
+ self::assertSame($expectedResult, $result);
+ }
+
+ protected function sampleDataForRow(): array
+ {
+ return [
+ ['East', 'Tom', 'Apple', 6830],
+ ['West', 'Fred', 'Grape', 5619],
+ ['North', 'Amy', 'Pear', 4565],
+ ['South', 'Sal', 'Banana', 5323],
+ ['East', 'Fritz', 'Apple', 4394],
+ ['West', 'Sravan', 'Grape', 7195],
+ ['North', 'Xi', 'Pear', 5231],
+ ['South', 'Hector', 'Banana', 2427],
+ ['East', 'Tom', 'Banana', 4213],
+ ['West', 'Fred', 'Pear', 3239],
+ ['North', 'Amy', 'Grape', 6420],
+ ['South', 'Sal', 'Apple', 1310],
+ ['East', 'Fritz', 'Banana', 6274],
+ ['West', 'Sravan', 'Pear', 4894],
+ ['North', 'Xi', 'Grape', 7580],
+ ['South', 'Hector', 'Apple', 8144],
+ ];
+ }
+
+ protected function sampleDataForColumn(): array
+ {
+ return [
+ ['Aiden', 'Andrew', 'Betty', 'Caden', 'Charlotte', 'Emma', 'Isabella', 'Mason', 'Oliver', 'Zoe'],
+ ['A', 'C', 'B', 'A', 'B', 'C', 'A', 'A', 'B', 'B'],
+ [0, 4, 1, 2, 2, 0, 2, 4, 4, 8],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/MatrixHelperFunctionsTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/MatrixHelperFunctionsTest.php
new file mode 100644
index 00000000..e5b4cace
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/MatrixHelperFunctionsTest.php
@@ -0,0 +1,77 @@
+ ['A', 1],
+ 'Mismatched sortIndex count' => [[1, 2, 3, 4], 1],
+ 'Non-numeric sortOrder' => [[1, 2, 3], 'A'],
+ 'Invalid negative sortOrder' => [[1, 2, 3], -2],
+ 'Zero sortOrder' => [[1, 2, 3], 0],
+ 'Invalid positive sortOrder' => [[1, 2, 3], 2],
+ ];
+ }
+
+ /**
+ * @dataProvider providerSortByRow
+ */
+ public function testSortByRow(array $expectedResult, array $matrix, ...$args): void
+ {
+ $result = Sort::sortBy($matrix, ...$args);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function providerSortByRow(): array
+ {
+ return [
+ 'Simple sort by age' => [
+ [
+ ['Fritz', 19],
+ ['Xi', 19],
+ ['Amy', 22],
+ ['Srivan', 39],
+ ['Tom', 52],
+ ['Fred', 65],
+ ['Hector', 66],
+ ['Sal', 73],
+ ],
+ $this->sampleDataForSimpleSort(),
+ array_column($this->sampleDataForSimpleSort(), 1),
+ ],
+ 'Simple sort by name' => [
+ [
+ ['Amy', 22],
+ ['Fred', 65],
+ ['Fritz', 19],
+ ['Hector', 66],
+ ['Sal', 73],
+ ['Srivan', 39],
+ ['Tom', 52],
+ ['Xi', 19],
+ ],
+ $this->sampleDataForSimpleSort(),
+ array_column($this->sampleDataForSimpleSort(), 0),
+ ],
+ 'Row vector' => [
+ [
+ ['Amy', 22],
+ ['Fred', 65],
+ ['Fritz', 19],
+ ['Hector', 66],
+ ['Sal', 73],
+ ['Srivan', 39],
+ ['Tom', 52],
+ ['Xi', 19],
+ ],
+ $this->sampleDataForSimpleSort(),
+ ['Tom', 'Fred', 'Amy', 'Sal', 'Fritz', 'Srivan', 'Xi', 'Hector'],
+ ],
+ 'Column vector' => [
+ [
+ ['Amy', 22],
+ ['Fred', 65],
+ ['Fritz', 19],
+ ['Hector', 66],
+ ['Sal', 73],
+ ['Srivan', 39],
+ ['Tom', 52],
+ ['Xi', 19],
+ ],
+ $this->sampleDataForSimpleSort(),
+ [['Tom'], ['Fred'], ['Amy'], ['Sal'], ['Fritz'], ['Srivan'], ['Xi'], ['Hector']],
+ ],
+ 'Sort by region asc, name asc' => [
+ [
+ ['East', 'Fritz', 19],
+ ['East', 'Tom', 52],
+ ['North', 'Amy', 22],
+ ['North', 'Xi', 19],
+ ['South', 'Hector', 66],
+ ['South', 'Sal', 73],
+ ['West', 'Fred', 65],
+ ['West', 'Srivan', 39],
+ ],
+ $this->sampleDataForMultiSort(),
+ array_column($this->sampleDataForMultiSort(), 0),
+ Sort::ORDER_ASCENDING,
+ array_column($this->sampleDataForMultiSort(), 1),
+ ],
+ 'Sort by region asc, age desc' => [
+ [
+ ['East', 'Tom', 52],
+ ['East', 'Fritz', 19],
+ ['North', 'Amy', 22],
+ ['North', 'Xi', 19],
+ ['South', 'Sal', 73],
+ ['South', 'Hector', 66],
+ ['West', 'Fred', 65],
+ ['West', 'Srivan', 39],
+ ],
+ $this->sampleDataForMultiSort(),
+ array_column($this->sampleDataForMultiSort(), 0),
+ Sort::ORDER_ASCENDING,
+ array_column($this->sampleDataForMultiSort(), 2),
+ Sort::ORDER_DESCENDING,
+ ],
+ ];
+ }
+
+ private function sampleDataForSimpleSort(): array
+ {
+ return [
+ ['Tom', 52],
+ ['Fred', 65],
+ ['Amy', 22],
+ ['Sal', 73],
+ ['Fritz', 19],
+ ['Srivan', 39],
+ ['Xi', 19],
+ ['Hector', 66],
+ ];
+ }
+
+ private function sampleDataForMultiSort(): array
+ {
+ return [
+ ['North', 'Amy', 22],
+ ['West', 'Fred', 65],
+ ['East', 'Fritz', 19],
+ ['South', 'Hector', 66],
+ ['South', 'Sal', 73],
+ ['West', 'Srivan', 39],
+ ['East', 'Tom', 52],
+ ['North', 'Xi', 19],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php
new file mode 100644
index 00000000..bd120e30
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/SortTest.php
@@ -0,0 +1,210 @@
+ [-1, -1],
+ 'Non-numeric sortIndex' => ['A', -1],
+ 'Zero sortIndex' => [0, -1],
+ 'Too high sortIndex' => [3, -1],
+ 'Non-numeric sortOrder' => [1, 'A'],
+ 'Invalid negative sortOrder' => [1, -2],
+ 'Zero sortOrder' => [1, 0],
+ 'Invalid positive sortOrder' => [1, 2],
+ 'Too many sortOrders (scalar and array)' => [1, [-1, 1]],
+ 'Too many sortOrders (both array)' => [[1, 2], [1, 2, 3]],
+ 'Zero positive sortIndex in vector' => [[0, 1]],
+ 'Too high sortIndex in vector' => [[1, 3]],
+ 'Invalid sortOrder in vector' => [[1, 2], [1, -2]],
+ ];
+ }
+
+ /**
+ * @dataProvider providerSortByRow
+ */
+ public function testSortByRow(array $expectedResult, array $matrix, int $sortIndex, int $sortOrder = Sort::ORDER_ASCENDING): void
+ {
+ $result = Sort::sort($matrix, $sortIndex, $sortOrder);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function providerSortByRow(): array
+ {
+ return [
+ [
+ [[142], [378], [404], [445], [483], [622], [650], [691], [783], [961]],
+ $this->sampleDataForRow(),
+ 1,
+ ],
+ [
+ [[961], [783], [691], [650], [622], [483], [445], [404], [378], [142]],
+ $this->sampleDataForRow(),
+ 1,
+ Sort::ORDER_DESCENDING,
+ ],
+ [
+ [['Peaches', 25], ['Cherries', 29], ['Grapes', 31], ['Lemons', 34], ['Oranges', 36], ['Apples', 38], ['Pears', 40]],
+ [['Apples', 38], ['Cherries', 29], ['Grapes', 31], ['Lemons', 34], ['Oranges', 36], ['Peaches', 25], ['Pears', 40]],
+ 2,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider providerSortByRowMultiLevel
+ */
+ public function testSortByRowMultiLevel(array $expectedResult, array $matrix, array $sortIndex, int $sortOrder = Sort::ORDER_ASCENDING): void
+ {
+ $result = Sort::sort($matrix, $sortIndex, $sortOrder);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function providerSortByRowMultiLevel(): array
+ {
+ return [
+ [
+ [
+ ['East', 'Grapes', 31],
+ ['East', 'Lemons', 36],
+ ['North', 'Cherries', 29],
+ ['North', 'Grapes', 27],
+ ['North', 'Peaches', 25],
+ ['South', 'Apples', 38],
+ ['South', 'Cherries', 28],
+ ['South', 'Oranges', 36],
+ ['South', 'Pears', 40],
+ ['West', 'Apples', 30],
+ ['West', 'Lemons', 34],
+ ['West', 'Oranges', 25],
+ ],
+ $this->sampleDataForMultiRow(),
+ [1, 2],
+ ],
+ [
+ [
+ ['East', 'Grapes', 31],
+ ['East', 'Lemons', 36],
+ ['North', 'Peaches', 25],
+ ['North', 'Grapes', 27],
+ ['North', 'Cherries', 29],
+ ['South', 'Cherries', 28],
+ ['South', 'Oranges', 36],
+ ['South', 'Apples', 38],
+ ['South', 'Pears', 40],
+ ['West', 'Oranges', 25],
+ ['West', 'Apples', 30],
+ ['West', 'Lemons', 34],
+ ],
+ $this->sampleDataForMultiRow(),
+ [1, 3],
+ ],
+ [
+ [
+ ['West', 'Apples', 30],
+ ['South', 'Apples', 38],
+ ['South', 'Cherries', 28],
+ ['North', 'Cherries', 29],
+ ['North', 'Grapes', 27],
+ ['East', 'Grapes', 31],
+ ['West', 'Lemons', 34],
+ ['East', 'Lemons', 36],
+ ['West', 'Oranges', 25],
+ ['South', 'Oranges', 36],
+ ['North', 'Peaches', 25],
+ ['South', 'Pears', 40],
+ ],
+ $this->sampleDataForMultiRow(),
+ [2, 3],
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider providerSortByColumn
+ */
+ public function testSortByColumn(array $expectedResult, array $matrix, int $sortIndex, int $sortOrder): void
+ {
+ $result = Sort::sort($matrix, $sortIndex, $sortOrder, true);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function providerSortByColumn(): array
+ {
+ return [
+ [
+ [[142, 378, 404, 445, 483, 622, 650, 691, 783, 961]],
+ $this->sampleDataForColumn(),
+ 1,
+ Sort::ORDER_ASCENDING,
+ ],
+ [
+ [[961, 783, 691, 650, 622, 483, 445, 404, 378, 142]],
+ $this->sampleDataForColumn(),
+ 1,
+ Sort::ORDER_DESCENDING,
+ ],
+ ];
+ }
+
+ public function sampleDataForRow(): array
+ {
+ return [
+ [622], [961], [691], [445], [378], [483], [650], [783], [142], [404],
+ ];
+ }
+
+ public function sampleDataForMultiRow(): array
+ {
+ return [
+ ['South', 'Pears', 40],
+ ['South', 'Apples', 38],
+ ['South', 'Oranges', 36],
+ ['East', 'Lemons', 36],
+ ['West', 'Lemons', 34],
+ ['East', 'Grapes', 31],
+ ['West', 'Apples', 30],
+ ['North', 'Cherries', 29],
+ ['South', 'Cherries', 28],
+ ['North', 'Grapes', 27],
+ ['North', 'Peaches', 25],
+ ['West', 'Oranges', 25],
+ ];
+ }
+
+ public function sampleDataForColumn(): array
+ {
+ return [
+ [622, 961, 691, 445, 378, 483, 650, 783, 142, 404],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/UniqueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/UniqueTest.php
new file mode 100644
index 00000000..6232505d
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/UniqueTest.php
@@ -0,0 +1,157 @@
+createMock(StreamInterface::class);
$body->expects(self::atMost(1))->method('getContents')->willReturn($responseData[1]);
diff --git a/tests/PhpSpreadsheetTests/Calculation/FunctionsTest.php b/tests/PhpSpreadsheetTests/Calculation/FunctionsTest.php
index c12bf060..9ea62400 100644
--- a/tests/PhpSpreadsheetTests/Calculation/FunctionsTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/FunctionsTest.php
@@ -76,12 +76,10 @@ class FunctionsTest extends TestCase
/**
* @dataProvider providerIfCondition
- *
- * @param mixed $expectedResult
*/
- public function testIfCondition($expectedResult, ...$args): void
+ public function testIfCondition(string $expectedResult, string $args): void
{
- $result = Functions::ifCondition(...$args);
+ $result = Functions::ifCondition($args);
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Cell/AddressHelperTest.php b/tests/PhpSpreadsheetTests/Cell/AddressHelperTest.php
index 58c696ef..f82a4ead 100644
--- a/tests/PhpSpreadsheetTests/Cell/AddressHelperTest.php
+++ b/tests/PhpSpreadsheetTests/Cell/AddressHelperTest.php
@@ -32,15 +32,17 @@ class AddressHelperTest extends TestCase
?int $row = null,
?int $column = null
): void {
- $args = [];
- if ($row !== null) {
- $args[] = $row;
+ if ($row === null) {
+ if ($column === null) {
+ $actualValue = AddressHelper::convertToA1($address);
+ } else {
+ $actualValue = AddressHelper::convertToA1($address, $column);
+ }
+ } elseif ($column === null) {
+ $actualValue = AddressHelper::convertToA1($address, $row);
+ } else {
+ $actualValue = AddressHelper::convertToA1($address, $row, $column);
}
- if ($column !== null) {
- $args[] = $column;
- }
-
- $actualValue = AddressHelper::convertToA1($address, ...$args);
self::assertSame($expectedValue, $actualValue);
}
diff --git a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
index 9a0a2c21..8ed23427 100644
--- a/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
+++ b/tests/PhpSpreadsheetTests/Cell/CoordinateTest.php
@@ -265,7 +265,7 @@ class CoordinateTest extends TestCase
$cellRange = null;
// @phpstan-ignore-next-line
- Coordinate::buildRange($cellRange);
+ Coordinate::buildRange(/** @scrutinizer ignore-type */ $cellRange);
}
public function testBuildRangeInvalid2(): void
diff --git a/tests/PhpSpreadsheetTests/CellReferenceHelperTest.php b/tests/PhpSpreadsheetTests/CellReferenceHelperTest.php
new file mode 100644
index 00000000..35352afd
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/CellReferenceHelperTest.php
@@ -0,0 +1,193 @@
+updateCellReference($cellAddress);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperInsertColumnsProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['D5', 'D5'],
+ ['G5', 'E5'],
+ ['$E5', '$E5'],
+ ['G$5', 'E$5'],
+ ['I5', 'G5'],
+ ['$G$5', '$G$5'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperDeleteColumnsProvider
+ */
+ public function testCellReferenceHelperDeleteColumns(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', -2, 0);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperDeleteColumnsProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['D5', 'D5'],
+ ['C5', 'E5'],
+ ['$E5', '$E5'],
+ ['C$5', 'E$5'],
+ ['E5', 'G5'],
+ ['$G$5', '$G$5'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperInsertRowsProvider
+ */
+ public function testCellReferenceHelperInsertRows(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', 0, 2);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperInsertRowsProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['E4', 'E4'],
+ ['E7', 'E5'],
+ ['E$5', 'E$5'],
+ ['$E7', '$E5'],
+ ['E11', 'E9'],
+ ['$E$9', '$E$9'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperDeleteRowsProvider
+ */
+ public function testCellReferenceHelperDeleteRows(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', 0, -2);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperDeleteRowsProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['E4', 'E4'],
+ ['E3', 'E5'],
+ ['E$5', 'E$5'],
+ ['$E3', '$E5'],
+ ['E7', 'E9'],
+ ['$E$9', '$E$9'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperInsertColumnsAbsoluteProvider
+ */
+ public function testCellReferenceHelperInsertColumnsAbsolute(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', 2, 0);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress, true);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperInsertColumnsAbsoluteProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['D5', 'D5'],
+ ['G5', 'E5'],
+ ['$G5', '$E5'],
+ ['G$5', 'E$5'],
+ ['I5', 'G5'],
+ ['$I$5', '$G$5'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperDeleteColumnsAbsoluteProvider
+ */
+ public function testCellReferenceHelperDeleteColumnsAbsolute(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', -2, 0);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress, true);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperDeleteColumnsAbsoluteProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['D5', 'D5'],
+ ['C5', 'E5'],
+ ['$C5', '$E5'],
+ ['C$5', 'E$5'],
+ ['E5', 'G5'],
+ ['$E$5', '$G$5'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperInsertRowsAbsoluteProvider
+ */
+ public function testCellReferenceHelperInsertRowsAbsolute(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', 0, 2);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress, true);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperInsertRowsAbsoluteProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['E4', 'E4'],
+ ['E7', 'E5'],
+ ['E$7', 'E$5'],
+ ['$E7', '$E5'],
+ ['E11', 'E9'],
+ ['$E$11', '$E$9'],
+ ];
+ }
+
+ /**
+ * @dataProvider cellReferenceHelperDeleteRowsAbsoluteProvider
+ */
+ public function testCellReferenceHelperDeleteRowsAbsolute(string $expectedResult, string $cellAddress): void
+ {
+ $cellReferenceHelper = new CellReferenceHelper('E5', 0, -2);
+ $result = $cellReferenceHelper->updateCellReference($cellAddress, true);
+ self::assertSame($expectedResult, $result);
+ }
+
+ public function cellReferenceHelperDeleteRowsAbsoluteProvider(): array
+ {
+ return [
+ ['A1', 'A1'],
+ ['E4', 'E4'],
+ ['E3', 'E5'],
+ ['E$3', 'E$5'],
+ ['$E3', '$E5'],
+ ['E7', 'E9'],
+ ['$E$7', '$E$9'],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Document/PropertiesTest.php b/tests/PhpSpreadsheetTests/Document/PropertiesTest.php
index c539da09..e6c95cd4 100644
--- a/tests/PhpSpreadsheetTests/Document/PropertiesTest.php
+++ b/tests/PhpSpreadsheetTests/Document/PropertiesTest.php
@@ -155,11 +155,17 @@ class PropertiesTest extends TestCase
*
* @param mixed $expectedType
* @param mixed $expectedValue
- * @param mixed $propertyName
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @param ?string $propertyType
*/
- public function testSetCustomProperties($expectedType, $expectedValue, $propertyName, ...$args): void
+ public function testSetCustomProperties($expectedType, $expectedValue, $propertyName, $propertyValue, $propertyType = null): void
{
- $this->properties->setCustomProperty($propertyName, ...$args);
+ if ($propertyType === null) {
+ $this->properties->setCustomProperty($propertyName, $propertyValue);
+ } else {
+ $this->properties->setCustomProperty($propertyName, $propertyValue, $propertyType);
+ }
self::assertTrue($this->properties->isCustomPropertySet($propertyName));
self::assertSame($expectedType, $this->properties->getCustomPropertyType($propertyName));
$result = $this->properties->getCustomPropertyValue($propertyName);
diff --git a/tests/PhpSpreadsheetTests/IOFactoryTest.php b/tests/PhpSpreadsheetTests/IOFactoryTest.php
index 958c6eb0..b516a9f4 100644
--- a/tests/PhpSpreadsheetTests/IOFactoryTest.php
+++ b/tests/PhpSpreadsheetTests/IOFactoryTest.php
@@ -81,39 +81,13 @@ class IOFactoryTest extends TestCase
/**
* @dataProvider providerIdentify
- *
- * @param string $file
- * @param string $expectedName
- * @param string $expectedClass
*/
- public function testIdentify($file, $expectedName, $expectedClass): void
+ public function testIdentifyCreateLoad(string $file, string $expectedName, string $expectedClass): void
{
$actual = IOFactory::identify($file);
self::assertSame($expectedName, $actual);
- }
-
- /**
- * @dataProvider providerIdentify
- *
- * @param string $file
- * @param string $expectedName
- * @param string $expectedClass
- */
- public function testCreateReaderForFile($file, $expectedName, $expectedClass): void
- {
$actual = IOFactory::createReaderForFile($file);
self::assertSame($expectedClass, get_class($actual));
- }
-
- /**
- * @dataProvider providerIdentify
- *
- * @param string $file
- * @param string $expectedName
- * @param string $expectedClass
- */
- public function testLoad($file, $expectedName, $expectedClass): void
- {
$actual = IOFactory::load($file);
self::assertInstanceOf(Spreadsheet::class, $actual);
}
diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php
new file mode 100644
index 00000000..ce224864
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php
@@ -0,0 +1,158 @@
+load($filename);
+ $this->sheet = $spreadsheet->getActiveSheet();
+ }
+
+ /**
+ * @dataProvider conditionalFormattingProvider
+ */
+ public function testReadConditionalFormatting(string $expectedRange, array $expectedRules): void
+ {
+ $hasConditionalStyles = $this->sheet->conditionalStylesExists($expectedRange);
+ self::assertTrue($hasConditionalStyles);
+
+ $conditionalStyles = $this->sheet->getConditionalStyles($expectedRange);
+
+ foreach ($conditionalStyles as $index => $conditionalStyle) {
+ self::assertSame($expectedRules[$index]['type'], $conditionalStyle->getConditionType());
+ self::assertSame($expectedRules[$index]['operator'], $conditionalStyle->getOperatorType());
+ self::assertSame($expectedRules[$index]['conditions'], $conditionalStyle->getConditions());
+ }
+ }
+
+ public function conditionalFormattingProvider(): array
+ {
+ return [
+ [
+ 'A2:E5',
+ [
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_EQUAL,
+ 'conditions' => [
+ 0,
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_GREATERTHAN,
+ 'conditions' => [
+ 0,
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_LESSTHAN,
+ 'conditions' => [
+ 0,
+ ],
+ ],
+ ],
+ ],
+ [
+ 'A10:E13',
+ [
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_EQUAL,
+ 'conditions' => [
+ '$H$9',
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_GREATERTHAN,
+ 'conditions' => [
+ '$H$9',
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_LESSTHAN,
+ 'conditions' => [
+ '$H$9',
+ ],
+ ],
+ ],
+ ],
+ [
+ 'A18:A20',
+ [
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_BETWEEN,
+ 'conditions' => [
+ '$B1',
+ '$C1',
+ ],
+ ],
+ ],
+ ],
+ [
+ 'A24:E27',
+ [
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_BETWEEN,
+ 'conditions' => [
+ 'AVERAGE($A$24:$E$27)-STDEV($A$24:$E$27)',
+ 'AVERAGE($A$24:$E$27)+STDEV($A$24:$E$27)',
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_GREATERTHAN,
+ 'conditions' => [
+ 'AVERAGE($A$24:$E$27)+STDEV($A$24:$E$27)',
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_LESSTHAN,
+ 'conditions' => [
+ 'AVERAGE($A$24:$E$27)-STDEV($A$24:$E$27)',
+ ],
+ ],
+ ],
+ ],
+ [
+ 'A31:A33',
+ [
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_EQUAL,
+ 'conditions' => [
+ '"LOVE"',
+ ],
+ ],
+ [
+ 'type' => Conditional::CONDITION_CELLIS,
+ 'operator' => Conditional::OPERATOR_EQUAL,
+ 'conditions' => [
+ '"PHP"',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php
new file mode 100644
index 00000000..6cc566c1
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php
@@ -0,0 +1,71 @@
+load($filename);
+ $this->sheet = $spreadsheet->getActiveSheet();
+ }
+
+ /**
+ * @dataProvider conditionalFormattingProvider
+ */
+ public function testReadConditionalFormatting(string $expectedRange, array $expectedRule): void
+ {
+ $hasConditionalStyles = $this->sheet->conditionalStylesExists($expectedRange);
+ self::assertTrue($hasConditionalStyles);
+
+ $conditionalStyles = $this->sheet->getConditionalStyles($expectedRange);
+
+ foreach ($conditionalStyles as $index => $conditionalStyle) {
+ self::assertSame($expectedRule[$index]['type'], $conditionalStyle->getConditionType());
+ self::assertSame($expectedRule[$index]['operator'], $conditionalStyle->getOperatorType());
+ self::assertSame($expectedRule[$index]['conditions'], $conditionalStyle->getConditions());
+ }
+ }
+
+ public function conditionalFormattingProvider(): array
+ {
+ return [
+ [
+ 'A3:D8',
+ [
+ [
+ 'type' => Conditional::CONDITION_EXPRESSION,
+ 'operator' => Conditional::OPERATOR_NONE,
+ 'conditions' => [
+ '$C1="USA"',
+ ],
+ ],
+ ],
+ ],
+ [
+ 'A13:D18',
+ [
+ [
+ 'type' => Conditional::CONDITION_EXPRESSION,
+ 'operator' => Conditional::OPERATOR_NONE,
+ 'conditions' => [
+ 'AND($C1="USA",$D1="Q4")',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php
new file mode 100644
index 00000000..bdefa17e
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php
@@ -0,0 +1,60 @@
+load($filename);
+ $this->sheet = $spreadsheet->getActiveSheet();
+ }
+
+ /**
+ * @dataProvider dataValidationProvider
+ */
+ public function testDataValidation(string $expectedRange, array $expectedRule): void
+ {
+ $hasDataValidation = $this->sheet->dataValidationExists($expectedRange);
+ self::assertTrue($hasDataValidation);
+
+ $dataValidation = $this->sheet->getDataValidation($expectedRange);
+ self::assertSame($expectedRule['type'], $dataValidation->getType());
+ self::assertSame($expectedRule['operator'], $dataValidation->getOperator());
+ self::assertSame($expectedRule['formula'], $dataValidation->getFormula1());
+ }
+
+ public function dataValidationProvider(): array
+ {
+ return [
+ [
+ 'B2',
+ [
+ 'type' => DataValidation::TYPE_WHOLE,
+ 'operator' => DataValidation::OPERATOR_GREATERTHANOREQUAL,
+ 'formula' => '18',
+ ],
+ ],
+ [
+ 'B3',
+ [
+ 'type' => DataValidation::TYPE_LIST,
+ 'operator' => DataValidation::OPERATOR_BETWEEN,
+ 'formula' => '"Blocked,Pending,Approved"',
+ ],
+ ],
+ ];
+ }
+}
diff --git a/tests/PhpSpreadsheetTests/ReferenceHelperTest.php b/tests/PhpSpreadsheetTests/ReferenceHelperTest.php
index 2c016b0b..2640d80c 100644
--- a/tests/PhpSpreadsheetTests/ReferenceHelperTest.php
+++ b/tests/PhpSpreadsheetTests/ReferenceHelperTest.php
@@ -3,8 +3,12 @@
namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class ReferenceHelperTest extends TestCase
@@ -177,4 +181,357 @@ class ReferenceHelperTest extends TestCase
self::assertNull($cells[1][1]);
self::assertArrayNotHasKey(2, $cells[1]);
}
+
+ public function testInsertRowsWithPageBreaks(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->setBreak('A2', Worksheet::BREAK_ROW);
+ $sheet->setBreak('A5', Worksheet::BREAK_ROW);
+
+ $sheet->insertNewRowBefore(2, 2);
+
+ $breaks = $sheet->getBreaks();
+ ksort($breaks);
+ self::assertSame(['A4' => Worksheet::BREAK_ROW, 'A7' => Worksheet::BREAK_ROW], $breaks);
+ }
+
+ public function testDeleteRowsWithPageBreaks(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->setBreak('A2', Worksheet::BREAK_ROW);
+ $sheet->setBreak('A5', Worksheet::BREAK_ROW);
+
+ $sheet->removeRow(2, 2);
+
+ $breaks = $sheet->getBreaks();
+ self::assertSame(['A3' => Worksheet::BREAK_ROW], $breaks);
+ }
+
+ public function testInsertRowsWithComments(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->getComment('A2')->getText()->createText('First Comment');
+ $sheet->getComment('A5')->getText()->createText('Second Comment');
+
+ $sheet->insertNewRowBefore(2, 2);
+
+ $comments = array_map(
+ function (Comment $value) {
+ return $value->getText()->getPlainText();
+ },
+ $sheet->getComments()
+ );
+
+ self::assertSame(['A4' => 'First Comment', 'A7' => 'Second Comment'], $comments);
+ }
+
+ public function testDeleteRowsWithComments(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->getComment('A2')->getText()->createText('First Comment');
+ $sheet->getComment('A5')->getText()->createText('Second Comment');
+
+ $sheet->removeRow(2, 2);
+
+ $comments = array_map(
+ function (Comment $value) {
+ return $value->getText()->getPlainText();
+ },
+ $sheet->getComments()
+ );
+
+ self::assertSame(['A3' => 'Second Comment'], $comments);
+ }
+
+ public function testInsertRowsWithHyperlinks(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->getCell('A2')->getHyperlink()->setUrl('https://github.com/PHPOffice/PhpSpreadsheet');
+ $sheet->getCell('A5')->getHyperlink()->setUrl('https://phpspreadsheet.readthedocs.io/en/latest/');
+
+ $sheet->insertNewRowBefore(2, 2);
+
+ $hyperlinks = array_map(
+ function (Hyperlink $value) {
+ return $value->getUrl();
+ },
+ $sheet->getHyperlinkCollection()
+ );
+ ksort($hyperlinks);
+
+ self::assertSame(
+ [
+ 'A4' => 'https://github.com/PHPOffice/PhpSpreadsheet',
+ 'A7' => 'https://phpspreadsheet.readthedocs.io/en/latest/',
+ ],
+ $hyperlinks
+ );
+ }
+
+ public function testDeleteRowsWithHyperlinks(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]], null, 'A1', true);
+ $sheet->getCell('A2')->getHyperlink()->setUrl('https://github.com/PHPOffice/PhpSpreadsheet');
+ $sheet->getCell('A5')->getHyperlink()->setUrl('https://phpspreadsheet.readthedocs.io/en/latest/');
+
+ $sheet->removeRow(2, 2);
+
+ $hyperlinks = array_map(
+ function (Hyperlink $value) {
+ return $value->getUrl();
+ },
+ $sheet->getHyperlinkCollection()
+ );
+
+ self::assertSame(['A3' => 'https://phpspreadsheet.readthedocs.io/en/latest/'], $hyperlinks);
+ }
+
+ public function testInsertRowsWithDataValidation(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+
+ $sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
+ $cellAddress = 'E5';
+ $this->setDataValidation($sheet, $cellAddress);
+
+ $sheet->insertNewRowBefore(2, 2);
+
+ self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
+ self::assertTrue($sheet->getCell('E7')->hasDataValidation());
+ }
+
+ public function testDeleteRowsWithDataValidation(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+
+ $sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
+ $cellAddress = 'E5';
+ $this->setDataValidation($sheet, $cellAddress);
+
+ $sheet->removeRow(2, 2);
+
+ self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
+ self::assertTrue($sheet->getCell('E3')->hasDataValidation());
+ }
+
+ public function testDeleteColumnsWithDataValidation(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+
+ $sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
+ $cellAddress = 'E5';
+ $this->setDataValidation($sheet, $cellAddress);
+
+ $sheet->removeColumn('B', 2);
+
+ self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
+ self::assertTrue($sheet->getCell('C5')->hasDataValidation());
+ }
+
+ public function testInsertColumnsWithDataValidation(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+
+ $sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
+ $cellAddress = 'E5';
+ $this->setDataValidation($sheet, $cellAddress);
+
+ $sheet->insertNewColumnBefore('C', 2);
+
+ self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
+ self::assertTrue($sheet->getCell('G5')->hasDataValidation());
+ }
+
+ private function setDataValidation(Worksheet $sheet, string $cellAddress): void
+ {
+ $validation = $sheet->getCell($cellAddress)
+ ->getDataValidation();
+ $validation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST);
+ $validation->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION);
+ $validation->setAllowBlank(false);
+ $validation->setShowInputMessage(true);
+ $validation->setShowErrorMessage(true);
+ $validation->setShowDropDown(true);
+ $validation->setErrorTitle('Input error');
+ $validation->setError('Value is not in list.');
+ $validation->setPromptTitle('Pick from list');
+ $validation->setPrompt('Please pick a value from the drop-down list.');
+ $validation->setFormula1('$A5:$A8');
+ }
+
+ public function testInsertRowsWithConditionalFormatting(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
+ $sheet->getCell('H5')->setValue(5);
+
+ $cellRange = 'C3:F7';
+ $this->setConditionalFormatting($sheet, $cellRange);
+
+ $sheet->insertNewRowBefore(4, 2);
+
+ $styles = $sheet->getConditionalStylesCollection();
+ // verify that the conditional range has been updated
+ self::assertSame('C3:F9', array_keys($styles)[0]);
+ // verify that the conditions have been updated
+ foreach ($styles as $style) {
+ foreach ($style as $conditions) {
+ self::assertSame('$H$7', $conditions->getConditions()[0]);
+ }
+ }
+ }
+
+ public function testInsertColumnssWithConditionalFormatting(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
+ $sheet->getCell('H5')->setValue(5);
+
+ $cellRange = 'C3:F7';
+ $this->setConditionalFormatting($sheet, $cellRange);
+
+ $sheet->insertNewColumnBefore('C', 2);
+
+ $styles = $sheet->getConditionalStylesCollection();
+ // verify that the conditional range has been updated
+ self::assertSame('E3:H7', array_keys($styles)[0]);
+ // verify that the conditions have been updated
+ foreach ($styles as $style) {
+ foreach ($style as $conditions) {
+ self::assertSame('$J$5', $conditions->getConditions()[0]);
+ }
+ }
+ }
+
+ public function testDeleteRowsWithConditionalFormatting(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
+ $sheet->getCell('H5')->setValue(5);
+
+ $cellRange = 'C3:F7';
+ $this->setConditionalFormatting($sheet, $cellRange);
+
+ $sheet->removeRow(4, 2);
+
+ $styles = $sheet->getConditionalStylesCollection();
+ // verify that the conditional range has been updated
+ self::assertSame('C3:F5', array_keys($styles)[0]);
+ // verify that the conditions have been updated
+ foreach ($styles as $style) {
+ foreach ($style as $conditions) {
+ self::assertSame('$H$5', $conditions->getConditions()[0]);
+ }
+ }
+ }
+
+ public function testDeleteColumnsWithConditionalFormatting(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
+ $sheet->getCell('H5')->setValue(5);
+
+ $cellRange = 'C3:F7';
+ $this->setConditionalFormatting($sheet, $cellRange);
+
+ $sheet->removeColumn('D', 2);
+
+ $styles = $sheet->getConditionalStylesCollection();
+ // verify that the conditional range has been updated
+ self::assertSame('C3:D7', array_keys($styles)[0]);
+ // verify that the conditions have been updated
+ foreach ($styles as $style) {
+ foreach ($style as $conditions) {
+ self::assertSame('$F$5', $conditions->getConditions()[0]);
+ }
+ }
+ }
+
+ private function setConditionalFormatting(Worksheet $sheet, string $cellRange): void
+ {
+ $conditionalStyles = [];
+ $wizardFactory = new Wizard($cellRange);
+ /** @var Wizard\CellValue $cellWizard */
+ $cellWizard = $wizardFactory->newRule(Wizard::CELL_VALUE);
+
+ $cellWizard->equals('$H$5', Wizard::VALUE_TYPE_CELL);
+ $conditionalStyles[] = $cellWizard->getConditional();
+
+ $cellWizard->greaterThan('$H$5', Wizard::VALUE_TYPE_CELL);
+ $conditionalStyles[] = $cellWizard->getConditional();
+
+ $cellWizard->lessThan('$H$5', Wizard::VALUE_TYPE_CELL);
+ $conditionalStyles[] = $cellWizard->getConditional();
+
+ $sheet->getStyle($cellWizard->getCellRange())
+ ->setConditionalStyles($conditionalStyles);
+ }
+
+ public function testInsertRowsWithPrintArea(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->getPageSetup()->setPrintArea('A1:J10');
+
+ $sheet->insertNewRowBefore(2, 2);
+
+ $printArea = $sheet->getPageSetup()->getPrintArea();
+ self::assertSame('A1:J12', $printArea);
+ }
+
+ public function testInsertColumnsWithPrintArea(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->getPageSetup()->setPrintArea('A1:J10');
+
+ $sheet->insertNewColumnBefore('B', 2);
+
+ $printArea = $sheet->getPageSetup()->getPrintArea();
+ self::assertSame('A1:L10', $printArea);
+ }
+
+ public function testDeleteRowsWithPrintArea(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->getPageSetup()->setPrintArea('A1:J10');
+
+ $sheet->removeRow(2, 2);
+
+ $printArea = $sheet->getPageSetup()->getPrintArea();
+ self::assertSame('A1:J8', $printArea);
+ }
+
+ public function testDeleteColumnsWithPrintArea(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->getPageSetup()->setPrintArea('A1:J10');
+
+ $sheet->removeColumn('B', 2);
+
+ $printArea = $sheet->getPageSetup()->getPrintArea();
+ self::assertSame('A1:H10', $printArea);
+ }
}
diff --git a/tests/PhpSpreadsheetTests/Shared/DateTest.php b/tests/PhpSpreadsheetTests/Shared/DateTest.php
index 9fb4d919..90ed5cc2 100644
--- a/tests/PhpSpreadsheetTests/Shared/DateTest.php
+++ b/tests/PhpSpreadsheetTests/Shared/DateTest.php
@@ -176,9 +176,9 @@ class DateTest extends TestCase
*
* @param mixed $expectedResult
*/
- public function testIsDateTimeFormatCode($expectedResult, ...$args): void
+ public function testIsDateTimeFormatCode($expectedResult, string $format): void
{
- $result = Date::isDateTimeFormatCode(...$args);
+ $result = Date::isDateTimeFormatCode($format);
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Style/ColorTest.php b/tests/PhpSpreadsheetTests/Style/ColorTest.php
index 6575c850..9767c537 100644
--- a/tests/PhpSpreadsheetTests/Style/ColorTest.php
+++ b/tests/PhpSpreadsheetTests/Style/ColorTest.php
@@ -78,11 +78,14 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetRed
*
* @param mixed $expectedResult
- * @param mixed $color
*/
- public function testGetRed($expectedResult, $color, ...$args): void
+ public function testGetRed($expectedResult, string $color, ?bool $bool = null): void
{
- $result = Color::getRed($color, ...$args);
+ if ($bool === null) {
+ $result = Color::getRed($color);
+ } else {
+ $result = Color::getRed($color, $bool);
+ }
self::assertEquals($expectedResult, $result);
}
@@ -95,11 +98,14 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetGreen
*
* @param mixed $expectedResult
- * @param mixed $color
*/
- public function testGetGreen($expectedResult, $color, ...$args): void
+ public function testGetGreen($expectedResult, string $color, ?bool $bool = null): void
{
- $result = Color::getGreen($color, ...$args);
+ if ($bool === null) {
+ $result = Color::getGreen($color);
+ } else {
+ $result = Color::getGreen($color, $bool);
+ }
self::assertEquals($expectedResult, $result);
}
@@ -112,11 +118,14 @@ class ColorTest extends TestCase
* @dataProvider providerColorGetBlue
*
* @param mixed $expectedResult
- * @param mixed $color
*/
- public function testGetBlue($expectedResult, $color, ...$args): void
+ public function testGetBlue($expectedResult, string $color, ?bool $bool = null): void
{
- $result = Color::getBlue($color, ...$args);
+ if ($bool === null) {
+ $result = Color::getBlue($color);
+ } else {
+ $result = Color::getBlue($color, $bool);
+ }
self::assertEquals($expectedResult, $result);
}
diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php
index 28bbbb87..a80d3d63 100644
--- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php
+++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterTest.php
@@ -134,6 +134,64 @@ class AutoFilterTest extends SetupTeardown
}
}
+ public function testRemoveColumns(): void
+ {
+ $sheet = $this->getSheet();
+ $sheet->fromArray(range('H', 'O'), null, 'H2');
+ $autoFilter = $sheet->getAutoFilter();
+ $autoFilter->setRange(self::INITIAL_RANGE);
+ $autoFilter->getColumn('L')->addRule((new Column\Rule())->setValue(5));
+
+ $sheet->removeColumn('K', 2);
+ $result = $autoFilter->getRange();
+ self::assertEquals('H2:M256', $result);
+
+ // Check that the rule that was set for column L is no longer set
+ self::assertEmpty($autoFilter->getColumn('L')->getRule(0)->getValue());
+ }
+
+ public function testRemoveRows(): void
+ {
+ $sheet = $this->getSheet();
+ $sheet->fromArray(range('H', 'O'), null, 'H2');
+ $autoFilter = $sheet->getAutoFilter();
+ $autoFilter->setRange(self::INITIAL_RANGE);
+
+ $sheet->removeRow(42, 128);
+ $result = $autoFilter->getRange();
+ self::assertEquals('H2:O128', $result);
+ }
+
+ public function testInsertColumns(): void
+ {
+ $sheet = $this->getSheet();
+ $sheet->fromArray(range('H', 'O'), null, 'H2');
+ $autoFilter = $sheet->getAutoFilter();
+ $autoFilter->setRange(self::INITIAL_RANGE);
+ $autoFilter->getColumn('N')->addRule((new Column\Rule())->setValue(5));
+
+ $sheet->insertNewColumnBefore('N', 3);
+ $result = $autoFilter->getRange();
+ self::assertEquals('H2:R256', $result);
+
+ // Check that column N no longer has a rule set
+ self::assertEmpty($autoFilter->getColumn('N')->getRule(0)->getValue());
+ // Check that the rule originally set in column N has been moved to column Q
+ self::assertSame(5, $autoFilter->getColumn('Q')->getRule(0)->getValue());
+ }
+
+ public function testInsertRows(): void
+ {
+ $sheet = $this->getSheet();
+ $sheet->fromArray(range('H', 'O'), null, 'H2');
+ $autoFilter = $sheet->getAutoFilter();
+ $autoFilter->setRange(self::INITIAL_RANGE);
+
+ $sheet->insertNewRowBefore(3, 4);
+ $result = $autoFilter->getRange();
+ self::assertEquals('H2:O260', $result);
+ }
+
public function testGetInvalidColumnOffset(): void
{
$this->expectException(PhpSpreadsheetException::class);
diff --git a/tests/PhpSpreadsheetTests/Worksheet/ColumnCellIteratorTest.php b/tests/PhpSpreadsheetTests/Worksheet/ColumnCellIteratorTest.php
index a81058af..b9e18e43 100644
--- a/tests/PhpSpreadsheetTests/Worksheet/ColumnCellIteratorTest.php
+++ b/tests/PhpSpreadsheetTests/Worksheet/ColumnCellIteratorTest.php
@@ -44,7 +44,7 @@ class ColumnCellIteratorTest extends TestCase
self::assertEquals($ColumnCellIndexResult++, $key);
self::assertInstanceOf(Cell::class, $ColumnCell);
}
- $transposed = array_map(null, ...self::CELL_VALUES);
+ $transposed = array_map(/** @scrutinizer ignore-type */ null, ...self::CELL_VALUES);
self::assertSame($transposed[0], $values);
$spreadsheet->disconnectWorksheets();
}
diff --git a/tests/PhpSpreadsheetTests/Worksheet/DrawingTest.php b/tests/PhpSpreadsheetTests/Worksheet/DrawingTest.php
index d18ff002..17e17e19 100644
--- a/tests/PhpSpreadsheetTests/Worksheet/DrawingTest.php
+++ b/tests/PhpSpreadsheetTests/Worksheet/DrawingTest.php
@@ -2,7 +2,10 @@
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
+use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PHPUnit\Framework\TestCase;
@@ -41,4 +44,76 @@ class DrawingTest extends TestCase
$spreadsheet->disconnectWorksheets();
}
}
+
+ public function testChangeWorksheet(): void
+ {
+ $spreadsheet = new Spreadsheet();
+ $sheet1 = $spreadsheet->getActiveSheet();
+ $sheet2 = $spreadsheet->createSheet();
+
+ $drawing = new Drawing();
+ $drawing->setName('Green Square');
+ $drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
+ self::assertEquals($drawing->getWidth(), 150);
+ self::assertEquals($drawing->getHeight(), 150);
+ $drawing->setCoordinates('A1');
+ $drawing->setOffsetX(30);
+ $drawing->setOffsetY(10);
+ $drawing->setWorksheet($sheet1);
+
+ try {
+ $drawing->setWorksheet($sheet2);
+ self::fail('Should throw exception when attempting set worksheet without specifying override');
+ } catch (PhpSpreadsheetException $e) {
+ self::assertStringContainsString('A Worksheet has already been assigned.', $e->getMessage());
+ }
+ self::assertSame($sheet1, $drawing->getWorksheet());
+ self::assertCount(1, $sheet1->getDrawingCollection());
+ self::assertCount(0, $sheet2->getDrawingCollection());
+ $drawing->setWorksheet($sheet2, true);
+ self::assertSame($sheet2, $drawing->getWorksheet());
+ self::assertCount(0, $sheet1->getDrawingCollection());
+ self::assertCount(1, $sheet2->getDrawingCollection());
+ }
+
+ public function testHeaderFooter(): void
+ {
+ $drawing1 = new HeaderFooterDrawing();
+ $drawing1->setName('Blue Square');
+ $drawing1->setPath('tests/data/Writer/XLSX/blue_square.png');
+ self::assertEquals($drawing1->getWidth(), 100);
+ self::assertEquals($drawing1->getHeight(), 100);
+ $drawing2 = new HeaderFooterDrawing();
+ $drawing2->setName('Blue Square');
+ $drawing2->setPath('tests/data/Writer/XLSX/blue_square.png');
+ self::assertSame($drawing1->getHashCode(), $drawing2->getHashCode());
+ $drawing2->setOffsetX(100);
+ self::assertNotEquals($drawing1->getHashCode(), $drawing2->getHashCode());
+ }
+
+ public function testSetWidthAndHeight(): void
+ {
+ $drawing = new Drawing();
+ $drawing->setName('Blue Square');
+ $drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
+ self::assertSame(100, $drawing->getWidth());
+ self::assertSame(100, $drawing->getHeight());
+ self::assertTrue($drawing->getResizeProportional());
+ $drawing->setResizeProportional(false);
+ $drawing->setWidthAndHeight(150, 200);
+ self::assertSame(150, $drawing->getWidth());
+ self::assertSame(200, $drawing->getHeight());
+ $drawing->setResizeProportional(true);
+ $drawing->setWidthAndHeight(300, 250);
+ // width increase% more than height, so scale width
+ self::assertSame(188, $drawing->getWidth());
+ self::assertSame(250, $drawing->getHeight());
+ $drawing->setResizeProportional(false);
+ $drawing->setWidthAndHeight(150, 200);
+ $drawing->setResizeProportional(true);
+ // height increase% more than width, so scale height
+ $drawing->setWidthAndHeight(175, 350);
+ self::assertSame(175, $drawing->getWidth());
+ self::assertSame(234, $drawing->getHeight());
+ }
}
diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php
index f9df2b1c..ccbced32 100644
--- a/tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php
+++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/DrawingsTest.php
@@ -8,7 +8,9 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class DrawingsTest extends AbstractFunctional
@@ -28,6 +30,8 @@ class DrawingsTest extends AbstractFunctional
// Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet);
+ $spreadsheet->disconnectWorksheets();
+ $reloadedSpreadsheet->disconnectWorksheets();
}
/**
@@ -59,6 +63,8 @@ class DrawingsTest extends AbstractFunctional
// Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet);
+ $spreadsheet->disconnectWorksheets();
+ $reloadedSpreadsheet->disconnectWorksheets();
}
/**
@@ -96,6 +102,8 @@ class DrawingsTest extends AbstractFunctional
unlink($tempFileName);
self::assertNotNull($reloadedSpreadsheet);
+ $spreadsheet->disconnectWorksheets();
+ $reloadedSpreadsheet->disconnectWorksheets();
}
/**
@@ -173,7 +181,8 @@ class DrawingsTest extends AbstractFunctional
self::assertEquals($comment->getBackgroundImage()->getType(), IMAGETYPE_PNG);
unlink($tempFileName);
- self::assertNotNull($reloadedSpreadsheet);
+ $spreadsheet->disconnectWorksheets();
+ $reloadedSpreadsheet->disconnectWorksheets();
}
/**
@@ -287,6 +296,7 @@ class DrawingsTest extends AbstractFunctional
$drawing->setPath('tests/data/Writer/XLSX/orange_square_24_bit.bmp');
self::assertEquals($drawing->getWidth(), 70);
self::assertEquals($drawing->getHeight(), 70);
+ self::assertSame(IMAGETYPE_PNG, $drawing->getImageTypeForSave());
$comment = $sheet->getComment('A6');
$comment->setBackgroundImage($drawing);
$comment->setSizeAsBackgroundImage();
@@ -304,6 +314,8 @@ class DrawingsTest extends AbstractFunctional
$drawing = new Drawing();
$drawing->setName('Purple Square');
$drawing->setPath('tests/data/Writer/XLSX/purple_square.tiff');
+ self::assertStringContainsString('purple_square.tiff', $drawing->getFilename());
+ self::assertFalse($drawing->getIsUrl());
$comment = $sheet->getComment('A7');
self::assertTrue($comment instanceof Comment);
self::assertFalse($comment->hasBackgroundImage());
@@ -326,6 +338,14 @@ class DrawingsTest extends AbstractFunctional
self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
}
+ try {
+ $drawing->getMediaFilename();
+ self::fail('Should throw exception when attempting to get media file name for tiff');
+ } catch (PhpSpreadsheetException $e) {
+ self::assertTrue($e instanceof PhpSpreadsheetException);
+ self::assertEquals($e->getMessage(), 'Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
+ }
+
try {
$drawing->getImageFileExtensionForSave();
self::fail('Should throw exception when attempting to get image file extention for tiff');
@@ -428,7 +448,8 @@ class DrawingsTest extends AbstractFunctional
unlink($tempFileName);
- self::assertNotNull($reloadedSpreadsheet);
+ $spreadsheet->disconnectWorksheets();
+ $reloadedSpreadsheet->disconnectWorksheets();
}
/**
@@ -436,7 +457,6 @@ class DrawingsTest extends AbstractFunctional
*/
public function testTwoCellAnchorDrawing(): void
{
- $reader = new Xlsx();
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
@@ -455,31 +475,133 @@ class DrawingsTest extends AbstractFunctional
$drawing->setWorksheet($sheet);
// Write file
- $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
- $tempFileName = File::sysGetTempDir() . '/drawings_image_that_two_cell_anchor.xlsx';
- $writer->save($tempFileName);
-
- // Read new file
- $reloadedSpreadsheet = $reader->load($tempFileName);
- $sheet = $reloadedSpreadsheet->getActiveSheet();
+ $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
+ $spreadsheet->disconnectWorksheets();
+ $rsheet = $reloadedSpreadsheet->getActiveSheet();
// Check image coordinates.
- $drawingCollection = $sheet->getDrawingCollection();
+ $drawingCollection = $rsheet->getDrawingCollection();
+ self::assertCount(1, $drawingCollection);
$drawing = $drawingCollection[0];
self::assertNotNull($drawing);
+ self::assertSame(150, $drawing->getWidth());
+ self::assertSame(150, $drawing->getHeight());
+ self::assertSame('A1', $drawing->getCoordinates());
+ self::assertSame(30, $drawing->getOffsetX());
+ self::assertSame(10, $drawing->getOffsetY());
+ self::assertSame('E8', $drawing->getCoordinates2());
+ self::assertSame(-50, $drawing->getOffsetX2());
+ self::assertSame(-20, $drawing->getOffsetY2());
+ self::assertSame($rsheet, $drawing->getWorksheet());
+ $reloadedSpreadsheet->disconnectWorksheets();
+ }
+
+ /**
+ * Test editAs attribute for two-cell anchors.
+ *
+ * @dataProvider providerEditAs
+ */
+ public function testTwoCellEditAs(string $editAs, ?string $expectedResult = null): void
+ {
+ if ($expectedResult === null) {
+ $expectedResult = $editAs;
+ }
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+
+ // Add gif image that coordinates is two cell anchor.
+ $drawing = new Drawing();
+ $drawing->setName('Green Square');
+ $drawing->setPath('tests/data/Writer/XLSX/green_square.gif');
self::assertEquals($drawing->getWidth(), 150);
self::assertEquals($drawing->getHeight(), 150);
- self::assertEquals($drawing->getCoordinates(), 'A1');
- self::assertEquals($drawing->getOffsetX(), 30);
- self::assertEquals($drawing->getOffsetY(), 10);
- self::assertEquals($drawing->getCoordinates2(), 'E8');
- self::assertEquals($drawing->getOffsetX2(), -50);
- self::assertEquals($drawing->getOffsetY2(), -20);
- self::assertEquals($drawing->getWorksheet(), $sheet);
+ $drawing->setCoordinates('A1');
+ $drawing->setOffsetX(30);
+ $drawing->setOffsetY(10);
+ $drawing->setCoordinates2('E8');
+ $drawing->setOffsetX2(-50);
+ $drawing->setOffsetY2(-20);
+ if ($editAs !== '') {
+ $drawing->setEditAs($editAs);
+ }
+ $drawing->setWorksheet($sheet);
- unlink($tempFileName);
+ // Write file
+ $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
+ $spreadsheet->disconnectWorksheets();
+ $rsheet = $reloadedSpreadsheet->getActiveSheet();
- self::assertNotNull($reloadedSpreadsheet);
+ // Check image coordinates.
+ $drawingCollection = $rsheet->getDrawingCollection();
+ $drawing = $drawingCollection[0];
+ self::assertNotNull($drawing);
+
+ self::assertSame(150, $drawing->getWidth());
+ self::assertSame(150, $drawing->getHeight());
+ self::assertSame('A1', $drawing->getCoordinates());
+ self::assertSame(30, $drawing->getOffsetX());
+ self::assertSame(10, $drawing->getOffsetY());
+ self::assertSame('E8', $drawing->getCoordinates2());
+ self::assertSame(-50, $drawing->getOffsetX2());
+ self::assertSame(-20, $drawing->getOffsetY2());
+ self::assertSame($rsheet, $drawing->getWorksheet());
+ self::assertSame($expectedResult, $drawing->getEditAs());
+ $reloadedSpreadsheet->disconnectWorksheets();
+ }
+
+ public function providerEditAs(): array
+ {
+ return [
+ 'absolute' => ['absolute'],
+ 'onecell' => ['onecell'],
+ 'twocell' => ['twocell'],
+ 'unset (will be treated as twocell)' => [''],
+ 'unknown (will be treated as twocell)' => ['unknown', ''],
+ ];
+ }
+
+ public function testMemoryDrawingDuplicateResource(): void
+ {
+ $gdImage = imagecreatetruecolor(120, 20);
+ $textColor = ($gdImage === false) ? false : imagecolorallocate($gdImage, 255, 255, 255);
+ if ($gdImage === false || $textColor === false) {
+ self::fail('imagecreatetruecolor or imagecolorallocate failed');
+ } else {
+ $spreadsheet = new Spreadsheet();
+ $aSheet = $spreadsheet->getActiveSheet();
+ imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);
+ $listOfModes = [
+ BaseDrawing::EDIT_AS_TWOCELL,
+ BaseDrawing::EDIT_AS_ABSOLUTE,
+ BaseDrawing::EDIT_AS_ONECELL,
+ ];
+
+ foreach ($listOfModes as $i => $mode) {
+ $drawing = new MemoryDrawing();
+ $drawing->setName('In-Memory image ' . $i);
+ $drawing->setDescription('In-Memory image ' . $i);
+
+ $drawing->setCoordinates('A' . ((4 * $i) + 1));
+ $drawing->setCoordinates2('D' . ((4 * $i) + 4));
+ $drawing->setEditAs($mode);
+
+ $drawing->setImageResource($gdImage);
+ $drawing->setRenderingFunction(
+ MemoryDrawing::RENDERING_JPEG
+ );
+
+ $drawing->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT);
+
+ $drawing->setWorksheet($aSheet);
+ }
+ $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
+ $spreadsheet->disconnectWorksheets();
+
+ foreach ($reloadedSpreadsheet->getActiveSheet()->getDrawingCollection() as $index => $pDrawing) {
+ self::assertEquals($listOfModes[$index], $pDrawing->getEditAs(), 'functional test drawing twoCellAnchor');
+ }
+ $reloadedSpreadsheet->disconnectWorksheets();
+ }
}
}
diff --git a/tests/data/Calculation/Information/ERROR_TYPE.php b/tests/data/Calculation/Information/ERROR_TYPE.php
index b08d69a6..45f27970 100644
--- a/tests/data/Calculation/Information/ERROR_TYPE.php
+++ b/tests/data/Calculation/Information/ERROR_TYPE.php
@@ -53,4 +53,12 @@ return [
7,
'#N/A',
],
+ [
+ 9,
+ '#SPILL!',
+ ],
+ [
+ 14,
+ '#CALC!',
+ ],
];
diff --git a/tests/data/Calculation/Information/IS_ERROR.php b/tests/data/Calculation/Information/IS_ERROR.php
index 0755fd81..24e864fe 100644
--- a/tests/data/Calculation/Information/IS_ERROR.php
+++ b/tests/data/Calculation/Information/IS_ERROR.php
@@ -49,6 +49,14 @@ return [
true,
'#N/A',
],
+ [
+ true,
+ '#SPILL!',
+ ],
+ [
+ true,
+ '#CALC!',
+ ],
[
false,
'TRUE',
diff --git a/tests/data/Calculation/LookupRef/INDIRECT.php b/tests/data/Calculation/LookupRef/INDIRECT.php
index b8519657..7dc2515a 100644
--- a/tests/data/Calculation/LookupRef/INDIRECT.php
+++ b/tests/data/Calculation/LookupRef/INDIRECT.php
@@ -34,4 +34,8 @@ return [
'supply a1 argument as int' => [900, 'A2:A4', 1],
'supply a1 argument as float' => [900, 'A2:A4', 7.3],
'supply a1 argument as string not permitted' => ['#VALUE!', 'A2:A4', '1'],
+ 'row range' => [600, '1:3'],
+ 'column range' => [1500, 'A:C'],
+ 'row range on different sheet' => [66, 'OtherSheet!1:3'],
+ 'column range on different sheet' => [165, 'OtherSheet!A:C'],
];
diff --git a/tests/data/Calculation/LookupRef/VLOOKUP.php b/tests/data/Calculation/LookupRef/VLOOKUP.php
index ac60bda3..2162d49a 100644
--- a/tests/data/Calculation/LookupRef/VLOOKUP.php
+++ b/tests/data/Calculation/LookupRef/VLOOKUP.php
@@ -24,6 +24,13 @@ return [
2,
false,
],
+ [
+ '#REF!',
+ 1,
+ 'HELLO WORLD',
+ 2,
+ false,
+ ],
[
100,
1,
diff --git a/tests/data/Reader/XLS/CF_Basic_Comparisons.xls b/tests/data/Reader/XLS/CF_Basic_Comparisons.xls
new file mode 100644
index 00000000..10c91be6
Binary files /dev/null and b/tests/data/Reader/XLS/CF_Basic_Comparisons.xls differ
diff --git a/tests/data/Reader/XLS/CF_Expression_Comparisons.xls b/tests/data/Reader/XLS/CF_Expression_Comparisons.xls
new file mode 100644
index 00000000..58273cae
Binary files /dev/null and b/tests/data/Reader/XLS/CF_Expression_Comparisons.xls differ
diff --git a/tests/data/Reader/XLS/DataValidation.xls b/tests/data/Reader/XLS/DataValidation.xls
new file mode 100644
index 00000000..44e9d1d3
Binary files /dev/null and b/tests/data/Reader/XLS/DataValidation.xls differ
diff --git a/tests/data/ReferenceHelperFormulaUpdates.php b/tests/data/ReferenceHelperFormulaUpdates.php
index d0835603..ef563f31 100644
--- a/tests/data/ReferenceHelperFormulaUpdates.php
+++ b/tests/data/ReferenceHelperFormulaUpdates.php
@@ -22,6 +22,34 @@ return [
'2020',
'=SUM(A1:C3)',
],
+ 'column range' => [
+ '=SUM(B:C)',
+ 2,
+ 0,
+ '2020',
+ '=SUM(D:E)',
+ ],
+ 'column range with absolute' => [
+ '=SUM($B:C)',
+ 2,
+ 0,
+ '2020',
+ '=SUM($B:E)',
+ ],
+ 'row range' => [
+ '=SUM(2:3)',
+ 0,
+ 2,
+ '2020',
+ '=SUM(4:5)',
+ ],
+ 'row range with absolute' => [
+ '=SUM($2:3)',
+ 0,
+ 2,
+ '2020',
+ '=SUM($2:5)',
+ ],
[
'=SUM(2020!C3:E5,2019!C3:E5)',
-2,