From 6083e393c1f1274c74a2aed7be1b7e32d7dd57b8 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:01:33 -0700 Subject: [PATCH 01/32] Permit Read to Class Which Extends Spreadsheet See discussion #4202. Users can extend Spreadsheet, but the readers cannot return the extended class. This is solved pretty easily by adding a protected method in BaseReader which returns a new Spreadsheet. Users can then extend the Reader which they want, overriding that method to return the extended class. --- src/PhpSpreadsheet/Reader/BaseReader.php | 5 +++++ src/PhpSpreadsheet/Reader/Csv.php | 6 ++--- src/PhpSpreadsheet/Reader/Gnumeric.php | 3 +-- src/PhpSpreadsheet/Reader/Html.php | 7 +++--- src/PhpSpreadsheet/Reader/Ods.php | 3 +-- src/PhpSpreadsheet/Reader/Slk.php | 3 +-- .../Reader/Xls/LoadSpreadsheet.php | 2 +- src/PhpSpreadsheet/Reader/Xlsx.php | 2 +- src/PhpSpreadsheet/Reader/Xml.php | 6 ++--- .../Reader/Xlsx/MySpreadsheet.php | 22 +++++++++++++++++++ .../Reader/Xlsx/MyXlsxReader.php | 16 ++++++++++++++ .../Reader/Xlsx/MyXlsxTest.php | 20 +++++++++++++++++ 12 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/MySpreadsheet.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxReader.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php diff --git a/src/PhpSpreadsheet/Reader/BaseReader.php b/src/PhpSpreadsheet/Reader/BaseReader.php index de80834d1..49707f3de 100644 --- a/src/PhpSpreadsheet/Reader/BaseReader.php +++ b/src/PhpSpreadsheet/Reader/BaseReader.php @@ -257,4 +257,9 @@ abstract class BaseReader implements IReader return $this; } + + protected function newSpreadsheet(): Spreadsheet + { + return new Spreadsheet(); + } } diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index cd69f2ebe..da4cfb4ee 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -257,8 +257,7 @@ class Csv extends BaseReader */ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); // Load into this instance @@ -270,8 +269,7 @@ class Csv extends BaseReader */ public function loadSpreadsheetFromString(string $contents): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); // Load into this instance diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 5b2c1a52e..35122c4d9 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -231,8 +231,7 @@ class Gnumeric extends BaseReader */ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); $spreadsheet->removeSheetByIndex(0); diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index a733e0db1..6253c6785 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -210,8 +210,7 @@ class Html extends BaseReader */ public function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); // Load into this instance @@ -826,7 +825,7 @@ class Html extends BaseReader if ($loaded === false) { throw new Exception('Failed to load content as a DOM Document', 0, $e ?? null); } - $spreadsheet = $spreadsheet ?? new Spreadsheet(); + $spreadsheet = $spreadsheet ?? $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); self::loadProperties($dom, $spreadsheet); @@ -1225,7 +1224,7 @@ class Html extends BaseReader public function listWorksheetInfo(string $filename): array { $info = []; - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $this->loadIntoExisting($filename, $spreadsheet); foreach ($spreadsheet->getAllSheets() as $sheet) { $newEntry = ['worksheetName' => $sheet->getTitle()]; diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 77e073995..38a5ced19 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -229,8 +229,7 @@ class Ods extends BaseReader */ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); $spreadsheet->removeSheetByIndex(0); diff --git a/src/PhpSpreadsheet/Reader/Slk.php b/src/PhpSpreadsheet/Reader/Slk.php index 10995c5b4..21c069ef5 100644 --- a/src/PhpSpreadsheet/Reader/Slk.php +++ b/src/PhpSpreadsheet/Reader/Slk.php @@ -144,8 +144,7 @@ class Slk extends BaseReader */ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); // Load into this instance diff --git a/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php b/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php index 4683ec224..e94fbccc2 100644 --- a/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php +++ b/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php @@ -26,7 +26,7 @@ class LoadSpreadsheet extends Xls $xls->loadOLE($filename); // Initialisations - $xls->spreadsheet = new Spreadsheet(); + $xls->spreadsheet = $this->newSpreadsheet(); $xls->spreadsheet->setValueBinder($this->valueBinder); $xls->spreadsheet->removeSheetByIndex(0); // remove 1st sheet if (!$xls->readDataOnly) { diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 2e1ea196b..f23df1344 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -400,7 +400,7 @@ class Xlsx extends BaseReader File::assertFile($filename, self::INITIAL_FILE); // Initialisations - $excel = new Spreadsheet(); + $excel = $this->newSpreadsheet(); $excel->setValueBinder($this->valueBinder); $excel->removeSheetByIndex(0); $addingFirstCellStyleXf = true; diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index df370b2f7..76cd8a440 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -243,8 +243,7 @@ class Xml extends BaseReader */ public function loadSpreadsheetFromString(string $contents): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); $spreadsheet->removeSheetByIndex(0); @@ -257,8 +256,7 @@ class Xml extends BaseReader */ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet { - // Create new Spreadsheet - $spreadsheet = new Spreadsheet(); + $spreadsheet = $this->newSpreadsheet(); $spreadsheet->setValueBinder($this->valueBinder); $spreadsheet->removeSheetByIndex(0); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/MySpreadsheet.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/MySpreadsheet.php new file mode 100644 index 000000000..b41122797 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/MySpreadsheet.php @@ -0,0 +1,22 @@ +getActiveSheet() + ->getCell($cellAddress) + ->getValue(); + if (is_numeric($value)) { + return $value * $value; + } + + return '#VALUE!'; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxReader.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxReader.php new file mode 100644 index 000000000..c537f7863 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxReader.php @@ -0,0 +1,16 @@ +load($infile); + self::assertSame(64, $mySpreadsheet->calcSquare('A3')); + $mySpreadsheet->disconnectWorksheets(); + } +} From 50bf8a0fad5fc84e0a6cec2fb542d475589ba3fa Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:10:21 -0700 Subject: [PATCH 02/32] Wrong Case in File Name --- tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php index 8f4a35595..ed726311b 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/MyXlsxTest.php @@ -11,7 +11,7 @@ class MyXlsxTest extends TestCase public function testCustomSpreadsheetCustomLoader(): void { $reader = new MyXlsxReader(); - $infile = 'tests/data/reader/XLSX/colorscale.xlsx'; + $infile = 'tests/data/Reader/XLSX/colorscale.xlsx'; /** @var MySpreadsheet */ $mySpreadsheet = $reader->load($infile); self::assertSame(64, $mySpreadsheet->calcSquare('A3')); From 9c12e9daa5d134fa3613eb0e891daca4566e1b8d Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 15 Mar 2025 21:48:17 -0700 Subject: [PATCH 03/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a567e36e6..36b7ea020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Add ability to add custom functions to Calculation. [PR #4390](https://github.com/PHPOffice/PhpSpreadsheet/pull/4390) - Add FormulaRange to IgnoredErrors. [PR #4393](https://github.com/PHPOffice/PhpSpreadsheet/pull/4393) +- Permit read to class which extends Spreadsheet. [Discussion #4402](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4402) [PR #4404](https://github.com/PHPOffice/PhpSpreadsheet/pull/4404) ### Removed From 54210e4c07255b9f2ac2039831171592d43ae830 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 15 Mar 2025 22:41:31 -0700 Subject: [PATCH 04/32] Phpstan Level 9 - Part 3 of Many All src modules except Calculation/* and Shared/OLE/*. --- phpstan-baseline.neon | 210 ------------------ .../Calculation/TextData/Text.php | 13 +- src/PhpSpreadsheet/Cell/DataValidator.php | 4 +- src/PhpSpreadsheet/Helper/Sample.php | 3 +- src/PhpSpreadsheet/Reader/Html.php | 9 +- .../Reader/Xls/LoadSpreadsheet.php | 1 + src/PhpSpreadsheet/Shared/CodePage.php | 1 + src/PhpSpreadsheet/Spreadsheet.php | 5 +- .../ConditionalFormatting/CellMatcher.php | 3 +- .../Style/NumberFormat/DateFormatter.php | 1 + .../Style/NumberFormat/Formatter.php | 7 +- .../Style/NumberFormat/NumberFormatter.php | 5 +- src/PhpSpreadsheet/Worksheet/AutoFilter.php | 2 +- src/PhpSpreadsheet/Worksheet/Table.php | 1 + src/PhpSpreadsheet/Worksheet/Worksheet.php | 2 +- src/PhpSpreadsheet/Writer/Html.php | 2 +- src/PhpSpreadsheet/Writer/Xls/Parser.php | 4 +- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 3 +- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 2 +- 19 files changed, 43 insertions(+), 235 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 99f55a974..d2f3a9b7a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1200,114 +1200,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/TextData/Replace.php - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Text.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Text\:\:split\(\) should return array\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Text.php - - - - message: '#^Parameter \#1 \$str of function preg_quote expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Text.php - - - - message: '#^Parameter \#2 \$subject of static method Composer\\Pcre\\Preg\:\:split\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Text.php - - - - message: '#^Part \$formula \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString - count: 1 - path: src/PhpSpreadsheet/Cell/DataValidator.php - - - - message: '#^Parameter \#1 \$path of function basename expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Helper/Sample.php - - - - message: '#^Binary operation "\." between ''RICH TEXT\: '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter \#4 \$cellContent of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementBr\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter \#5 \$cellContent of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElement\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementBr\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementH1Etc\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 2 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementHr\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementLi\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 2 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementTable\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Parameter &\$cellContent by\-ref type of method PhpOffice\\PhpSpreadsheet\\Reader\\Html\:\:processDomElementThTd\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: '#^Binary operation "\-" between mixed and 1 results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Shared\\CodePage\:\:numberToName\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Shared/CodePage.php - - - - message: '#^Parameter \#2 \$to_encoding of function iconv expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Shared/CodePage.php - - message: '#^Cannot access an offset on mixed\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -1379,105 +1271,3 @@ parameters: identifier: clone.nonObject count: 2 path: src/PhpSpreadsheet/Shared/OLE/PPS.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/PhpSpreadsheet/Spreadsheet.php - - - - message: '#^Cannot clone non\-object variable \$item of type mixed\.$#' - identifier: clone.nonObject - count: 1 - path: src/PhpSpreadsheet/Spreadsheet.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Spreadsheet\:\:copy\(\) should return PhpOffice\\PhpSpreadsheet\\Spreadsheet but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Spreadsheet.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Spreadsheet\:\:getRibbonBinObjects\(\) should return array\|null but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Spreadsheet.php - - - - message: '#^Binary operation "\." between ''"'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php - - - - message: '#^Binary operation "\*" between 24\|1440\|86400 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 2 - path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php - - - - message: '#^Parameter \#2 \$replace of function str_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php - - - - message: '#^Binary operation "\*\=" between mixed and int\<1, max\> results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php - - - - message: '#^Cannot clone non\-object variable \$v of type mixed\.$#' - identifier: clone.nonObject - count: 1 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - - message: '#^Cannot clone non\-object variable \$v of type mixed\.$#' - identifier: clone.nonObject - count: 1 - path: src/PhpSpreadsheet/Worksheet/Table.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - - - message: '#^Parameter \#2 \$cellAddress of method PhpOffice\\PhpSpreadsheet\\Writer\\Html\:\:generateRowCellCss\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Writer/Html.php - - - - message: '#^Parameter \#1 \$token of method PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Parser\:\:convert\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Parser.php - - - - message: '#^Parameter \#1 \$tree of method PhpOffice\\PhpSpreadsheet\\Writer\\Xls\\Parser\:\:toReversePolish\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Parser.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php diff --git a/src/PhpSpreadsheet/Calculation/TextData/Text.php b/src/PhpSpreadsheet/Calculation/TextData/Text.php index 57117ca6c..b5e841b66 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Text.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Text.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Text { @@ -113,7 +114,7 @@ class Text { $text = Functions::flattenSingleValue($text); if (ErrorValue::isError($text, true)) { - return $text; + return StringHelper::convertToString($text); } $flags = self::matchFlags($matchMode); @@ -122,7 +123,7 @@ class Text $delimiter = self::buildDelimiter($rowDelimiter); $rows = ($delimiter === '()') ? [$text] - : Preg::split("/{$delimiter}/{$flags}", $text); + : Preg::split("/{$delimiter}/{$flags}", StringHelper::convertToString($text)); } else { $rows = [$text]; } @@ -141,7 +142,7 @@ class Text function (&$row) use ($delimiter, $flags, $ignoreEmpty): void { $row = ($delimiter === '()') ? [$row] - : Preg::split("/{$delimiter}/{$flags}", $row); + : Preg::split("/{$delimiter}/{$flags}", StringHelper::convertToString($row)); if ($ignoreEmpty === true) { $row = array_values(array_filter( $row, @@ -195,7 +196,7 @@ class Text return '(' . $delimiters . ')'; } - return '(' . preg_quote(Functions::flattenSingleValue($delimiter), '/') . ')'; + return '(' . preg_quote(StringHelper::convertToString(Functions::flattenSingleValue($delimiter)), '/') . ')'; } private static function matchFlags(bool $matchMode): string @@ -226,7 +227,7 @@ class Text return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE'); } - return (string) $cellValue; + return StringHelper::convertToString($cellValue); } private static function formatValueMode1(mixed $cellValue): string @@ -237,6 +238,6 @@ class Text return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE'); } - return (string) $cellValue; + return StringHelper::convertToString($cellValue); } } diff --git a/src/PhpSpreadsheet/Cell/DataValidator.php b/src/PhpSpreadsheet/Cell/DataValidator.php index dcee04935..5a4c7744b 100644 --- a/src/PhpSpreadsheet/Cell/DataValidator.php +++ b/src/PhpSpreadsheet/Cell/DataValidator.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Cell; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Exception; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; /** * Validate a cell value according to its validation rules. @@ -60,8 +61,9 @@ class DataValidator $calculation = Calculation::getInstance($cell->getWorksheet()->getParent()); try { + $formula2 = StringHelper::convertToString($formula); $result = $calculation - ->calculateFormula("=$formula", $cell->getCoordinate(), $cell); + ->calculateFormula("=$formula2", $cell->getCoordinate(), $cell); while (is_array($result)) { $result = array_pop($result); } diff --git a/src/PhpSpreadsheet/Helper/Sample.php b/src/PhpSpreadsheet/Helper/Sample.php index 420e80b7b..f7e380f78 100644 --- a/src/PhpSpreadsheet/Helper/Sample.php +++ b/src/PhpSpreadsheet/Helper/Sample.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart; use PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Settings; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Writer\IWriter; @@ -35,7 +36,7 @@ class Sample */ public function getScriptFilename(): string { - return basename($_SERVER['SCRIPT_FILENAME'], '.php'); + return basename(StringHelper::convertToString($_SERVER['SCRIPT_FILENAME']), '.php'); } /** diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index a733e0db1..41a2970b0 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -16,6 +16,7 @@ use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException; use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension; use PhpOffice\PhpSpreadsheet\Helper\Html as HelperHtml; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Color; @@ -251,6 +252,8 @@ class Html extends BaseReader /** * Flush cell. + * + * @param-out string $cellContent In one case, it can be bool */ protected function flushCell(Worksheet $sheet, string $column, int|string $row, mixed &$cellContent, array $attributeArray): void { @@ -273,7 +276,8 @@ class Html extends BaseReader } } if ($datatype === DataType::TYPE_BOOL) { - $cellContent = self::convertBoolean($cellContent); + // This is the case where we can set cellContent to bool rather than string + $cellContent = self::convertBoolean($cellContent); //* @phpstan-ignore-line if (!is_bool($cellContent)) { $attributeArray['data-type'] = DataType::TYPE_STRING; } @@ -293,7 +297,7 @@ class Html extends BaseReader } else { // We have a Rich Text run // TODO - $this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent; + $this->dataArray[$row][$column] = 'RICH TEXT: ' . StringHelper::convertToString($cellContent); } $cellContent = (string) ''; } @@ -623,6 +627,7 @@ class Html extends BaseReader // apply inline style $this->applyInlineStyle($sheet, $row, $column, $attributeArray); + /** @var string $cellContent */ $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray); $this->processDomElementBgcolor($sheet, $row, $column, $attributeArray); diff --git a/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php b/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php index 4683ec224..384288059 100644 --- a/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php +++ b/src/PhpSpreadsheet/Reader/Xls/LoadSpreadsheet.php @@ -480,6 +480,7 @@ class LoadSpreadsheet extends Xls case 0x08: // picture // get index to BSE entry (1-based) + /** @var int */ $BSEindex = $spContainer->getOPT(0x0104); // If there is no BSE Index, we will fail here and other fields are not read. diff --git a/src/PhpSpreadsheet/Shared/CodePage.php b/src/PhpSpreadsheet/Shared/CodePage.php index 307f8d931..ddc9def89 100644 --- a/src/PhpSpreadsheet/Shared/CodePage.php +++ b/src/PhpSpreadsheet/Shared/CodePage.php @@ -8,6 +8,7 @@ class CodePage { public const DEFAULT_CODE_PAGE = 'CP1252'; + /** @var array|string> */ private static array $pageArray = [ 0 => 'CP1252', // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program 367 => 'ASCII', // ASCII diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 597651bde..ca69b4131 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -331,7 +331,7 @@ class Spreadsheet implements JsonSerializable return $this->ribbonBinObjects; case 'names': case 'data': - if (is_array($this->ribbonBinObjects) && isset($this->ribbonBinObjects[$what])) { + if (is_array($this->ribbonBinObjects) && is_array($this->ribbonBinObjects[$what] ?? null)) { $ReturnData = $this->ribbonBinObjects[$what]; } @@ -1058,7 +1058,7 @@ class Spreadsheet implements JsonSerializable */ public function copy(): self { - return unserialize(serialize($this)); + return unserialize(serialize($this)); //* @phpstan-ignore-line } /** @@ -1119,6 +1119,7 @@ class Spreadsheet implements JsonSerializable switch ($key) { // arrays of objects not covered above case 'definedNames': + /** @var DefinedName[] */ $currentCollection = $val; $this->$key = []; foreach ($currentCollection as $item) { diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php index e14ceacdc..61027975a 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; @@ -123,7 +124,7 @@ class CellMatcher return 'NULL'; } - return '"' . $value . '"'; + return '"' . StringHelper::convertToString($value) . '"'; } return $value; diff --git a/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php index 4b3d301d2..9eb643c46 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php @@ -98,6 +98,7 @@ class DateFormatter '[ss]' => self::SECONDS_IN_DAY, ]; + /** @param float|int|numeric-string $value */ private static function tryInterval(bool &$seekingBracket, string &$block, mixed $value, string $format): void { if ($seekingBracket) { diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php index bbc4677cf..68bca2377 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Reader\Xls\Color\BIFF8; use PhpOffice\PhpSpreadsheet\RichText\RichText; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; @@ -129,13 +130,13 @@ class Formatter extends BaseFormatter $formatx = str_replace('\"', self::QUOTE_REPLACEMENT, $format); if (preg_match(self::SECTION_SPLIT, $format) === 0 && preg_match(self::SYMBOL_AT, $formatx) === 1) { if (!str_contains($format, '"')) { - return str_replace('@', $value, $format); + return str_replace('@', StringHelper::convertToString($value), $format); } //escape any dollar signs on the string, so they are not replaced with an empty value $value = str_replace( ['$', '"'], ['\$', self::QUOTE_REPLACEMENT], - (string) $value + StringHelper::convertToString($value) ); return str_replace( @@ -147,7 +148,7 @@ class Formatter extends BaseFormatter // If we have a text value, return it "as is" if (!is_numeric($value)) { - return (string) $value; + return StringHelper::convertToString($value); } // For 'General' format code, we just pass the value although this is not entirely the way Excel does it, diff --git a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php index 030570057..5e49744c3 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php @@ -159,9 +159,10 @@ class NumberFormatter extends BaseFormatter $size = $decimals + 3; return sprintf("%{$size}.{$decimals}E", $valueFloat); - } elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) { + } + if (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) { if ($valueFloat == floor($valueFloat) && substr_count($format, '.') === 1) { - $value *= 10 ** strlen(explode('.', $format)[1]); + $value *= 10 ** strlen(explode('.', $format)[1]); //* @phpstan-ignore-line } $result = self::complexNumberFormatMask($value, $format); diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index 73a2ea62d..5747d77c5 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -1071,7 +1071,7 @@ class AutoFilter implements Stringable // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects $this->{$key} = []; foreach ($value as $k => $v) { - $this->{$key}[$k] = clone $v; + $this->{$key}[$k] = clone $v; //* @phpstan-ignore-line // attach the new cloned Column to this new cloned Autofilter object $this->{$key}[$k]->setParent($this); } diff --git a/src/PhpSpreadsheet/Worksheet/Table.php b/src/PhpSpreadsheet/Worksheet/Table.php index 18a2ced2c..7f5b876ee 100644 --- a/src/PhpSpreadsheet/Worksheet/Table.php +++ b/src/PhpSpreadsheet/Worksheet/Table.php @@ -558,6 +558,7 @@ class Table implements Stringable // The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table objects $this->{$key} = []; foreach ($value as $k => $v) { + /** @var Table\Column $v */ $this->{$key}[$k] = clone $v; // attach the new cloned Column to this new cloned Table object $this->{$key}[$k]->setTable($this); diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 6f6c5cf14..daa1853bb 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -3766,7 +3766,7 @@ class Worksheet $keys = $this->cellCollection->getCoordinates(); foreach ($keys as $key) { if ($this->getCell($key)->getDataType() === DataType::TYPE_FORMULA) { - if (preg_match(self::FUNCTION_LIKE_GROUPBY, $this->getCell($key)->getValue()) !== 1) { + if (preg_match(self::FUNCTION_LIKE_GROUPBY, $this->getCell($key)->getValueString()) !== 1) { $this->getCell($key)->getCalculatedValue(); } } diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index a70cc974c..bc26c0ff2 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1557,7 +1557,7 @@ class Html extends BaseWriter /** * Generate row. * - * @param array $values Array containing cells in a row + * @param array $values Array containing cells in a row * @param int $row Row number (0-based) * @param string $cellType eg: 'td' */ diff --git a/src/PhpSpreadsheet/Writer/Xls/Parser.php b/src/PhpSpreadsheet/Writer/Xls/Parser.php index c6581adcb..ff26d2a7f 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Parser.php +++ b/src/PhpSpreadsheet/Writer/Xls/Parser.php @@ -1598,7 +1598,7 @@ class Parser $converted_tree = $this->toReversePolish($tree['left']); $polish .= $converted_tree; } elseif ($tree['left'] != '') { // It's a final node - $converted_tree = $this->convert($tree['left']); + $converted_tree = $this->convert($tree['left']); //* @phpstan-ignore-line $polish .= $converted_tree; } if (is_array($tree['right'])) { @@ -1621,7 +1621,7 @@ class Parser ) { // left subtree for a function is always an array. if ($tree['left'] != '') { - $left_tree = $this->toReversePolish($tree['left']); + $left_tree = $this->toReversePolish($tree['left']); //* @phpstan-ignore-line } else { $left_tree = ''; } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index afb901f78..a3c2ba1ad 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -13,6 +13,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Chart\TrendLine; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; @@ -1505,7 +1506,7 @@ class Chart extends WriterPart $count = $plotSeriesValues->getPointCount(); $source = $plotSeriesValues->getDataSource(); $values = $plotSeriesValues->getDataValues(); - if ($count > 1 || ($count === 1 && is_array($values) && array_key_exists(0, $values) && "=$source" !== (string) $values[0])) { + if ($count > 1 || ($count === 1 && is_array($values) && array_key_exists(0, $values) && "=$source" !== StringHelper::convertToString($values[0], false))) { $objWriter->startElement('c:' . $dataType . 'Cache'); if (($groupType != DataSeries::TYPE_PIECHART) && ($groupType != DataSeries::TYPE_PIECHART_3D) && ($groupType != DataSeries::TYPE_DONUTCHART)) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 903ddde1a..336cfe60c 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1604,7 +1604,7 @@ class Worksheet extends WriterPart $mappedType = $pCell->getDataType(); if ($mappedType === DataType::TYPE_FORMULA) { if ($this->useDynamicArrays) { - if (preg_match(PhpspreadsheetWorksheet::FUNCTION_LIKE_GROUPBY, $cellValue) === 1) { + if (preg_match(PhpspreadsheetWorksheet::FUNCTION_LIKE_GROUPBY, $cellValueString) === 1) { $tempCalc = []; } else { $tempCalc = $pCell->getCalculatedValue(); From 06ddcfbd7fa9becdc724158dc68acae93404bc10 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 15 Mar 2025 23:05:22 -0700 Subject: [PATCH 05/32] Phpstan Level 9 - Part 4 of Many Mostly Calculation/Calculation. --- phpstan-baseline.neon | 330 ------------------ .../Calculation/BinaryComparison.php | 23 +- .../Calculation/Calculation.php | 81 +++-- 3 files changed, 67 insertions(+), 367 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 99f55a974..b264bd9fd 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,323 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Parameter \#1 \$str1 of static method PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison\:\:strcmpAllowNull\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/BinaryComparison.php - - - - message: '#^Parameter \#1 \$str1 of static method PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison\:\:strcmpLowercaseFirst\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/BinaryComparison.php - - - - message: '#^Parameter \#2 \$str2 of static method PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison\:\:strcmpAllowNull\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/BinaryComparison.php - - - - message: '#^Parameter \#2 \$str2 of static method PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison\:\:strcmpLowercaseFirst\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/BinaryComparison.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 4 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\*" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\*\*" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\*\=" between mixed and \-1\|0\.01 results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\*\=" between mixed and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\+" between mixed and 1 results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\+" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\+\=" between mixed and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\-" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\-\=" between mixed and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between ''Pruned branch \(only…'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between ''a '' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between ''a boolean with a…''\|''a floating point…''\|''a matrix with a…''\|''a string with a…''\|''an integer number…'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between ''onlyIf\-'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 4 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between ''onlyIfNot\-'' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 4 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "\." between non\-falsy\-string and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "/" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Binary operation "/\=" between mixed and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''onlyIf'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''onlyIfNot'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''reference'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 3 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''storeKey'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''type'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 3 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset ''value'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset \(float\|int\) on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset \(int\|string\) on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset 0 on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 4 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset int\<0, max\> on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 41 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot access offset mixed on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Cannot call method push\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation\:\:makeError\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$array of function array_intersect_key expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$branchPruningEnabled of class PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\BranchPruner constructor expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$columnAddress of static method PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate\:\:columnIndexFromString\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$formula of method PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation\:\:_calculateFormulaValue\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$matrix of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation\:\:getMatrixDimensions\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$operand of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\FormattedNumber\:\:convertToNumberIfFormatted\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$string of function strtoupper expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$type of method PhpOffice\\PhpSpreadsheet\\Calculation\\Token\\Stack\:\:push\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#1 \$worksheetName of method PhpOffice\\PhpSpreadsheet\\Spreadsheet\:\:getSheetByName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#2 \.\.\.\$arrays of function array_intersect_key expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#3 \$reference of method PhpOffice\\PhpSpreadsheet\\Calculation\\Token\\Stack\:\:push\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#3 \$subject of function preg_replace_callback expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter \#3 \$subject of function str_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Parameter &\$stack by\-ref type of method PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation\:\:executeNumericBinaryOperation\(\) expects PhpOffice\\PhpSpreadsheet\\Calculation\\Token\\Stack, mixed given\.$#' - identifier: parameterByRef.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Part \$token \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString - count: 2 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - - - message: '#^Property PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation\:\:\$branchPruningEnabled \(bool\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: src/PhpSpreadsheet/Calculation/Calculation.php - - message: '#^Cannot access offset int\|string\|null on mixed\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -1086,24 +768,12 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - message: '#^Cannot access offset int\<0, max\> on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 11 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - message: '#^Cannot cast mixed to string\.$#' identifier: cast.string count: 1 path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Concatenate\:\:concatenate2Args\(\) should return array\|string but returns mixed\.$#' - identifier: return.type - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - message: '#^Parameter \#1 \$ignoreEmpty of static method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Concatenate\:\:evaluateTextJoinArray\(\) expects bool, mixed given\.$#' identifier: argument.type diff --git a/src/PhpSpreadsheet/Calculation/BinaryComparison.php b/src/PhpSpreadsheet/Calculation/BinaryComparison.php index e4bc156af..1f697946c 100644 --- a/src/PhpSpreadsheet/Calculation/BinaryComparison.php +++ b/src/PhpSpreadsheet/Calculation/BinaryComparison.php @@ -14,13 +14,15 @@ class BinaryComparison /** * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters. * - * @param null|string $str1 First string value for the comparison - * @param null|string $str2 Second string value for the comparison + * @param mixed $str1 First string value for the comparison, expect ?string + * @param mixed $str2 Second string value for the comparison, expect ?string */ - private static function strcmpLowercaseFirst(?string $str1, ?string $str2): int + private static function strcmpLowercaseFirst(mixed $str1, mixed $str2): int { - $inversedStr1 = StringHelper::strCaseReverse($str1 ?? ''); - $inversedStr2 = StringHelper::strCaseReverse($str2 ?? ''); + $str1 = StringHelper::convertToString($str1); + $str2 = StringHelper::convertToString($str2); + $inversedStr1 = StringHelper::strCaseReverse($str1); + $inversedStr2 = StringHelper::strCaseReverse($str2); return strcmp($inversedStr1, $inversedStr2); } @@ -28,12 +30,15 @@ class BinaryComparison /** * PHP8.1 deprecates passing null to strcmp. * - * @param null|string $str1 First string value for the comparison - * @param null|string $str2 Second string value for the comparison + * @param mixed $str1 First string value for the comparison, expect ?string + * @param mixed $str2 Second string value for the comparison, expect ?string */ - private static function strcmpAllowNull(?string $str1, ?string $str2): int + private static function strcmpAllowNull(mixed $str1, mixed $str2): int { - return strcmp($str1 ?? '', $str2 ?? ''); + $str1 = StringHelper::convertToString($str1); + $str2 = StringHelper::convertToString($str2); + + return strcmp($str1, $str2); } public static function compare(mixed $operand1, mixed $operand2, string $operator): bool diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 1572d7ea1..c65d19bdb 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -15,7 +15,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\ReferenceHelper; -use PhpOffice\PhpSpreadsheet\Shared; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use ReflectionClassConstant; @@ -371,7 +371,7 @@ class Calculation extends CalculationLocale */ public function setBranchPruningEnabled(mixed $enabled): void { - $this->branchPruningEnabled = $enabled; + $this->branchPruningEnabled = (bool) $enabled; $this->branchPruner = new BranchPruner($this->branchPruningEnabled); } @@ -470,14 +470,14 @@ class Calculation extends CalculationLocale try { $value = $cell->getValue(); - if ($cell->getDataType() === DataType::TYPE_FORMULA) { + if (is_string($value) && $cell->getDataType() === DataType::TYPE_FORMULA) { $value = preg_replace_callback( self::CALCULATION_REGEXP_CELLREF_SPILL, fn (array $matches) => 'ANCHORARRAY(' . substr($matches[0], 0, -1) . ')', $value ); } - $result = self::unwrapResult($this->_calculateFormulaValue($value, $cell->getCoordinate(), $cell)); + $result = self::unwrapResult($this->_calculateFormulaValue($value, $cell->getCoordinate(), $cell)); //* @phpstan-ignore-line if ($this->spreadsheet === null) { throw new Exception('null spreadsheet in calculateCellValue'); } @@ -496,7 +496,8 @@ class Calculation extends CalculationLocale $cellAddress = array_pop($this->cellStack); } if ($this->spreadsheet !== null && is_array($cellAddress) && array_key_exists('sheet', $cellAddress)) { - $testSheet = $this->spreadsheet->getSheetByName($cellAddress['sheet']); + $sheetName = $cellAddress['sheet'] ?? null; + $testSheet = is_string($sheetName) ? $this->spreadsheet->getSheetByName($sheetName) : null; if ($testSheet !== null && array_key_exists('cell', $cellAddress)) { $testSheet->getCell($cellAddress['cell']); } @@ -693,7 +694,13 @@ class Calculation extends CalculationLocale * Ensure that paired matrix operands are both matrices and of the same size. * * @param mixed $operand1 First matrix operand + * + * @param-out array $operand1 + * * @param mixed $operand2 Second matrix operand + * + * @param-out array $operand2 + * * @param int $resize Flag indicating whether the matrices should be resized to match * and (if so), whether the smaller dimension should grow or the * larger should shrink. @@ -706,9 +713,14 @@ class Calculation extends CalculationLocale // Examine each of the two operands, and turn them into an array if they aren't one already // Note that this function should only be called if one or both of the operand is already an array if (!is_array($operand1)) { - [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2); - $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1)); - $resize = 0; + if (is_array($operand2)) { + [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand2); + $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1)); + $resize = 0; + } else { + $operand1 = [$operand1]; + $operand2 = [$operand2]; + } } elseif (!is_array($operand2)) { [$matrixRows, $matrixColumns] = self::getMatrixDimensions($operand1); $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2)); @@ -913,6 +925,7 @@ class Calculation extends CalculationLocale } elseif (is_array($value)) { $typeString = 'a matrix'; } else { + /** @var string $value */ if ($value == '') { return 'an empty string'; } elseif ($value[0] == '#') { @@ -921,7 +934,7 @@ class Calculation extends CalculationLocale $typeString = 'a string'; } - return $typeString . ' with a value of ' . $this->showValue($value); + return $typeString . ' with a value of ' . StringHelper::convertToString($this->showValue($value)); } return null; @@ -1128,7 +1141,7 @@ class Calculation extends CalculationLocale $expectedArgumentCountString = $expectedArgumentCount; } } - } elseif ($expectedArgumentCount != '*') { + } elseif (is_string($expectedArgumentCount) && $expectedArgumentCount !== '*') { if (1 !== preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch)) { $argMatch = ['', '', '', '']; } @@ -1362,7 +1375,7 @@ class Calculation extends CalculationLocale } } elseif ($opCharacter === self::FORMULA_STRING_QUOTE) { // UnEscape any quotes within the string - $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val))); + $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, StringHelper::convertToString(self::unwrapResult($val)))); } elseif (isset(self::EXCEL_CONSTANTS[trim(strtoupper($val))])) { $stackItemType = 'Constant'; $excelConstant = trim(strtoupper($val)); @@ -1521,7 +1534,7 @@ class Calculation extends CalculationLocale /** * @return array|false|string */ - private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell $cell = null) + private function processTokenStack(false|array $tokens, ?string $cellID = null, ?Cell $cell = null) { if ($tokens === false) { return false; @@ -1790,8 +1803,10 @@ class Calculation extends CalculationLocale } elseif (Information\ErrorValue::isError($op2x)) { $operand1[$row][$column] = $op2x; } else { + /** @var string $op1x */ + /** @var string $op2x */ $operand1[$row][$column] - = Shared\StringHelper::substring( + = StringHelper::substring( $op1x . $op2x, 0, DataType::MAX_STRING_LENGTH @@ -1806,8 +1821,8 @@ class Calculation extends CalculationLocale } elseif (Information\ErrorValue::isError($operand2)) { $result = $operand2; } else { - $result = str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)); - $result = Shared\StringHelper::substring( + $result = str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)); //* @phpstan-ignore-line + $result = StringHelper::substring( $result, 0, DataType::MAX_STRING_LENGTH @@ -1824,6 +1839,8 @@ class Calculation extends CalculationLocale break; case '∩': // Intersect + /** @var array $operand1 */ + /** @var array $operand2 */ $rowIntersect = array_intersect_key($operand1, $operand2); $cellIntersect = $oCol = $oRow = []; foreach (array_keys($rowIntersect) as $row) { @@ -2105,7 +2122,7 @@ class Calculation extends CalculationLocale if (isset($storeKey)) { $branchStore[$storeKey] = $token; } - } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) { // @phpstan-ignore-line + } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) { // if the token is a named range or formula, evaluate it and push the result onto the stack $definedName = $matches[6]; if (str_starts_with($definedName, '_xleta')) { @@ -2163,7 +2180,7 @@ class Calculation extends CalculationLocale return $output; } - private function validateBinaryOperand(mixed &$operand, mixed &$stack): bool + private function validateBinaryOperand(mixed &$operand, Stack &$stack): bool { if (is_array($operand)) { if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) { @@ -2177,7 +2194,7 @@ class Calculation extends CalculationLocale // We only need special validations for the operand if it is a string // Start by stripping off the quotation marks we use to identify true excel string values internally if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) { - $operand = self::unwrapResult($operand); + $operand = StringHelper::convertToString(self::unwrapResult($operand)); } // If the string is a numeric value, we treat it as a numeric, so no further testing if (!is_numeric($operand)) { @@ -2204,7 +2221,7 @@ class Calculation extends CalculationLocale private function executeArrayComparison(mixed $operand1, mixed $operand2, string $operation, Stack &$stack, bool $recursingArrays): array { $result = []; - if (!is_array($operand2)) { + if (!is_array($operand2) && is_array($operand1)) { // Operand 1 is an array, Operand 2 is a scalar foreach ($operand1 as $x => $operandData) { $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operandData), $operation, $this->showValue($operand2)); @@ -2213,7 +2230,7 @@ class Calculation extends CalculationLocale $r = $stack->pop(); $result[$x] = $r['value']; } - } elseif (!is_array($operand1)) { + } elseif (is_array($operand2) && !is_array($operand1)) { // Operand 1 is a scalar, Operand 2 is an array foreach ($operand2 as $x => $operandData) { $this->debugLog->writeDebugLog('Evaluating Comparison %s %s %s', $this->showValue($operand1), $operation, $this->showValue($operandData)); @@ -2222,7 +2239,7 @@ class Calculation extends CalculationLocale $r = $stack->pop(); $result[$x] = $r['value']; } - } else { + } elseif (is_array($operand2) && is_array($operand1)) { // Operand 1 and Operand 2 are both arrays if (!$recursingArrays) { self::checkMatrixOperands($operand1, $operand2, 2); @@ -2234,6 +2251,8 @@ class Calculation extends CalculationLocale $r = $stack->pop(); $result[$x] = $r['value']; } + } else { + throw new Exception('Neither operand is an arra'); } // Log the result details $this->debugLog->writeDebugLog('Comparison Evaluation Result is %s', $this->showTypeDetails($result)); @@ -2306,29 +2325,33 @@ class Calculation extends CalculationLocale continue; } + /** @var float|int */ + $operand1Val = $operand1[$row][$column]; + /** @var float|int */ + $operand2Val = $operand2[$row][$column]; switch ($operation) { case '+': - $operand1[$row][$column] += $operand2[$row][$column]; + $operand1[$row][$column] = $operand1Val + $operand2Val; break; case '-': - $operand1[$row][$column] -= $operand2[$row][$column]; + $operand1[$row][$column] = $operand1Val - $operand2Val; break; case '*': - $operand1[$row][$column] *= $operand2[$row][$column]; + $operand1[$row][$column] = $operand1Val * $operand2Val; break; case '/': - if ($operand2[$row][$column] == 0) { + if ($operand2Val == 0) { $operand1[$row][$column] = ExcelError::DIV0(); } else { - $operand1[$row][$column] /= $operand2[$row][$column]; + $operand1[$row][$column] = $operand1Val / $operand2Val; } break; case '^': - $operand1[$row][$column] = $operand1[$row][$column] ** $operand2[$row][$column]; + $operand1[$row][$column] = $operand1Val ** $operand2Val; break; @@ -2340,6 +2363,8 @@ class Calculation extends CalculationLocale $result = $operand1; } else { // If we're dealing with non-matrix operations, execute the necessary operation + /** @var float|int $operand1 */ + /** @var float|int $operand2 */ switch ($operation) { // Addition case '+': @@ -2715,7 +2740,7 @@ class Calculation extends CalculationLocale private static function makeError(mixed $operand = ''): string { - return Information\ErrorValue::isError($operand) ? $operand : ExcelError::VALUE(); + return (is_string($operand) && Information\ErrorValue::isError($operand)) ? $operand : ExcelError::VALUE(); } private static function swapOperands(Stack $stack, string $opCharacter): bool From 19055da8b63f9fd3de7007dd7ac51c40d72be858 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:21:14 -0700 Subject: [PATCH 06/32] Better Handling of Chart DisplayBlanksAs Fix #4411. User copied some code from a PHPExcel program which set DisplayAsBlanks to `0`. This resulted in what Excel deemed a corrupt spreadsheet using PhpSpreadsheet. The only values allowed for that field are `gap`, `zero` (not `0`), and `span`. PHPExcel used `0` as a default, and got away with it because it ignored the value entirely when writing out the spreadsheet, using `gap` all the time. I had to choose between throwing an exception and just using the default when an attempt is made to set that property to an invalid value. An exception just seems more punitive than helpful to me, especially if we want people to migrate from PHPExcel, which still seems to have a large user base. So I've gone with using `gap` in place of an invalid value. Note that, according to https://learn.microsoft.com/ru-ru/openspecs/office_standards/ms-oe376/b5c5c694-21d9-437c-9a4a-21e0e843eed8, `gap` is used as the default whenever it is permitted for the chart in question; and, when it isn't permitted, the chart will use its default method (which will always be `zero`). There were no tests nor samples for this property. All the tests and samples which use it use only `gap`. I have added a small test, and a new sample to illustrate the difference between the 3 options. --- .../33_Chart_create_scatter7_blanks.php | 144 ++++++++++++++++++ src/PhpSpreadsheet/Chart/Chart.php | 7 +- src/PhpSpreadsheet/Chart/DataSeries.php | 2 + src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 8 +- .../Chart/DisplayBlanksAsTest.php | 106 +++++++++++++ 5 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 samples/Chart33b/33_Chart_create_scatter7_blanks.php create mode 100644 tests/PhpSpreadsheetTests/Chart/DisplayBlanksAsTest.php diff --git a/samples/Chart33b/33_Chart_create_scatter7_blanks.php b/samples/Chart33b/33_Chart_create_scatter7_blanks.php new file mode 100644 index 000000000..55305d741 --- /dev/null +++ b/samples/Chart33b/33_Chart_create_scatter7_blanks.php @@ -0,0 +1,144 @@ +getActiveSheet(); +$worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, null, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ], + strictNullComparison: true +); + +// Set the Labels for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 +]; +// Set the X-Axis Labels +$xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 +]; +// Set the Data values for each data series we want to plot +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4), +]; + +// Build the dataseries +$series = new DataSeries( + DataSeries::TYPE_SCATTERCHART, // plotType + null, // plotGrouping (Scatter charts don't have any grouping) + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues, // plotValues + null, // plotDirection + false, // smooth line + DataSeries::STYLE_LINEMARKER // plotStyle +); + +// Set the series in the plot area +$plotArea = new PlotArea(null, [$series]); +// Set the chart legend +$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false); + +$title1 = new Title('Test Scatter Chart Gap'); +$yAxisLabel1 = new Title('Value ($k)'); +// Create the chart +$chart1 = new Chart( + 'chart1', // name + $title1, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel1 // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart1->setTopLeftPosition('A7'); +$chart1->setBottomRightPosition('H20'); + +// Add the chart to the worksheet +$worksheet->addChart($chart1); + +$helper->renderChart($chart1, __FILE__); + +$title2 = new Title('Test Scatter Chart Zero'); +$yAxisLabel2 = new Title('Value ($k)'); +// Create the chart +$chart2 = new Chart( + 'chart2', // name + $title2, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_ZERO, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel2 // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart2->setTopLeftPosition('A22'); +$chart2->setBottomRightPosition('H35'); + +// Add the chart to the worksheet +$worksheet->addChart($chart2); + +$helper->renderChart($chart2, __FILE__); + +$title3 = new Title('Test Scatter Chart Span'); +$yAxisLabel3 = new Title('Value ($k)'); + +// Create the chart +$chart3 = new Chart( + 'chart3', // name + $title3, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_SPAN, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel3 // yAxisLabel +); + +// Set the position where the chart should appear in the worksheet +$chart3->setTopLeftPosition('A37'); +$chart3->setBottomRightPosition('H50'); + +// Add the chart to the worksheet +$worksheet->addChart($chart3); + +$helper->renderChart($chart3, __FILE__); + +// Save Excel 2007 file +$helper->write($spreadsheet, __FILE__, ['Xlsx'], true); diff --git a/src/PhpSpreadsheet/Chart/Chart.php b/src/PhpSpreadsheet/Chart/Chart.php index 6bcae28c8..6b41e367f 100644 --- a/src/PhpSpreadsheet/Chart/Chart.php +++ b/src/PhpSpreadsheet/Chart/Chart.php @@ -128,7 +128,7 @@ class Chart * Create a new Chart. * majorGridlines and minorGridlines are deprecated, moved to Axis. */ - public function __construct(string $name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, bool $plotVisibleOnly = true, string $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null) + public function __construct(string $name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, bool $plotVisibleOnly = true, string $displayBlanksAs = DataSeries::DEFAULT_EMPTY_AS, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null) { $this->name = $name; $this->title = $title; @@ -137,7 +137,7 @@ class Chart $this->yAxisLabel = $yAxisLabel; $this->plotArea = $plotArea; $this->plotVisibleOnly = $plotVisibleOnly; - $this->displayBlanksAs = $displayBlanksAs; + $this->setDisplayBlanksAs($displayBlanksAs); $this->xAxis = $xAxis ?? new Axis(); $this->yAxis = $yAxis ?? new Axis(); if ($majorGridlines !== null) { @@ -318,7 +318,8 @@ class Chart */ public function setDisplayBlanksAs(string $displayBlanksAs): static { - $this->displayBlanksAs = $displayBlanksAs; + $displayBlanksAs = strtolower($displayBlanksAs); + $this->displayBlanksAs = in_array($displayBlanksAs, DataSeries::VALID_EMPTY_AS, true) ? $displayBlanksAs : DataSeries::DEFAULT_EMPTY_AS; return $this; } diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index dd392005a..06f1c3c7f 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -43,6 +43,8 @@ class DataSeries const EMPTY_AS_GAP = 'gap'; const EMPTY_AS_ZERO = 'zero'; const EMPTY_AS_SPAN = 'span'; + const DEFAULT_EMPTY_AS = self::EMPTY_AS_GAP; + const VALID_EMPTY_AS = [self::EMPTY_AS_GAP, self::EMPTY_AS_ZERO, self::EMPTY_AS_SPAN]; /** * Series Plot Type. diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index afb901f78..8172fa22c 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -96,11 +96,9 @@ class Chart extends WriterPart $objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly()); $objWriter->endElement(); - if ($chart->getDisplayBlanksAs() !== '') { - $objWriter->startElement('c:dispBlanksAs'); - $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs()); - $objWriter->endElement(); - } + $objWriter->startElement('c:dispBlanksAs'); + $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs()); + $objWriter->endElement(); $objWriter->startElement('c:showDLblsOverMax'); $objWriter->writeAttribute('val', '0'); diff --git a/tests/PhpSpreadsheetTests/Chart/DisplayBlanksAsTest.php b/tests/PhpSpreadsheetTests/Chart/DisplayBlanksAsTest.php new file mode 100644 index 000000000..a1322c09a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/DisplayBlanksAsTest.php @@ -0,0 +1,106 @@ +getActiveSheet(); + $worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, 73, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ] + ); + + $dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 + ]; + + $xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 + ]; + + $dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4), + ]; + + // Build the dataseries + $series = new DataSeries( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues + ); + + $plotArea = new PlotArea(null, [$series]); + $legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false); + + $title = new Title('Test %age-Stacked Area Chart'); + $yAxisLabel = new Title('Value ($k)'); + + $chart1 = new Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel // yAxisLabel + ); + self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs()); + $chart1->setDisplayBlanksAs(DataSeries::EMPTY_AS_ZERO); + self::assertSame(DataSeries::EMPTY_AS_ZERO, $chart1->getDisplayBlanksAs()); + $chart1->setDisplayBlanksAs('0'); + self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs(), 'invalid setting converted to default'); + + $chart2 = new Chart( + 'chart2', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_SPAN, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel // yAxisLabel + ); + self::assertSame(DataSeries::EMPTY_AS_SPAN, $chart2->getDisplayBlanksAs()); + + $chart3 = new Chart( + 'chart3', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + '0', // displayBlanksAs, PHPExcel default + null, // xAxisLabel + $yAxisLabel // yAxisLabel + ); + self::assertSame(DataSeries::EMPTY_AS_GAP, $chart3->getDisplayBlanksAs(), 'invalid setting converted to default'); + + $spreadsheet->disconnectWorksheets(); + } +} From 71b243539af82b17c1945842205a9bb837ebafa5 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 20 Mar 2025 20:11:11 -0700 Subject: [PATCH 07/32] Ignore Fractional Part of Drawing Shadow Alpha Fix #4415. We store the rarely-used property Drawing/Shadow/Alpha as an integer representing the percentage. Excel also stores it as an integer, but multiplies it by 1,000, so we divide by 1,000 when we read this value. This can, and in the case of the issue at hand does, leave a fractional portion. Php has deprecated passing a float with a fractional portion to an int argument, so the reporter saw a deprecation message. This is easily fixed. --- src/PhpSpreadsheet/Reader/Xlsx.php | 16 +++++- .../Reader/Xlsx/Issue4415Test.php | 47 ++++++++++++++++++ tests/data/Reader/XLSX/issue.4415.xlsx | Bin 0 -> 29325 bytes 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4415Test.php create mode 100644 tests/data/Reader/XLSX/issue.4415.xlsx diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 2e1ea196b..ffb6c34dc 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -1492,7 +1492,13 @@ class Xlsx extends BaseReader $shadow->setAlignment(self::getArrayItemString(self::getAttributes($outerShdw), 'algn')); $clr = $outerShdw->srgbClr ?? $outerShdw->prstClr; $shadow->getColor()->setRGB(self::getArrayItemString(self::getAttributes($clr), 'val')); - $shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000); // @phpstan-ignore-line + if ($clr->alpha) { + $alpha = StringHelper::convertToString(self::getArrayItem(self::getAttributes($clr->alpha), 'val')); + if (is_numeric($alpha)) { + $alpha = (int) ($alpha / 1000); + $shadow->setAlpha($alpha); + } + } } $this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks); @@ -1597,7 +1603,13 @@ class Xlsx extends BaseReader $shadow->setAlignment(self::getArrayItemString(self::getAttributes($outerShdw), 'algn')); $clr = $outerShdw->srgbClr ?? $outerShdw->prstClr; $shadow->getColor()->setRGB(self::getArrayItemString(self::getAttributes($clr), 'val')); - $shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000); // @phpstan-ignore-line + if ($clr->alpha) { + $alpha = StringHelper::convertToString(self::getArrayItem(self::getAttributes($clr->alpha), 'val')); + if (is_numeric($alpha)) { + $alpha = (int) ($alpha / 1000); + $shadow->setAlpha($alpha); + } + } } $this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4415Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4415Test.php new file mode 100644 index 000000000..2417e9f22 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4415Test.php @@ -0,0 +1,47 @@ +'; + self::assertStringContainsString($expected, $data); + $expected = ''; + self::assertStringContainsString($expected, $data); + self::assertSame(2, substr_count($data, ''), 'first 2 drawings'); + self::assertSame(1, substr_count($data, ''), 'third drawings'); + } + + public function testFractionalAlpha(): void + { + $file = self::$file; + $reader = new XlsxReader(); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + $drawings = $sheet->getDrawingCollection(); + self::assertCount(3, $drawings); + self::assertNotNull($drawings[0]); + self::assertNotNull($drawings[1]); + self::assertNotNull($drawings[2]); + self::assertSame(50, $drawings[0]->getShadow()->getAlpha()); + self::assertSame(72, $drawings[1]->getShadow()->getAlpha()); + self::assertSame('', $drawings[1]->getCoordinates2(), 'one cell anchor'); + self::assertSame(90, $drawings[2]->getShadow()->getAlpha()); + self::assertNotEquals('', $drawings[2]->getCoordinates2(), 'two cell anchor'); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/issue.4415.xlsx b/tests/data/Reader/XLSX/issue.4415.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2b970b318d1c427712b93f249be7aacb18da530c GIT binary patch literal 29325 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 z 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 QkBJ4P1+l7$Y<~axf4^Itr2qf` literal 0 HcmV?d00001 From ca71ae77ac8fb978c4a064c5cb2079c70165672f Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 22 Mar 2025 20:37:28 -0700 Subject: [PATCH 08/32] TextGrid Improvements Helper/TextGrid was intended to assist with some samples. However, it has emerged recently on two tickets. PR #4342 intended to introduce functionality very similar to TextGrid, and was closed for that reason. Issue #1640 was closed as stale over 4 years ago, despite the fact that TextGrid seems an adequate resolution for it. Since it seems that there is a use for this function beyond its original intended usage, I added a few parameters to give it some flexibility - the ability to omit row and/or column headers, and the ability to add a divider line between rows. A description of this function is added to the formal documentation. --- docs/topics/reading-and-writing-to-file.md | 36 +++- src/PhpSpreadsheet/Helper/TextGrid.php | 61 +++++-- src/PhpSpreadsheet/Shared/StringHelper.php | 7 +- .../Helper/TextGridTest.php | 159 ++++++++++++++++++ 4 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Helper/TextGridTest.php diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index 70946fcb5..f472178f6 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -1169,6 +1169,38 @@ One benefit of flags is that you can pass several flags in a single method call. Two or more flags can be passed together using PHP's `|` operator. ```php -$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("myExampleFile.xlsx"); -$reader->load("spreadsheetWithCharts.xlsx", $reader::READ_DATA_ONLY | $reader::IGNORE_EMPTY_CELLS); +$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile('myExampleFile.xlsx'); +$reader->load( + 'spreadsheetWithCharts.xlsx', + $reader::READ_DATA_ONLY | $reader::IGNORE_EMPTY_CELLS +); +``` + +## Writing Data as a Plaintext Grid + +Although not really a spreadsheet format, it can be useful to write data in grid format to a plaintext file. +Code like the following can be used: +```php + $array = $sheet->toArray(null, true, true, true); + $textGrid = new \PhpOffice\PhpSpreadsheet\Shared\TextGrid( + $array, + true, // true for cli, false for html + // Starting with release 4.2, + // the output format can be tweaked by uncommenting + // any of the following 3 optional parameters. + // rowDividers: true, + // rowHeaders: false, + // columnHeaders: false, + ); + $result = $textGrid->render(); +``` +You can then echo `$result` to a terminal, or write it to a file with `file_put_contents`. The result will resemble: +``` + +-----+------------------+---+----------+ + | A | B | C | D | ++---+-----+------------------+---+----------+ +| 1 | 6 | 1900-01-06 00:00 | | 0.572917 | +| 2 | 6 | 1900-01-06 00:00 | | 1<>2 | +| 3 | xyz | xyz | | | ++---+-----+------------------+---+----------+ ``` diff --git a/src/PhpSpreadsheet/Helper/TextGrid.php b/src/PhpSpreadsheet/Helper/TextGrid.php index 693e0255a..76a5258a2 100644 --- a/src/PhpSpreadsheet/Helper/TextGrid.php +++ b/src/PhpSpreadsheet/Helper/TextGrid.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Helper; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; + class TextGrid { private bool $isCli; @@ -14,7 +16,13 @@ class TextGrid private string $gridDisplay; - public function __construct(array $matrix, bool $isCli = true) + private bool $rowDividers = false; + + private bool $rowHeaders = true; + + private bool $columnHeaders = true; + + public function __construct(array $matrix, bool $isCli = true, bool $rowDividers = false, bool $rowHeaders = true, bool $columnHeaders = true) { $this->rows = array_keys($matrix); $this->columns = array_keys($matrix[$this->rows[0]]); @@ -29,11 +37,14 @@ class TextGrid $this->matrix = $matrix; $this->isCli = $isCli; + $this->rowDividers = $rowDividers; + $this->rowHeaders = $rowHeaders; + $this->columnHeaders = $columnHeaders; } public function render(): string { - $this->gridDisplay = $this->isCli ? '' : '
';
+        $this->gridDisplay = $this->isCli ? '' : ('
' . PHP_EOL);
 
         if (!empty($this->rows)) {
             $maxRow = max($this->rows);
@@ -42,7 +53,9 @@ class TextGrid
 
             $this->renderColumnHeader($maxRowLength, $columnWidths);
             $this->renderRows($maxRowLength, $columnWidths);
-            $this->renderFooter($maxRowLength, $columnWidths);
+            if (!$this->rowDividers) {
+                $this->renderFooter($maxRowLength, $columnWidths);
+            }
         }
 
         $this->gridDisplay .= $this->isCli ? '' : '
'; @@ -53,30 +66,48 @@ class TextGrid private function renderRows(int $maxRowLength, array $columnWidths): void { foreach ($this->matrix as $row => $rowData) { - $this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' '; + if ($this->rowHeaders) { + $this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' '; + } $this->renderCells($rowData, $columnWidths); $this->gridDisplay .= '|' . PHP_EOL; + if ($this->rowDividers) { + $this->renderFooter($maxRowLength, $columnWidths); + } } } private function renderCells(array $rowData, array $columnWidths): void { foreach ($rowData as $column => $cell) { - $displayCell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell); + $valueForLength = StringHelper::convertToString($cell, convertBool: true); + $displayCell = $this->isCli ? $valueForLength : htmlentities($valueForLength); $this->gridDisplay .= '| '; - $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($cell ?? '') + 1); + $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($valueForLength) + 1); } } - private function renderColumnHeader(int $maxRowLength, array $columnWidths): void + private function renderColumnHeader(int $maxRowLength, array &$columnWidths): void { - $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2); + if (!$this->columnHeaders) { + $this->renderFooter($maxRowLength, $columnWidths); + + return; + } + foreach ($this->columns as $column => $reference) { + $columnWidths[$column] = max($columnWidths[$column], mb_strlen($reference)); + } + if ($this->rowHeaders) { + $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2); + } foreach ($this->columns as $column => $reference) { $this->gridDisplay .= '+-' . str_repeat('-', $columnWidths[$column] + 1); } $this->gridDisplay .= '+' . PHP_EOL; - $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2); + if ($this->rowHeaders) { + $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2); + } foreach ($this->columns as $column => $reference) { $this->gridDisplay .= '| ' . str_pad((string) $reference, $columnWidths[$column] + 1, ' '); } @@ -87,7 +118,9 @@ class TextGrid private function renderFooter(int $maxRowLength, array $columnWidths): void { - $this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1); + if ($this->rowHeaders) { + $this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1); + } foreach ($this->columns as $column => $reference) { $this->gridDisplay .= '+-'; $this->gridDisplay .= str_pad((string) '', $columnWidths[$column] + 1, '-'); @@ -112,13 +145,7 @@ class TextGrid $columnData = array_values($columnData); foreach ($columnData as $columnValue) { - if (is_string($columnValue)) { - $columnWidth = max($columnWidth, mb_strlen($columnValue)); - } elseif (is_bool($columnValue)) { - $columnWidth = max($columnWidth, mb_strlen($columnValue ? 'TRUE' : 'FALSE')); - } - - $columnWidth = max($columnWidth, mb_strlen((string) $columnWidth)); + $columnWidth = max($columnWidth, mb_strlen(StringHelper::convertToString($columnValue, convertBool: true))); } return $columnWidth; diff --git a/src/PhpSpreadsheet/Shared/StringHelper.php b/src/PhpSpreadsheet/Shared/StringHelper.php index ce9718f11..4427028a3 100644 --- a/src/PhpSpreadsheet/Shared/StringHelper.php +++ b/src/PhpSpreadsheet/Shared/StringHelper.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Shared; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException; use Stringable; @@ -647,8 +648,12 @@ class StringHelper return strlen("$string"); } - public static function convertToString(mixed $value, bool $throw = true, string $default = ''): string + /** @param bool $convertBool If true, convert bool to locale-aware TRUE/FALSE rather than 1/null-string */ + public static function convertToString(mixed $value, bool $throw = true, string $default = '', bool $convertBool = false): string { + if ($convertBool && is_bool($value)) { + return $value ? Calculation::getTRUE() : Calculation::getFALSE(); + } if ($value === null || is_scalar($value) || $value instanceof Stringable) { return (string) $value; } diff --git a/tests/PhpSpreadsheetTests/Helper/TextGridTest.php b/tests/PhpSpreadsheetTests/Helper/TextGridTest.php new file mode 100644 index 000000000..505e9198d --- /dev/null +++ b/tests/PhpSpreadsheetTests/Helper/TextGridTest.php @@ -0,0 +1,159 @@ +getActiveSheet(); + $sheet->fromArray([ + [6, '=TEXT(A1,"yyyy-mm-dd hh:mm")', null, 0.572917], + ['="6"', '=TEXT(A2,"yyyy-mm-dd hh:mm")', null, '1<>2'], + ['xyz', '=TEXT(A3,"yyyy-mm-dd hh:mm")'], + ], strictNullComparison: true); + $textGrid = new TextGrid( + $sheet->toArray(null, true, true, true), + $cli, + rowDividers: $rowDividers, + rowHeaders: $rowHeaders, + columnHeaders: $columnHeaders + ); + $result = $textGrid->render(); + // Note that, for cli, string will end with PHP_EOL, + // so explode will add an extra null-string element + // to its array output. + $lines = explode(PHP_EOL, $result); + self::assertSame($expected, $lines); + $spreadsheet->disconnectWorksheets(); + } + + public static function providerTextGrid(): array + { + return [ + 'cli default values' => [ + true, false, true, true, + [ + ' +-----+------------------+---+----------+', + ' | A | B | C | D |', + '+---+-----+------------------+---+----------+', + '| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |', + '| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |', + '| 3 | xyz | xyz | | |', + '+---+-----+------------------+---+----------+', + '', + ], + ], + 'html default values' => [ + false, false, true, true, + [ + '
',
+                    '    +-----+------------------+---+----------+',
+                    '    | A   | B                | C | D        |',
+                    '+---+-----+------------------+---+----------+',
+                    '| 1 | 6   | 1900-01-06 00:00 |   | 0.572917 |',
+                    '| 2 | 6   | 1900-01-06 00:00 |   | 1<>2     |',
+                    '| 3 | xyz | xyz              |   |          |',
+                    '+---+-----+------------------+---+----------+',
+                    '
', + ], + ], + 'cli rowDividers' => [ + true, true, true, true, + [ + ' +-----+------------------+---+----------+', + ' | A | B | C | D |', + '+---+-----+------------------+---+----------+', + '| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |', + '+---+-----+------------------+---+----------+', + '| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |', + '+---+-----+------------------+---+----------+', + '| 3 | xyz | xyz | | |', + '+---+-----+------------------+---+----------+', + '', + ], + ], + 'cli no columnHeaders' => [ + true, false, true, false, + [ + '+---+-----+------------------+--+----------+', + '| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |', + '| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |', + '| 3 | xyz | xyz | | |', + '+---+-----+------------------+--+----------+', + '', + ], + ], + 'cli no row headers' => [ + true, false, false, true, + [ + '+-----+------------------+---+----------+', + '| A | B | C | D |', + '+-----+------------------+---+----------+', + '| 6 | 1900-01-06 00:00 | | 0.572917 |', + '| 6 | 1900-01-06 00:00 | | 1<>2 |', + '| xyz | xyz | | |', + '+-----+------------------+---+----------+', + '', + ], + ], + 'cli row dividers, no row nor column headers' => [ + true, true, false, false, + [ + '+-----+------------------+--+----------+', + '| 6 | 1900-01-06 00:00 | | 0.572917 |', + '+-----+------------------+--+----------+', + '| 6 | 1900-01-06 00:00 | | 1<>2 |', + '+-----+------------------+--+----------+', + '| xyz | xyz | | |', + '+-----+------------------+--+----------+', + '', + ], + ], + ]; + } + + public function testBool(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + [0, 1], + [true, false], + [true, true], + ], strictNullComparison: true); + $textGrid = new TextGrid( + $sheet->toArray(null, true, false, true), + true, + rowDividers: false, + rowHeaders: false, + columnHeaders: false, + ); + $expected = [ + '+------+-------+', + '| 0 | 1 |', + '| TRUE | FALSE |', + '| TRUE | TRUE |', + '+------+-------+', + '', + ]; + $result = $textGrid->render(); + $lines = explode(PHP_EOL, $result); + self::assertSame($expected, $lines); + $spreadsheet->disconnectWorksheets(); + } +} From 4670aeaa0f57cf82243492ceb42a41df6df44fb5 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:47:39 -0700 Subject: [PATCH 09/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..0c82d4896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389) - Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) +- Better handling of Chart DisplayBlanksAs. [Issue #4411](https://github.com/PHPOffice/PhpSpreadsheet/issues/4411) [PR #4414](https://github.com/PHPOffice/PhpSpreadsheet/pull/4414) ## 2025-03-02 - 4.1.0 From 7fc2a3a1f8dc08fe8c4198f716dff5a72da8cf58 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:01:21 -0700 Subject: [PATCH 10/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..39b006691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed +- Ignore fractional part of Drawing Shadow Alpha. [Issue #4415](https://github.com/PHPOffice/PhpSpreadsheet/issues/4415) [PR #4417](https://github.com/PHPOffice/PhpSpreadsheet/pull/4417) - BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389) - Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) From 9875233c7e6edd5a5717668a5f8c33847286884c Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:11:02 -0700 Subject: [PATCH 11/32] Tweak to Spreadsheet Clone Spreadsheet clone already copies Calculation instanceArrayReturnType property. It should also copy some other Calculation instance properties, namely suppressFormulaErrors, calculationCacheEnabled, and branchPruningEnabled. --- .../Calculation/Calculation.php | 25 +++++++++---- src/PhpSpreadsheet/Spreadsheet.php | 9 +++++ .../SpreadsheetCopyCloneTest.php | 35 +++++++++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 1572d7ea1..0afae73a5 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -315,10 +315,12 @@ class Calculation extends CalculationLocale /** * Enable/disable calculation cache. */ - public function setCalculationCacheEnabled(bool $calculationCacheEnabled): void + public function setCalculationCacheEnabled(bool $calculationCacheEnabled): self { $this->calculationCacheEnabled = $calculationCacheEnabled; $this->clearCalculationCache(); + + return $this; } /** @@ -366,13 +368,17 @@ class Calculation extends CalculationLocale } } - /** - * Enable/disable calculation cache. - */ - public function setBranchPruningEnabled(mixed $enabled): void + public function getBranchPruningEnabled(): bool + { + return $this->branchPruningEnabled; + } + + public function setBranchPruningEnabled(mixed $enabled): self { $this->branchPruningEnabled = $enabled; $this->branchPruner = new BranchPruner($this->branchPruningEnabled); + + return $this; } public function enableBranchPruning(): void @@ -2687,9 +2693,11 @@ class Calculation extends CalculationLocale return $result; } - public function setSuppressFormulaErrors(bool $suppressFormulaErrors): void + public function setSuppressFormulaErrors(bool $suppressFormulaErrors): self { $this->suppressFormulaErrors = $suppressFormulaErrors; + + return $this; } public function getSuppressFormulaErrors(): bool @@ -2732,4 +2740,9 @@ class Calculation extends CalculationLocale return $retVal; } + + public function getSpreadsheet(): ?Spreadsheet + { + return $this->spreadsheet; + } } diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 597651bde..65f38e625 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -1079,6 +1079,15 @@ class Spreadsheet implements JsonSerializable $this->calculationEngine = new Calculation($this); if ($oldCalc !== null) { $this->calculationEngine + ->setSuppressFormulaErrors( + $oldCalc->getSuppressFormulaErrors() + ) + ->setCalculationCacheEnabled( + $oldCalc->getCalculationCacheEnabled() + ) + ->setBranchPruningEnabled( + $oldCalc->getBranchPruningEnabled() + ) ->setInstanceArrayReturnType( $oldCalc->getInstanceArrayReturnType() ); diff --git a/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php b/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php index bb99bd432..77f9a3390 100644 --- a/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php +++ b/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php @@ -49,6 +49,8 @@ class SpreadsheetCopyCloneTest extends TestCase } else { $this->spreadsheet2 = clone $this->spreadsheet; } + self::assertSame($this->spreadsheet, $this->spreadsheet->getCalculationEngine()->getSpreadsheet()); + self::assertSame($this->spreadsheet2, $this->spreadsheet2->getCalculationEngine()->getSpreadsheet()); self::assertSame('A3', $sheet->getSelectedCells()); $copysheet = $this->spreadsheet2->getActiveSheet(); self::assertSame('A3', $copysheet->getSelectedCells()); @@ -112,4 +114,37 @@ class SpreadsheetCopyCloneTest extends TestCase ['clone'], ]; } + + public static function providerCopyClone2(): array + { + return [ + ['copy', true, false, true, 'array'], + ['clone', true, false, true, 'array'], + ['copy', false, true, false, 'value'], + ['clone', false, true, false, 'value'], + ['copy', false, true, true, 'error'], + ['clone', false, true, true, 'error'], + ]; + } + + #[DataProvider('providerCopyClone2')] + public function testCopyClone2(string $type, bool $suppress, bool $cache, bool $pruning, string $return): void + { + $this->spreadsheet = new Spreadsheet(); + $calc = $this->spreadsheet->getCalculationEngine(); + $calc->setSuppressFormulaErrors($suppress); + $calc->setCalculationCacheEnabled($cache); + $calc->setBranchPruningEnabled($pruning); + $calc->setInstanceArrayReturnType($return); + if ($type === 'copy') { + $this->spreadsheet2 = $this->spreadsheet->copy(); + } else { + $this->spreadsheet2 = clone $this->spreadsheet; + } + $calc2 = $this->spreadsheet2->getCalculationEngine(); + self::assertSame($suppress, $calc2->getSuppressFormulaErrors()); + self::assertSame($cache, $calc2->getCalculationCacheEnabled()); + self::assertSame($pruning, $calc2->getBranchPruningEnabled()); + self::assertSame($return, $calc2->getInstanceArrayReturnType()); + } } From 60c7e3512445221026067821e2e520eb106fcda6 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:31:04 -0700 Subject: [PATCH 12/32] Fix Phpstan Errors --- tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php b/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php index 77f9a3390..f45ed9385 100644 --- a/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php +++ b/tests/PhpSpreadsheetTests/SpreadsheetCopyCloneTest.php @@ -49,8 +49,8 @@ class SpreadsheetCopyCloneTest extends TestCase } else { $this->spreadsheet2 = clone $this->spreadsheet; } - self::assertSame($this->spreadsheet, $this->spreadsheet->getCalculationEngine()->getSpreadsheet()); - self::assertSame($this->spreadsheet2, $this->spreadsheet2->getCalculationEngine()->getSpreadsheet()); + self::assertSame($this->spreadsheet, $this->spreadsheet->getCalculationEngine()?->getSpreadsheet()); + self::assertSame($this->spreadsheet2, $this->spreadsheet2->getCalculationEngine()?->getSpreadsheet()); self::assertSame('A3', $sheet->getSelectedCells()); $copysheet = $this->spreadsheet2->getActiveSheet(); self::assertSame('A3', $copysheet->getSelectedCells()); @@ -132,6 +132,7 @@ class SpreadsheetCopyCloneTest extends TestCase { $this->spreadsheet = new Spreadsheet(); $calc = $this->spreadsheet->getCalculationEngine(); + self::assertNotNull($calc); $calc->setSuppressFormulaErrors($suppress); $calc->setCalculationCacheEnabled($cache); $calc->setBranchPruningEnabled($pruning); @@ -142,6 +143,7 @@ class SpreadsheetCopyCloneTest extends TestCase $this->spreadsheet2 = clone $this->spreadsheet; } $calc2 = $this->spreadsheet2->getCalculationEngine(); + self::assertNotNull($calc2); self::assertSame($suppress, $calc2->getSuppressFormulaErrors()); self::assertSame($cache, $calc2->getCalculationCacheEnabled()); self::assertSame($pruning, $calc2->getBranchPruningEnabled()); From e3ef02a4221f68e8a52a08ce37ab29c685cc5e98 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:55:48 -0700 Subject: [PATCH 13/32] Phpstan Level 9 - Part 5 of Many Calculation: Database through Financial. --- phpstan-baseline.neon | 336 ------------------ .../Calculation/Database/DatabaseAbstract.php | 12 +- .../Calculation/DateTimeExcel/Date.php | 23 +- .../Calculation/DateTimeExcel/DateValue.php | 11 +- .../Calculation/DateTimeExcel/Helpers.php | 15 +- .../Calculation/DateTimeExcel/TimeParts.php | 6 +- .../Calculation/DateTimeExcel/YearFrac.php | 10 +- .../Calculation/Engine/FormattedNumber.php | 4 +- .../Calculation/Engine/Logger.php | 2 +- .../Calculation/Engineering/ConvertBase.php | 3 +- .../Calculation/Financial/Amortization.php | 8 +- .../CashFlow/Variable/NonPeriodic.php | 19 +- .../Financial/CashFlow/Variable/Periodic.php | 6 + .../Calculation/Financial/Helpers.php | 7 +- .../Financial/Securities/AccruedInterest.php | 7 +- .../Financial/Securities/Price.php | 11 +- .../Financial/Securities/Rates.php | 5 +- .../Financial/Securities/Yields.php | 9 +- .../Calculation/Information/Value.php | 38 +- 19 files changed, 128 insertions(+), 404 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 817c20b5c..c45352ec7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,299 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Cannot access offset int\|string\|null on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(mixed\)\: mixed\)\|null, ''strtoupper'' given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#1 \$criteriaNames of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DatabaseAbstract\:\:buildQuery\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#1 \$string of function strtoupper expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#2 \$array of function array_map expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#3 \$criteria of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DatabaseAbstract\:\:executeQuery\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Parameter \#4 \$fields of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Database\\DatabaseAbstract\:\:executeQuery\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php - - - - message: '#^Parameter \#1 \$day of static method PhpOffice\\PhpSpreadsheet\\Shared\\Date\:\:dayStringToNumber\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php - - - - message: '#^Parameter \#1 \$monthName of static method PhpOffice\\PhpSpreadsheet\\Shared\\Date\:\:monthStringToNumber\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 3 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: '#^Parameter \#1 \$dateValue of static method PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateValue\:\:fromString\(\) expects array\|bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: '#^Parameter \#1 \$excelTimestamp of static method PhpOffice\\PhpSpreadsheet\\Shared\\Date\:\:excelToDateTimeObject\(\) expects float\|int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: '#^Parameter \#1 \$timeValue of static method PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Helpers\:\:getTimeValue\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php - - - - message: '#^Binary operation "/" between mixed and 360 results in an error\.$#' - identifier: binaryOp.invalid - count: 3 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php - - - - message: '#^Binary operation "/" between mixed and 365 results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php - - - - message: '#^Binary operation "/" between mixed and float\|int results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php - - - - message: '#^Parameter &\$operand by\-ref type of method PhpOffice\\PhpSpreadsheet\\Calculation\\Engine\\FormattedNumber\:\:convertToNumberIfFraction\(\) expects string, mixed given\.$#' - identifier: parameterByRef.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php - - - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Engine/Logger.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php - - - - message: '#^Parameter \#1 \$year of static method PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\Helpers\:\:isLeapYear\(\) expects int\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Amortization.php - - - - message: '#^Binary operation "\+" between 1 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Binary operation "\-" between \-1 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xnpvOrdered\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Parameter \#1 \$values of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xirrBisection\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Parameter \#1 \$values of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xirrPart3\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Parameter \#2 \$dates of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xirrBisection\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Parameter \#2 \$dates of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xirrPart3\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Parameter \#2 \$values of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\CashFlow\\Variable\\NonPeriodic\:\:xnpvOrdered\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 7 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: '#^Binary operation "\+" between 1 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Binary operation "\+" between 1\.0 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Binary operation "\+" between mixed and float results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Binary operation "\+\=" between mixed and float results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Binary operation "\-" between float and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Binary operation "\-" between mixed and float results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php - - - - message: '#^Parameter \#1 \$year of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers\:\:daysPerYear\(\) expects int\|string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Financial/Coupons.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\AccruedInterest\:\:atMaturity\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\AccruedInterest\:\:periodic\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Price\:\:priceAtMaturity\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Price\:\:priceDiscounted\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Price\:\:received\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php - - - - message: '#^Parameter \#1 \$year of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers\:\:daysPerYear\(\) expects int\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Rates\:\:discount\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Rates\:\:interest\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Yields\:\:yieldAtMaturity\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Securities\\Yields\:\:yieldDiscounted\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php - - - - message: '#^Parameter \#1 \$year of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers\:\:daysPerYear\(\) expects int\|string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php - - - - message: '#^Parameter \#1 \$year of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Financial\\Helpers\:\:daysPerYear\(\) expects int\|string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php - - message: '#^Binary operation "\." between ''\='' and mixed results in an error\.$#' identifier: binaryOp.invalid @@ -354,48 +60,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/Functions.php - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Information\\Value\:\:asNumber\(\) should return float\|int\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Parameter \#1 \$coordinate of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Functions\:\:trimSheetFromCellReference\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Parameter \#1 \$coordinate of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Functions\:\:trimTrailingRange\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Parameter \#1 \$haystack of function str_contains expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Parameter \#1 \$namedRange of method PhpOffice\\PhpSpreadsheet\\Spreadsheet\:\:getNamedRange\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - - - message: '#^Parameter \#1 \$num1 of function fmod expects float, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Information/Value.php - - message: '#^Parameter \#1 \$row of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsA1\(\) expects int, mixed given\.$#' identifier: argument.type diff --git a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php index 7f0b57d3a..a54a3ac4e 100644 --- a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php +++ b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php @@ -27,12 +27,17 @@ abstract class DatabaseAbstract */ protected static function fieldExtract(array $database, mixed $field): ?int { - $field = strtoupper(Functions::flattenSingleValue($field) ?? ''); + /** @var ?string */ + $single = Functions::flattenSingleValue($field); + $field = strtoupper($single ?? ''); if ($field === '') { return null; } - $fieldNames = array_map('strtoupper', array_shift($database)); + /** @var callable */ + $callable = 'strtoupper'; + /** @var non-empty-array $database */ + $fieldNames = array_map($callable, array_shift($database)); if (is_numeric($field)) { $field = (int) $field - 1; if ($field < 0 || $field >= count($fieldNames)) { @@ -66,7 +71,9 @@ abstract class DatabaseAbstract */ protected static function filter(array $database, array $criteria): array { + /** @var array */ $fieldNames = array_shift($database); + /** @var array */ $criteriaNames = array_shift($criteria); // Convert the criteria into a set of AND/OR conditions with [:placeholders] @@ -84,6 +91,7 @@ abstract class DatabaseAbstract // extract an array of values for the requested column $columnData = []; + /** @var array $row */ foreach ($database as $rowKey => $row) { $keys = array_keys($row); $key = $keys[$field] ?? null; diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php index dd3bfc27f..d4925f8af 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php @@ -92,7 +92,11 @@ class Date */ private static function getYear(mixed $year, int $baseYear): int { - $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0; + if ($year === null) { + $year = 0; + } elseif (is_scalar($year)) { + $year = StringHelper::testStringAsNumeric((string) $year); + } if (!is_numeric($year)) { throw new Exception(ExcelError::VALUE()); } @@ -117,11 +121,14 @@ class Date */ private static function getMonth(mixed $month): int { - if (($month !== null) && (!is_numeric($month))) { + if (is_string($month) && !is_numeric($month)) { $month = SharedDateHelper::monthStringToNumber($month); } - - $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0; + if ($month === null) { + $month = 0; + } elseif (is_scalar($month)) { + $year = StringHelper::testStringAsNumeric((string) $month); + } if (!is_numeric($month)) { throw new Exception(ExcelError::VALUE()); } @@ -134,11 +141,15 @@ class Date */ private static function getDay(mixed $day): int { - if (($day !== null) && (!is_numeric($day))) { + if (is_string($day) && !is_numeric($day)) { $day = SharedDateHelper::dayStringToNumber($day); } - $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0; + if ($day === null) { + $day = 0; + } elseif (is_scalar($day)) { + $day = StringHelper::testStringAsNumeric((string) $day); + } if (!is_numeric($day)) { throw new Exception(ExcelError::VALUE()); } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php index b6411df58..e42b70530 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php @@ -149,9 +149,9 @@ class DateValue $PHPDateArray['hour'] = 0; $PHPDateArray['minute'] = 0; $PHPDateArray['second'] = 0; - $month = (int) $PHPDateArray['month']; - $day = (int) $PHPDateArray['day']; - $year = (int) $PHPDateArray['year']; + $month = self::getInt($PHPDateArray, 'month'); + $day = self::getInt($PHPDateArray, 'day'); + $year = self::getInt($PHPDateArray, 'year'); if (!checkdate($month, $day, $year)) { return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE(); } @@ -160,4 +160,9 @@ class DateValue return $retValue; } + + private static function getInt(array $array, string $index): int + { + return (array_key_exists($index, $array) && is_numeric($array[$index])) ? (int) $array[$index] : 0; + } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php index 04d58a957..84817d4cf 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php @@ -44,7 +44,9 @@ class Helpers if (!is_numeric($dateValue)) { $saveReturnDateType = Functions::getReturnDateType(); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - $dateValue = DateValue::fromString($dateValue); + if (is_string($dateValue)) { + $dateValue = DateValue::fromString($dateValue); + } Functions::setReturnDateType($saveReturnDateType); if (!is_numeric($dateValue)) { throw new Exception(ExcelError::VALUE()); @@ -75,8 +77,10 @@ class Helpers /** * Adjust date by given months. + * + * @param float|int $dateValue date to be adjusted */ - public static function adjustDateByMonths(mixed $dateValue = 0, float $adjustmentMonths = 0): DateTime + public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime { // Execute function $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); @@ -284,4 +288,11 @@ class Helpers { return is_array($dateArray) ? $dateArray : ['error_count' => 1]; } + + public static function floatOrInt(mixed $value): float|int + { + $result = Functions::scalar($value); + + return is_numeric($result) ? ($result + 0) : 0; + } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php index de5226924..7d9fa04f9 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php @@ -35,7 +35,7 @@ class TimeParts try { Helpers::nullFalseTrueToNumber($timeValue); - if (!is_numeric($timeValue)) { + if (is_string($timeValue) && !is_numeric($timeValue)) { $timeValue = Helpers::getTimeValue($timeValue); } Helpers::validateNotNegative($timeValue); @@ -76,7 +76,7 @@ class TimeParts try { Helpers::nullFalseTrueToNumber($timeValue); - if (!is_numeric($timeValue)) { + if (is_string($timeValue) && !is_numeric($timeValue)) { $timeValue = Helpers::getTimeValue($timeValue); } Helpers::validateNotNegative($timeValue); @@ -117,7 +117,7 @@ class TimeParts try { Helpers::nullFalseTrueToNumber($timeValue); - if (!is_numeric($timeValue)) { + if (is_string($timeValue) && !is_numeric($timeValue)) { $timeValue = Helpers::getTimeValue($timeValue); } Helpers::validateNotNegative($timeValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index 2713754ac..ec1b3bfbd 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -62,11 +62,11 @@ class YearFrac } return match ($method) { - 0 => Functions::scalar(Days360::between($startDate, $endDate)) / 360, + 0 => Helpers::floatOrInt(Days360::between($startDate, $endDate)) / 360, 1 => self::method1($startDate, $endDate), - 2 => Functions::scalar(Difference::interval($startDate, $endDate)) / 360, - 3 => Functions::scalar(Difference::interval($startDate, $endDate)) / 365, - 4 => Functions::scalar(Days360::between($startDate, $endDate, true)) / 360, + 2 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 360, + 3 => Helpers::floatOrInt(Difference::interval($startDate, $endDate)) / 365, + 4 => Helpers::floatOrInt(Days360::between($startDate, $endDate, true)) / 360, default => ExcelError::NAN(), }; } @@ -91,7 +91,7 @@ class YearFrac private static function method1(float $startDate, float $endDate): float { - $days = Functions::scalar(Difference::interval($startDate, $endDate)); + $days = Helpers::floatOrInt(Difference::interval($startDate, $endDate)); $startYear = (int) DateParts::year($startDate); $endYear = (int) DateParts::year($endDate); $years = $endYear - $startYear + 1; diff --git a/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php b/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php index ee1f2ae6e..5e18781ba 100644 --- a/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php +++ b/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php @@ -74,7 +74,9 @@ class FormattedNumber $sign = ($match[1] === '-') ? '-' : '+'; $wholePart = ($match[3] === '') ? '' : ($sign . $match[3]); $fractionFormula = '=' . $wholePart . $sign . $match[4]; - $operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula); + /** @var string */ + $operandx = Calculation::getInstance()->_calculateFormulaValue($fractionFormula); + $operand = $operandx; return true; } diff --git a/src/PhpSpreadsheet/Calculation/Engine/Logger.php b/src/PhpSpreadsheet/Calculation/Engine/Logger.php index 9adcd5595..e82faf5f9 100644 --- a/src/PhpSpreadsheet/Calculation/Engine/Logger.php +++ b/src/PhpSpreadsheet/Calculation/Engine/Logger.php @@ -78,7 +78,7 @@ class Logger { // Only write the debug log if logging is enabled if ($this->writeDebugLog) { - $message = sprintf($message, ...$args); + $message = sprintf($message, ...$args); //* @phpstan-ignore-line $cellReference = implode(' -> ', $this->cellStack->showStack()); if ($this->echoDebugLog) { echo $cellReference, diff --git a/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php b/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php index 1222831a1..6aa631a10 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; abstract class ConvertBase { @@ -26,7 +27,7 @@ abstract class ConvertBase } } - return strtoupper((string) $value); + return strtoupper(StringHelper::convertToString($value)); } protected static function validatePlaces(mixed $places = null): ?int diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index b53829b76..b775a54d9 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -171,9 +171,13 @@ class Amortization if ( $basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL && $yearFrac < 1 - && DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear)) ) { - $yearFrac *= 365 / 366; + $temp = Functions::scalar($purchasedYear); + if (is_int($temp) || is_string($temp)) { + if (DateTimeExcel\Helpers::isLeapYear($temp)) { + $yearFrac *= 365 / 366; + } + } } $f0Rate = $yearFrac * $rate * $cost; diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php index e503f72cb..732897971 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class NonPeriodic { @@ -23,16 +24,17 @@ class NonPeriodic * Excel Function: * =XIRR(values,dates,guess) * - * @param mixed $values A series of cash flow payments, expecting float[] + * @param array $values A series of cash flow payments, expecting float[] * The series of values must contain at least one positive value & one negative value * @param mixed[] $dates A series of payment dates * The first payment date indicates the beginning of the schedule of payments * All other dates must be later than this date, but they may occur in any order * @param mixed $guess An optional guess at the expected answer */ - public static function rate(mixed $values, mixed $dates, mixed $guess = self::DEFAULT_GUESS): float|string + public static function rate(mixed $values, $dates, mixed $guess = self::DEFAULT_GUESS): float|string { $rslt = self::xirrPart1($values, $dates); + /** @var array $dates */ if ($rslt !== '') { return $rslt; } @@ -107,7 +109,7 @@ class NonPeriodic * =XNPV(rate,values,dates) * * @param mixed $rate the discount rate to apply to the cash flows, expect array|float - * @param mixed $values A series of cash flows that corresponds to a schedule of payments in dates, expecting floag[]. + * @param array $values A series of cash flows that corresponds to a schedule of payments in dates, expecting float[]. * The first payment is optional and corresponds to a cost or payment that occurs * at the beginning of the investment. * If the first value is a cost or payment, it must be a negative value. @@ -127,9 +129,10 @@ class NonPeriodic return $neg && $pos; } + /** @param array $values */ private static function xirrPart1(mixed &$values, mixed &$dates): string { - $values = Functions::flattenArray($values); + $values = Functions::flattenArray($values); //* @phpstan-ignore-line $dates = Functions::flattenArray($dates); $valuesIsArray = count($values) > 1; $datesIsArray = count($dates) > 1; @@ -152,6 +155,7 @@ class NonPeriodic return self::xirrPart2($values); } + /** @param array $values */ private static function xirrPart2(array &$values): string { $valCount = count($values); @@ -159,7 +163,7 @@ class NonPeriodic $foundneg = false; for ($i = 0; $i < $valCount; ++$i) { $fld = $values[$i]; - if (!is_numeric($fld)) { + if (!is_numeric($fld)) { //* @phpstan-ignore-line return ExcelError::VALUE(); } elseif ($fld > 0) { $foundpos = true; @@ -243,6 +247,9 @@ class NonPeriodic private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false): float|string { $rate = Functions::flattenSingleValue($rate); + if (!is_numeric($rate)) { + return ExcelError::VALUE(); + } $values = Functions::flattenArray($values); $dates = Functions::flattenArray($dates); $valCount = count($values); @@ -274,7 +281,7 @@ class NonPeriodic $dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd')); } if (!is_numeric($dif)) { - return $dif; + return StringHelper::convertToString($dif); } if ($rate <= -1.0) { $xnpv += -abs($values[$i] + 0) / (-1 - $rate) ** ($dif / 365); diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php index 21e537be2..301c8d9db 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php @@ -36,6 +36,9 @@ class Periodic } $values = Functions::flattenArray($values); $guess = Functions::flattenSingleValue($guess); + if (!is_numeric($guess)) { + return ExcelError::VALUE(); + } // create an initial range, with a root somewhere between 0 and guess $x1 = 0.0; @@ -103,7 +106,9 @@ class Periodic return ExcelError::DIV0(); } $values = Functions::flattenArray($values); + /** @var float */ $financeRate = Functions::flattenSingleValue($financeRate); + /** @var float */ $reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate); $n = count($values); @@ -140,6 +145,7 @@ class Periodic { $returnValue = 0; + /** @var float */ $rate = Functions::flattenSingleValue($rate); $aArgs = Functions::flattenArray($args); diff --git a/src/PhpSpreadsheet/Calculation/Financial/Helpers.php b/src/PhpSpreadsheet/Calculation/Financial/Helpers.php index aa2871295..c983ecf49 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Helpers.php @@ -14,7 +14,7 @@ class Helpers * * Returns the number of days in a specified year, as defined by the "basis" value * - * @param int|string $year The year against which we're testing + * @param mixed $year The year against which we're testing, expect int|string * @param int|string $basis The type of day count: * 0 or omitted US (NASD) 360 * 1 Actual (365 or 366 in a leap year) @@ -24,8 +24,11 @@ class Helpers * * @return int|string Result, or a string containing an error */ - public static function daysPerYear($year, $basis = 0): string|int + public static function daysPerYear(mixed $year, $basis = 0): string|int { + if (!is_int($year) && !is_string($year)) { + return ExcelError::VALUE(); + } if (!is_numeric($basis)) { return ExcelError::NAN(); } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php index eb57abfca..c23373a4a 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\YearFrac; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants; use PhpOffice\PhpSpreadsheet\Calculation\Functions; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class AccruedInterest { @@ -81,12 +82,12 @@ class AccruedInterest $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis)); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error - return $daysBetweenIssueAndSettlement; + return StringHelper::convertToString($daysBetweenIssueAndSettlement); } $daysBetweenFirstInterestAndSettlement = Functions::scalar(YearFrac::fraction($firstInterest, $settlement, $basis)); if (!is_numeric($daysBetweenFirstInterestAndSettlement)) { // return date error - return $daysBetweenFirstInterestAndSettlement; + return StringHelper::convertToString($daysBetweenFirstInterestAndSettlement); } return $parValue * $rate * $daysBetweenIssueAndSettlement; @@ -143,7 +144,7 @@ class AccruedInterest $daysBetweenIssueAndSettlement = Functions::scalar(YearFrac::fraction($issue, $settlement, $basis)); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error - return $daysBetweenIssueAndSettlement; + return StringHelper::convertToString($daysBetweenIssueAndSettlement); } return $parValue * $rate * $daysBetweenIssueAndSettlement; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index b07b2c9fc..b6e1cd3fd 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Price { @@ -137,7 +138,7 @@ class Price $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity); @@ -201,19 +202,19 @@ class Price $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis)); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error - return $daysBetweenIssueAndSettlement; + return StringHelper::convertToString($daysBetweenIssueAndSettlement); } $daysBetweenIssueAndSettlement *= $daysPerYear; $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis)); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error - return $daysBetweenIssueAndMaturity; + return StringHelper::convertToString($daysBetweenIssueAndMaturity); } $daysBetweenIssueAndMaturity *= $daysPerYear; $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } $daysBetweenSettlementAndMaturity *= $daysPerYear; @@ -275,7 +276,7 @@ class Price $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return Functions::scalar($daysBetweenSettlementAndMaturity); + return StringHelper::convertToString(Functions::scalar($daysBetweenSettlementAndMaturity)); } return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity)); diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php index 2989a29b3..c2826738b 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Rates { @@ -65,7 +66,7 @@ class Rates $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity; @@ -126,7 +127,7 @@ class Rates $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity); diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php index a4c5a48fd..021b0de9e 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants; use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers; use PhpOffice\PhpSpreadsheet\Calculation\Functions; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Yields { @@ -64,7 +65,7 @@ class Yields $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } $daysBetweenSettlementAndMaturity *= $daysPerYear; @@ -129,19 +130,19 @@ class Yields $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis)); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error - return $daysBetweenIssueAndSettlement; + return StringHelper::convertToString($daysBetweenIssueAndSettlement); } $daysBetweenIssueAndSettlement *= $daysPerYear; $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis)); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error - return $daysBetweenIssueAndMaturity; + return StringHelper::convertToString($daysBetweenIssueAndMaturity); } $daysBetweenIssueAndMaturity *= $daysPerYear; $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis)); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error - return $daysBetweenSettlementAndMaturity; + return StringHelper::convertToString($daysBetweenSettlementAndMaturity); } $daysBetweenSettlementAndMaturity *= $daysPerYear; diff --git a/src/PhpSpreadsheet/Calculation/Information/Value.php b/src/PhpSpreadsheet/Calculation/Information/Value.php index 6f04df870..2ade1c2e1 100644 --- a/src/PhpSpreadsheet/Calculation/Information/Value.php +++ b/src/PhpSpreadsheet/Calculation/Information/Value.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\NamedRange; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class Value @@ -43,6 +44,7 @@ class Value return false; } + $value = StringHelper::convertToString($value); $cellValue = Functions::trimTrailingRange($value); if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) { [$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true, true); @@ -79,11 +81,12 @@ class Value if ($value === null) { return ExcelError::NAME(); - } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) { + } + if (!is_numeric($value)) { return ExcelError::VALUE(); } - return ((int) fmod($value, 2)) === 0; + return ((int) fmod($value + 0, 2)) === 0; } /** @@ -103,11 +106,12 @@ class Value if ($value === null) { return ExcelError::NAME(); - } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) { + } + if (!is_numeric($value)) { return ExcelError::VALUE(); } - return ((int) fmod($value, 2)) !== 0; + return ((int) fmod($value + 0, 2)) !== 0; } /** @@ -197,8 +201,9 @@ class Value if ($cell === null) { return ExcelError::REF(); } + $cellReference = StringHelper::convertToString($cellReference); - $fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell); + $fullCellReference = Functions::expandDefinedName($cellReference, $cell); if (str_contains($cellReference, '!')) { $cellReference = Functions::trimSheetFromCellReference($cellReference); @@ -244,21 +249,14 @@ class Value while (is_array($value)) { $value = array_shift($value); } - - switch (gettype($value)) { - case 'double': - case 'float': - case 'integer': - return $value; - case 'boolean': - return (int) $value; - case 'string': - // Errors - if (($value !== '') && ($value[0] == '#')) { - return $value; - } - - break; + if (is_float($value) || is_int($value)) { + return $value; + } + if (is_bool($value)) { + return (int) $value; + } + if (is_string($value) && substr($value, 0, 1) === '#') { + return $value; } return 0; From bc315a3edccaf8846157e6e1a0fb62d5b4574c6d Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 12:33:01 -0700 Subject: [PATCH 14/32] A Win For Scrutinizer! It flagged a statement as dead code. It was correct - there was a typo in the variable name. But no tests had failed. The explanation was, of course, that this particular path was not adequately tested. PhpSpreadsheet extends Excel (dating back to PHPExcel) by allowing the ordinal form of days in the DATE function, implemented as "take the numeric portion if the field is a string consisting of a numeric portion followed by some alphabetics". Whether or not this is a good idea, it would be a breaking change to eliminate it, so that's not going to happen. However, the same logic has been applied to month, and I don't see a use case for that, so I'm eliminating it - any non-numeric string used as the month parameter will now result in a VALUE error. It also turns out that Excel accepts null, false, and true for the month, and PhpSpreadsheet will now do likewise. --- CHANGELOG.md | 1 + .../Calculation/DateTimeExcel/Date.php | 15 ++++++++------- .../Calculation/Functions/DateTime/DateTest.php | 15 ++++++++------- tests/data/Calculation/DateTime/DATE.php | 5 +++++ 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f3bf4f8..b60ce1e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Start migration to Phpstan level 9. [PR #4396](https://github.com/PHPOffice/PhpSpreadsheet/pull/4396) - Calculation locale logic moved to separate class. [PR #4398](https://github.com/PHPOffice/PhpSpreadsheet/pull/4398) - TREND_POLYNOMIAL_* and TREND_BEST_FIT do not work, and are changed to throw Exceptions if attempted. (TREND_BEST_FIT_NO_POLY works.) An attempt to use an unknown trend type will now also throw an exception. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) +- Month parameter of DATE function will now return VALUE if an ordinal string (e.g. '3rd') is used, but will accept bool or null. [PR #4420](https://github.com/PHPOffice/PhpSpreadsheet/pull/4420) ### Moved diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php index d4925f8af..5858349d2 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php @@ -64,7 +64,7 @@ class Date * If an array of numbers is passed as the argument, then the returned result will also be an array * with the same dimensions */ - public static function fromYMD(array|float|int|string $year, array|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array + public static function fromYMD(array|float|int|string $year, null|array|bool|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array { if (is_array($year) || is_array($month) || is_array($day)) { return self::evaluateArrayArguments([self::class, __FUNCTION__], $year, $month, $day); @@ -121,13 +121,14 @@ class Date */ private static function getMonth(mixed $month): int { - if (is_string($month) && !is_numeric($month)) { - $month = SharedDateHelper::monthStringToNumber($month); - } - if ($month === null) { + if (is_string($month)) { + if (!is_numeric($month)) { + $month = SharedDateHelper::monthStringToNumber($month); + } + } elseif ($month === null) { $month = 0; - } elseif (is_scalar($month)) { - $year = StringHelper::testStringAsNumeric((string) $month); + } elseif (is_bool($month)) { + $month = (int) $month; } if (!is_numeric($month)) { throw new Exception(ExcelError::VALUE()); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php index 043c35b7b..8347715c3 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php @@ -12,6 +12,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheetTests\Calculation\Functions\FormulaArguments; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class DateTest extends TestCase @@ -36,14 +37,14 @@ class DateTest extends TestCase Functions::setReturnDateType($this->returnDateType); } - #[\PHPUnit\Framework\Attributes\DataProvider('providerDATE')] - public function testDirectCallToDATE(float|string $expectedResult, int|string $year, float|int|string $month, float|int|string $day): void + #[DataProvider('providerDATE')] + public function testDirectCallToDATE(float|string $expectedResult, int|string $year, null|bool|float|int|string $month, float|int|string $day): void { $result = Date::fromYMD($year, $month, $day); self::assertSame($expectedResult, $result); } - #[\PHPUnit\Framework\Attributes\DataProvider('providerDATE')] + #[DataProvider('providerDATE')] public function testDATEAsFormula(mixed $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -55,7 +56,7 @@ class DateTest extends TestCase self::assertSame($expectedResult, $result); } - #[\PHPUnit\Framework\Attributes\DataProvider('providerDATE')] + #[DataProvider('providerDATE')] public function testDATEInWorksheet(mixed $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -78,7 +79,7 @@ class DateTest extends TestCase return require 'tests/data/Calculation/DateTime/DATE.php'; } - #[\PHPUnit\Framework\Attributes\DataProvider('providerUnhappyDATE')] + #[DataProvider('providerUnhappyDATE')] public function testDATEUnhappyPath(string $expectedException, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -136,7 +137,7 @@ class DateTest extends TestCase self::assertEquals($result, ExcelError::NAN()); } - #[\PHPUnit\Framework\Attributes\DataProvider('providerDateArray')] + #[DataProvider('providerDateArray')] public function testDateArray(array $expectedResult, string $year, string $month, string $day): void { $calculation = Calculation::getInstance(); @@ -200,7 +201,7 @@ class DateTest extends TestCase ]; } - #[\PHPUnit\Framework\Attributes\DataProvider('providerDateArrayException')] + #[DataProvider('providerDateArrayException')] public function testDateArrayException(string $year, string $month, string $day): void { $calculation = Calculation::getInstance(); diff --git a/tests/data/Calculation/DateTime/DATE.php b/tests/data/Calculation/DateTime/DATE.php index d585846da..ee96ba0ee 100644 --- a/tests/data/Calculation/DateTime/DATE.php +++ b/tests/data/Calculation/DateTime/DATE.php @@ -62,11 +62,14 @@ return [ [39844.0, 2008, 13, 31], [39813.0, 2009, 1, 0], [39812.0, 2009, 1, -1], + 'month expressed as true' => [39812.0, 2009, true, -1], [39782.0, 2009, 0, 0], [39781.0, 2009, 0, -1], [39752.0, 2009, -1, 0], [39751.0, 2009, -1, -1], [40146.0, 2010, 0, -1], + 'month expressed as false' => [40146.0, 2010, false, -1], + 'month expressed as null' => [40146.0, 2010, null, -1], [40329.0, 2010, 5, 31], [40199.0, 2010, 1, '21st'], // Excel can't parse ordinal, PhpSpreadsheet can [40200.0, 2010, 1, '22nd'], // Excel can't parse ordinal, PhpSpreadsheet can @@ -75,6 +78,8 @@ return [ [40258.0, 2010, 'March', '21st'], // ordinal and month name // MS Excel will fail with a #VALUE return, but PhpSpreadsheet can parse this date [40258.0, 2010, 'March', 21], // Excel can't parse month name, PhpSpreadsheet can + 'month expressed as string' => [40258.0, 2010, '03', 21], + 'month expressed as invalid string' => [ExcelError::VALUE(), 2010, '03x', 21], [ExcelError::VALUE(), 'ABC', 1, 21], [ExcelError::VALUE(), 2010, 'DEF', 21], [ExcelError::VALUE(), 2010, 3, 'GHI'], From 5a486a185758ed5e4ab807a7185dbfd00c8ece24 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 23 Mar 2025 12:40:25 -0700 Subject: [PATCH 15/32] Phpstan Level 9 - Part 6 or Many (Shared/OLE) --- phpstan-baseline.neon | 72 ------------------- src/PhpSpreadsheet/Shared/OLE.php | 10 +-- .../Shared/OLE/ChainedBlockStream.php | 10 ++- src/PhpSpreadsheet/Shared/OLE/PPS.php | 4 +- 4 files changed, 16 insertions(+), 80 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 817c20b5c..89ad43c0f 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -869,75 +869,3 @@ parameters: identifier: return.type count: 1 path: src/PhpSpreadsheet/Calculation/TextData/Replace.php - - - - message: '#^Cannot access an offset on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Shared/OLE.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Shared/OLE.php - - - - message: '#^Cannot access offset array\\|string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$_file_handle on mixed\.$#' - identifier: property.nonObject - count: 4 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$bbat on mixed\.$#' - identifier: property.nonObject - count: 1 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$bigBlockSize on mixed\.$#' - identifier: property.nonObject - count: 3 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$bigBlockThreshold on mixed\.$#' - identifier: property.nonObject - count: 1 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$root on mixed\.$#' - identifier: property.nonObject - count: 2 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot access property \$sbat on mixed\.$#' - identifier: property.nonObject - count: 1 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot call method getBlockOffset\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Property PhpOffice\\PhpSpreadsheet\\Shared\\OLE\\ChainedBlockStream\:\:\$ole \(PhpOffice\\PhpSpreadsheet\\Shared\\OLE\|null\) does not accept mixed\.$#' - identifier: assign.propertyType - count: 1 - path: src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php - - - - message: '#^Cannot clone mixed\.$#' - identifier: clone.nonObject - count: 2 - path: src/PhpSpreadsheet/Shared/OLE/PPS.php diff --git a/src/PhpSpreadsheet/Shared/OLE.php b/src/PhpSpreadsheet/Shared/OLE.php index dbf6df80e..ba62230a0 100644 --- a/src/PhpSpreadsheet/Shared/OLE.php +++ b/src/PhpSpreadsheet/Shared/OLE.php @@ -83,7 +83,7 @@ class OLE /** * Size of big blocks. This is usually 512. * - * @var int number of octets per block + * @var int<1, max> number of octets per block */ public int $bigBlockSize; @@ -124,7 +124,9 @@ class OLE throw new ReaderException('Only Little-Endian encoding is supported.'); } // Size of blocks and short blocks in bytes - $this->bigBlockSize = 2 ** self::readInt2($fh); + /** @var int<1, max> */ + $temp = 2 ** self::readInt2($fh); + $this->bigBlockSize = $temp; $this->smallBlockSize = 2 ** self::readInt2($fh); // Skip UID, revision number and version number @@ -217,8 +219,8 @@ class OLE // Store current instance in global array, so that it can be accessed // in OLE_ChainedBlockStream::stream_open(). // Object is removed from self::$instances in OLE_Stream::close(). - $GLOBALS['_OLE_INSTANCES'][] = $this; - $keys = array_keys($GLOBALS['_OLE_INSTANCES']); + $GLOBALS['_OLE_INSTANCES'][] = $this; //* @phpstan-ignore-line + $keys = array_keys($GLOBALS['_OLE_INSTANCES']); //* @phpstan-ignore-line $instanceId = end($keys); $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId; diff --git a/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php b/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php index 52102161f..6b6d1677c 100644 --- a/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php +++ b/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Shared\OLE; +use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\Shared\OLE; class ChainedBlockStream @@ -55,20 +56,23 @@ class ChainedBlockStream // 25 is length of "ole-chainedblockstream://" parse_str(substr($path, 25), $this->params); - if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { + if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { //* @phpstan-ignore-line if ($options & STREAM_REPORT_ERRORS) { trigger_error('OLE stream not found', E_USER_WARNING); } return false; } - $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; + $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; //* @phpstan-ignore-line + if (!($this->ole instanceof OLE)) { + throw new Exception('class is not OLE'); + } $blockId = $this->params['blockId']; $this->data = ''; if (isset($this->params['size']) && $this->params['size'] < $this->ole->bigBlockThreshold && $blockId != $this->ole->root->startBlock) { // Block id refers to small blocks - $rootPos = $this->ole->getBlockOffset($this->ole->root->startBlock); + $rootPos = $this->ole->getBlockOffset((int) $this->ole->root->startBlock); while ($blockId != -2) { $pos = $rootPos + $blockId * $this->ole->bigBlockSize; $blockId = $this->ole->sbat[$blockId]; diff --git a/src/PhpSpreadsheet/Shared/OLE/PPS.php b/src/PhpSpreadsheet/Shared/OLE/PPS.php index 3a77c78c6..ee7fd11ed 100644 --- a/src/PhpSpreadsheet/Shared/OLE/PPS.php +++ b/src/PhpSpreadsheet/Shared/OLE/PPS.php @@ -181,7 +181,9 @@ class PPS { if (!is_array($to_save) || (empty($to_save))) { return self::ALL_ONE_BITS; - } elseif (count($to_save) == 1) { + } + /** @var self[] $to_save */ + if (count($to_save) == 1) { $cnt = count($raList); // If the first entry, it's the root... Don't clone it! $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0]; From 6b61da0017cad6e8e124df7309312ae0f59674bb Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 24 Mar 2025 20:18:51 -0700 Subject: [PATCH 16/32] Column Widths Not Preserved When Using Read Filter Fix #4416. A peculiar problem indeed. PhpSpreadsheet has been considering a column to be filtered if any cell in the column is filtered and does not preserve the column width if that is the case. It should consider the column not filtered if any cell in the column is not filtered, and consider it filtered only if there are no cells to which that applies. At least, that's how I think it should work, and this change doesn't break any existing tests, and solves this issue. --- .../Reader/Xlsx/ColumnAndRowAttributes.php | 31 ++++--- .../Reader/Xlsx/Issue4416Filter.php | 18 ++++ .../Reader/Xlsx/Issue4416Test.php | 82 ++++++++++++++++++ .../Reader/XLSX/issue.4416.smallauto.xlsx | Bin 0 -> 9615 bytes 4 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4416Filter.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4416Test.php create mode 100644 tests/data/Reader/XLSX/issue.4416.smallauto.xlsx diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php b/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php index cf9046ce5..63dd4ade5 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php @@ -77,6 +77,9 @@ class ColumnAndRowAttributes extends BaseParserClass if ($this->worksheetXml === null) { return; } + if ($readFilter !== null && $readFilter::class === DefaultReadFilter::class) { + $readFilter = null; + } $columnsAttributes = []; $rowsAttributes = []; @@ -85,11 +88,7 @@ class ColumnAndRowAttributes extends BaseParserClass } if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) { - $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly, $ignoreRowsWithNoCells); - } - - if ($readFilter !== null && $readFilter::class === DefaultReadFilter::class) { - $readFilter = null; + $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly, $ignoreRowsWithNoCells, $readFilter !== null); } // set columns/rows attributes @@ -123,12 +122,12 @@ class ColumnAndRowAttributes extends BaseParserClass private function isFilteredColumn(IReadFilter $readFilter, string $columnCoordinate, array $rowsAttributes): bool { foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) { - if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) { - return true; + if ($readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) { + return false; } } - return false; + return true; } private function readColumnAttributes(SimpleXMLElement $worksheetCols, bool $readDataOnly): array @@ -189,27 +188,31 @@ class ColumnAndRowAttributes extends BaseParserClass return false; } - private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly, bool $ignoreRowsWithNoCells): array + private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly, bool $ignoreRowsWithNoCells, bool $readFilterIsNotNull): array { $rowAttributes = []; foreach ($worksheetRow as $rowx) { $row = $rowx->attributes(); if ($row !== null && (!$ignoreRowsWithNoCells || isset($rowx->c))) { + $rowIndex = (int) $row['r']; if (isset($row['ht']) && !$readDataOnly) { - $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht']; + $rowAttributes[$rowIndex]['rowHeight'] = (float) $row['ht']; } if (isset($row['hidden']) && self::boolean($row['hidden'])) { - $rowAttributes[(int) $row['r']]['visible'] = false; + $rowAttributes[$rowIndex]['visible'] = false; } if (isset($row['collapsed']) && self::boolean($row['collapsed'])) { - $rowAttributes[(int) $row['r']]['collapsed'] = true; + $rowAttributes[$rowIndex]['collapsed'] = true; } if (isset($row['outlineLevel']) && (int) $row['outlineLevel'] > 0) { - $rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel']; + $rowAttributes[$rowIndex]['outlineLevel'] = (int) $row['outlineLevel']; } if (isset($row['s']) && !$readDataOnly) { - $rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s']; + $rowAttributes[$rowIndex]['xfIndex'] = (int) $row['s']; + } + if ($readFilterIsNotNull && empty($rowAttributes[$rowIndex])) { + $rowAttributes[$rowIndex]['exists'] = true; } } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4416Filter.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4416Filter.php new file mode 100644 index 000000000..e7e5cbb66 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4416Filter.php @@ -0,0 +1,18 @@ +load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertEqualsWithDelta( + 16.5430, + $sheet->getColumnDimension('A')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 6.0, + $sheet->getColumnDimension('B')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 11.3633, + $sheet->getColumnDimension('C')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 41.0898, + $sheet->getColumnDimension('D')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 28.5, + $sheet->getRowDimension(6)->getRowHeight(), + 1E-4 + ); + $spreadsheet->disconnectWorksheets(); + } + + public function testWithFilter(): void + { + $file = self::$file; + $reader = new XlsxReader(); + $reader->setReadFilter(new Issue4416Filter()); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertEqualsWithDelta( + 16.5430, + $sheet->getColumnDimension('A')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 6.0, + $sheet->getColumnDimension('B')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 11.3633, + $sheet->getColumnDimension('C')->getWidth(), + 1E-4 + ); + self::assertEqualsWithDelta( + 41.0898, + $sheet->getColumnDimension('D')->getWidth(), + 1E-4 + ); + self::assertEquals( + -1, + $sheet->getRowDimension(6)->getRowHeight(), + 'row has been filtered away' + ); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLSX/issue.4416.smallauto.xlsx b/tests/data/Reader/XLSX/issue.4416.smallauto.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a9dc2d0a67dedf3d4504a27b1826cc64de452fd6 GIT binary patch literal 9615 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 LbibV8=hgoJo?y#{ literal 0 HcmV?d00001 From eb319843402dfe2843e23be56c21f3ccaca26cc2 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 24 Mar 2025 21:06:43 -0700 Subject: [PATCH 17/32] Make Class Extendable Document some cases where solution might not work well. Making class extendable and offering some over-rideable methods may allow for solutions to some of these problems. --- docs/topics/reading-and-writing-to-file.md | 7 ++++++- src/PhpSpreadsheet/Helper/TextGrid.php | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index f472178f6..a0181993e 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -1200,7 +1200,12 @@ You can then echo `$result` to a terminal, or write it to a file with `file_put_ | A | B | C | D | +---+-----+------------------+---+----------+ | 1 | 6 | 1900-01-06 00:00 | | 0.572917 | -| 2 | 6 | 1900-01-06 00:00 | | 1<>2 | +| 2 | 6 | TRUE | | 1<>2 | | 3 | xyz | xyz | | | +---+-----+------------------+---+----------+ ``` +Please note that this may produce sub-optimal results for situations such as: +- use of accents as combining characters rather than using pre-composed characters (may be handled by extending the class to override the `getString` or `strlen` methods) +- Fullwidth characters +- right-to-left characters (better display in a browser than a terminal on a non-RTL system) +- multi-line strings diff --git a/src/PhpSpreadsheet/Helper/TextGrid.php b/src/PhpSpreadsheet/Helper/TextGrid.php index 76a5258a2..c7868c050 100644 --- a/src/PhpSpreadsheet/Helper/TextGrid.php +++ b/src/PhpSpreadsheet/Helper/TextGrid.php @@ -48,7 +48,7 @@ class TextGrid if (!empty($this->rows)) { $maxRow = max($this->rows); - $maxRowLength = mb_strlen((string) $maxRow) + 1; + $maxRowLength = strlen((string) $maxRow) + 1; $columnWidths = $this->getColumnWidths(); $this->renderColumnHeader($maxRowLength, $columnWidths); @@ -80,10 +80,10 @@ class TextGrid private function renderCells(array $rowData, array $columnWidths): void { foreach ($rowData as $column => $cell) { - $valueForLength = StringHelper::convertToString($cell, convertBool: true); + $valueForLength = $this->getString($cell); $displayCell = $this->isCli ? $valueForLength : htmlentities($valueForLength); $this->gridDisplay .= '| '; - $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($valueForLength) + 1); + $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - $this->strlen($valueForLength) + 1); } } @@ -95,7 +95,7 @@ class TextGrid return; } foreach ($this->columns as $column => $reference) { - $columnWidths[$column] = max($columnWidths[$column], mb_strlen($reference)); + $columnWidths[$column] = max($columnWidths[$column], $this->strlen($reference)); } if ($this->rowHeaders) { $this->gridDisplay .= str_repeat(' ', $maxRowLength + 2); @@ -145,9 +145,19 @@ class TextGrid $columnData = array_values($columnData); foreach ($columnData as $columnValue) { - $columnWidth = max($columnWidth, mb_strlen(StringHelper::convertToString($columnValue, convertBool: true))); + $columnWidth = max($columnWidth, $this->strlen($this->getString($columnValue))); } return $columnWidth; } + + protected function getString(mixed $value): string + { + return StringHelper::convertToString($value, convertBool: true); + } + + protected function strlen(string $value): int + { + return mb_strlen($value); + } } From 80ee4a4eb81ec044d63e45a9120a8f13769ea9c8 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 24 Mar 2025 21:24:38 -0700 Subject: [PATCH 18/32] Didn't Like Doc Formatting Looks like list may need a blank line preceding it. --- docs/topics/reading-and-writing-to-file.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index a0181993e..8764851b8 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -1205,6 +1205,7 @@ You can then echo `$result` to a terminal, or write it to a file with `file_put_ +---+-----+------------------+---+----------+ ``` Please note that this may produce sub-optimal results for situations such as: + - use of accents as combining characters rather than using pre-composed characters (may be handled by extending the class to override the `getString` or `strlen` methods) - Fullwidth characters - right-to-left characters (better display in a browser than a terminal on a non-RTL system) From 100d14969dbab2524e8d7284951ee0972ae6167b Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 24 Mar 2025 22:05:20 -0700 Subject: [PATCH 19/32] Fix Typo in Style exportArray quotePrefix Fix #4422. Add tests. --- src/PhpSpreadsheet/Style/Style.php | 2 +- .../Style/ExportArrayTest.php | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Style/Style.php b/src/PhpSpreadsheet/Style/Style.php index 17d632e9b..7d36489af 100644 --- a/src/PhpSpreadsheet/Style/Style.php +++ b/src/PhpSpreadsheet/Style/Style.php @@ -698,7 +698,7 @@ class Style extends Supervisor $this->exportArray2($exportedArray, 'font', $this->getFont()); $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat()); $this->exportArray2($exportedArray, 'protection', $this->getProtection()); - $this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix()); + $this->exportArray2($exportedArray, 'quotePrefix', $this->getQuotePrefix()); return $exportedArray; } diff --git a/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php b/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php index 5709d5c0a..7480a4d92 100644 --- a/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php +++ b/tests/PhpSpreadsheetTests/Style/ExportArrayTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace PhpOffice\PhpSpreadsheetTests\Style; +use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; @@ -287,4 +288,24 @@ class ExportArrayTest extends TestCase ); $spreadsheet->disconnectWorksheets(); } + + public function testQuotePrefix(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1') + ->setValueExplicit('=1+2', DataType::TYPE_STRING); + self::assertSame('=1+2', $sheet->getCell('A1')->getCalculatedValue()); + self::assertTrue($sheet->getStyle('A1')->getQuotePrefix()); + $sheet->getCell('A2')->setValue('=1+2'); + self::assertSame(3, $sheet->getCell('A2')->getCalculatedValue()); + self::assertFalse($sheet->getStyle('A2')->getQuotePrefix()); + $styleArray1 = $sheet->getStyle('A1')->exportArray(); + $styleArray2 = $sheet->getStyle('A2')->exportArray(); + $sheet->getStyle('B1')->applyFromArray($styleArray1); + $sheet->getStyle('B2')->applyFromArray($styleArray2); + self::assertTrue($sheet->getStyle('B1')->getQuotePrefix()); + self::assertFalse($sheet->getStyle('B2')->getQuotePrefix()); + $spreadsheet->disconnectWorksheets(); + } } From f5b4f18090095c7c093664d429f9a134c584df0e Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 25 Mar 2025 00:06:53 -0700 Subject: [PATCH 20/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..eb231376d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389) - Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) +- Tweak Spreadsheet clone. [PR #4419](https://github.com/PHPOffice/PhpSpreadsheet/pull/4419) ## 2025-03-02 - 4.1.0 From 30f517faaeb95d7c45aae94c239fa14868b44b94 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:49:47 -0700 Subject: [PATCH 21/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..639e99eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Add ability to add custom functions to Calculation. [PR #4390](https://github.com/PHPOffice/PhpSpreadsheet/pull/4390) - Add FormulaRange to IgnoredErrors. [PR #4393](https://github.com/PHPOffice/PhpSpreadsheet/pull/4393) +- TextGrid improvements. [PR #4418](https://github.com/PHPOffice/PhpSpreadsheet/pull/4418) ### Removed From 511f4c62776bd7fb62dfea368e9f2cfb2919f5ec Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 25 Mar 2025 20:01:33 -0700 Subject: [PATCH 22/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..2f50dea93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389) - Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) +- Fix typo in Style exportArray quotePrefix. [Issue #4422](https://github.com/PHPOffice/PhpSpreadsheet/issues/4422) [PR #4424](https://github.com/PHPOffice/PhpSpreadsheet/pull/4424) ## 2025-03-02 - 4.1.0 From a67e778b50c561139de7f5c444e914126feb768d Mon Sep 17 00:00:00 2001 From: Mikko Date: Wed, 19 Mar 2025 14:08:36 +0200 Subject: [PATCH 23/32] Conditional and table formatting support for html writer --- CHANGELOG.md | 1 + docs/topics/conditional-formatting.md | 37 ++-- docs/topics/tables.md | 16 ++ .../Html/01_Basic_Conditional_Formatting.php | 25 +++ .../Html/02_More_Conditional_Formatting.php | 25 +++ samples/Html/03_Color_Scale.php | 25 +++ .../04_Table_Format_without_Conditional.php | 25 +++ .../Html/05_Table_Format_with_Conditional.php | 27 +++ .../templates/BasicConditionalFormatting.xlsx | Bin 0 -> 9048 bytes samples/templates/ColourScale.xlsx | Bin 0 -> 8798 bytes .../ConditionalFormattingConditions.xlsx | Bin 0 -> 5588 bytes samples/templates/TableFormat.xlsx | Bin 0 -> 9586 bytes src/PhpSpreadsheet/Reader/Xlsx.php | 15 +- .../Reader/Xlsx/ConditionalStyles.php | 7 + src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 41 +++++ .../Reader/Xlsx/TableReader.php | 16 +- src/PhpSpreadsheet/Style/Conditional.php | 10 +- .../ConditionalFormatting/CellMatcher.php | 12 +- .../CellStyleAssessor.php | 29 ++- .../ConditionalColorScale.php | 164 +++++++++++++++++ src/PhpSpreadsheet/Worksheet/Table.php | 13 ++ .../Worksheet/Table/TableDxfsStyle.php | 170 ++++++++++++++++++ .../Worksheet/Table/TableStyle.php | 33 ++++ src/PhpSpreadsheet/Worksheet/Worksheet.php | 60 +++++++ src/PhpSpreadsheet/Writer/BaseWriter.php | 42 +++++ src/PhpSpreadsheet/Writer/Html.php | 102 ++++++++++- .../Writer/Html/HtmlColourScaleTest.php | 79 ++++++++ .../Html/HtmlConditionalFormattingTest.php | 65 +++++++ ...tmlDifferentConditionalFormattingsTest.php | 94 ++++++++++ .../Writer/Html/HtmlTableFormatTest.php | 64 +++++++ .../HtmlTableFormatWithConditionalTest.php | 65 +++++++ 31 files changed, 1225 insertions(+), 37 deletions(-) create mode 100644 docs/topics/tables.md create mode 100644 samples/Html/01_Basic_Conditional_Formatting.php create mode 100644 samples/Html/02_More_Conditional_Formatting.php create mode 100644 samples/Html/03_Color_Scale.php create mode 100644 samples/Html/04_Table_Format_without_Conditional.php create mode 100644 samples/Html/05_Table_Format_with_Conditional.php create mode 100644 samples/templates/BasicConditionalFormatting.xlsx create mode 100644 samples/templates/ColourScale.xlsx create mode 100644 samples/templates/ConditionalFormattingConditions.xlsx create mode 100644 samples/templates/TableFormat.xlsx create mode 100644 src/PhpSpreadsheet/Worksheet/Table/TableDxfsStyle.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatWithConditionalTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a7c0e63c0..0ca1b6c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Add FormulaRange to IgnoredErrors. [PR #4393](https://github.com/PHPOffice/PhpSpreadsheet/pull/4393) - TextGrid improvements. [PR #4418](https://github.com/PHPOffice/PhpSpreadsheet/pull/4418) - Permit read to class which extends Spreadsheet. [Discussion #4402](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4402) [PR #4404](https://github.com/PHPOffice/PhpSpreadsheet/pull/4404) +- Conditional and table formatting support for html writer [PR #4412](https://github.com/PHPOffice/PhpSpreadsheet/pull/4412) ### Removed diff --git a/docs/topics/conditional-formatting.md b/docs/topics/conditional-formatting.md index 352acbef1..735215c62 100644 --- a/docs/topics/conditional-formatting.md +++ b/docs/topics/conditional-formatting.md @@ -143,20 +143,28 @@ Currently, the following Conditional Types are supported for the following Reade MS Excel | Conditional Type | Readers | Writers ---|---|---|--- -| Cell Value | Conditional::CONDITION_CELLIS | Xlsx | Xlsx, Xls -Specific Text | Conditional::CONDITION_CONTAINSTEXT | Xlsx | Xlsx - | Conditional::CONDITION_NOTCONTAINSTEXT | Xlsx | Xlsx - | Conditional::CONDITION_BEGINSWITH | Xlsx | Xlsx - | Conditional::CONDITION_ENDSWITH | Xlsx | Xlsx -Dates Occurring | Conditional::CONDITION_TIMEPERIOD | Xlsx | Xlsx -Blanks | Conditional::CONDITION_CONTAINSBLANKS | Xlsx | Xlsx -No Blanks | Conditional::CONDITION_NOTCONTAINSBLANKS | Xlsx | Xlsx -Errors | Conditional::CONDITION_CONTAINSERRORS | Xlsx | Xlsx -No Errors | Conditional::CONDITION_NOTCONTAINSERRORS | Xlsx | Xlsx -Duplicates/Unique | Conditional::CONDITION_DUPLICATES | Xlsx | Xlsx - | Conditional::CONDITION_UNIQUE | Xlsx | Xlsx -Use a formula | Conditional::CONDITION_EXPRESSION | Xlsx | Xlsx, Xls -Data Bars | Conditional::CONDITION_DATABAR | Xlsx | Xlsx +| Cell Value | Conditional::CONDITION_CELLIS | Xlsx | Xlsx, Xls, Html +Specific Text | Conditional::CONDITION_CONTAINSTEXT | Xlsx | Xlsx, Html + | Conditional::CONDITION_NOTCONTAINSTEXT | Xlsx | Xlsx, Html + | Conditional::CONDITION_BEGINSWITH | Xlsx | Xlsx, Html + | Conditional::CONDITION_ENDSWITH | Xlsx | Xlsx, Html +Dates Occurring | Conditional::CONDITION_TIMEPERIOD | Xlsx | Xlsx, Html +Blanks | Conditional::CONDITION_CONTAINSBLANKS | Xlsx | Xlsx, Html +No Blanks | Conditional::CONDITION_NOTCONTAINSBLANKS | Xlsx | Xlsx, Html +Errors | Conditional::CONDITION_CONTAINSERRORS | Xlsx | Xlsx, Html +No Errors | Conditional::CONDITION_NOTCONTAINSERRORS | Xlsx | Xlsx, Html +Duplicates/Unique | Conditional::CONDITION_DUPLICATES | Xlsx | Xlsx, Html + | Conditional::CONDITION_UNIQUE | Xlsx | Xlsx, Html +Use a formula | Conditional::CONDITION_EXPRESSION | Xlsx | Xlsx, Xls, Html +Data Bars | Conditional::CONDITION_DATABAR | Xlsx | Xlsx, Html +Colour Scales | Conditional::COLORSCALE | Xlsx | Html + +To enable conditional formatting for Html writer, use: + +```php + $writer = new HtmlWriter($spreadsheet); + $writer->setConditionalFormatting(true); +``` The following Conditional Types are currently not supported by any Readers or Writers: @@ -165,7 +173,6 @@ MS Excel | Conditional Type Above/Below Average | ? Top/Bottom Items | ? Top/Bottom %age | ? -Colour Scales |? Icon Sets | ? Unsupported types will by ignored by the Readers, and cannot be created through PHPSpreadsheet. diff --git a/docs/topics/tables.md b/docs/topics/tables.md new file mode 100644 index 000000000..a16d036bf --- /dev/null +++ b/docs/topics/tables.md @@ -0,0 +1,16 @@ +# Tables + +## Introduction + +To make managing and analyzing a group of related data easier, you can turn a range of cells into an Excel table (previously known as an Excel list). + +## Support + +Currently tables are supported in Xlsx reader and Html Writer + +To enable table formatting for Html writer, use: + +```php + $writer = new HtmlWriter($spreadsheet); + $writer->setConditionalFormatting(true); +``` \ No newline at end of file diff --git a/samples/Html/01_Basic_Conditional_Formatting.php b/samples/Html/01_Basic_Conditional_Formatting.php new file mode 100644 index 000000000..c2a1efd1a --- /dev/null +++ b/samples/Html/01_Basic_Conditional_Formatting.php @@ -0,0 +1,25 @@ +isCli() ? ('samples/templates/' . $inputFileName) : ('' . 'samples/templates/' . $inputFileName . ''); +$helper->log('Read ' . $codePath . ' with conditional formatting'); +$reader = IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(false); +$spreadsheet = $reader->load($inputFilePath); +$helper->log('Enable conditional formatting output'); + +function writerCallback(HtmlWriter $writer): void +{ + $writer->setPreCalculateFormulas(true); + $writer->setConditionalFormatting(true); +} + +// Save +$helper->write($spreadsheet, __FILE__, ['Html'], false, writerCallback: writerCallback(...)); diff --git a/samples/Html/02_More_Conditional_Formatting.php b/samples/Html/02_More_Conditional_Formatting.php new file mode 100644 index 000000000..b8971f0e3 --- /dev/null +++ b/samples/Html/02_More_Conditional_Formatting.php @@ -0,0 +1,25 @@ +isCli() ? ('samples/templates/' . $inputFileName) : ('' . 'samples/templates/' . $inputFileName . ''); +$helper->log('Read ' . $codePath . ' with conditional formatting'); +$reader = IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(false); +$spreadsheet = $reader->load($inputFilePath); +$helper->log('Enable conditional formatting output'); + +function writerCallback(HtmlWriter $writer): void +{ + $writer->setPreCalculateFormulas(true); + $writer->setConditionalFormatting(true); +} + +// Save +$helper->write($spreadsheet, __FILE__, ['Html'], false, writerCallback: writerCallback(...)); diff --git a/samples/Html/03_Color_Scale.php b/samples/Html/03_Color_Scale.php new file mode 100644 index 000000000..ed2296bfb --- /dev/null +++ b/samples/Html/03_Color_Scale.php @@ -0,0 +1,25 @@ +isCli() ? ('samples/templates/' . $inputFileName) : ('' . 'samples/templates/' . $inputFileName . ''); +$helper->log('Read ' . $codePath . ' with color scale'); +$reader = IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(false); +$spreadsheet = $reader->load($inputFilePath); +$helper->log('Enable conditional formatting output'); + +function writerCallback(HtmlWriter $writer): void +{ + $writer->setPreCalculateFormulas(true); + $writer->setConditionalFormatting(true); +} + +// Save +$helper->write($spreadsheet, __FILE__, ['Html'], false, writerCallback: writerCallback(...)); diff --git a/samples/Html/04_Table_Format_without_Conditional.php b/samples/Html/04_Table_Format_without_Conditional.php new file mode 100644 index 000000000..396af5375 --- /dev/null +++ b/samples/Html/04_Table_Format_without_Conditional.php @@ -0,0 +1,25 @@ +isCli() ? ('samples/templates/' . $inputFileName) : ('' . 'samples/templates/' . $inputFileName . ''); +$helper->log('Read ' . $codePath); +$reader = IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(false); +$spreadsheet = $reader->load($inputFilePath); +$helper->log('Enable table formatting output'); + +function writerCallback(HtmlWriter $writer): void +{ + $writer->setPreCalculateFormulas(true); + $writer->setTableFormats(true); +} + +// Save +$helper->write($spreadsheet, __FILE__, ['Html'], false, writerCallback: writerCallback(...)); diff --git a/samples/Html/05_Table_Format_with_Conditional.php b/samples/Html/05_Table_Format_with_Conditional.php new file mode 100644 index 000000000..bdfa6ab88 --- /dev/null +++ b/samples/Html/05_Table_Format_with_Conditional.php @@ -0,0 +1,27 @@ +isCli() ? ('samples/templates/' . $inputFileName) : ('' . 'samples/templates/' . $inputFileName . ''); +$helper->log('Read ' . $codePath); +$reader = IOFactory::createReader('Xlsx'); +$reader->setReadDataOnly(false); +$spreadsheet = $reader->load($inputFilePath); +$helper->log('Enable table formatting output'); +$helper->log('Enable conditional formatting output'); + +function writerCallback(HtmlWriter $writer): void +{ + $writer->setPreCalculateFormulas(true); + $writer->setTableFormats(true); + $writer->setConditionalFormatting(true); +} + +// Save +$helper->write($spreadsheet, __FILE__, ['Html'], false, writerCallback: writerCallback(...)); diff --git a/samples/templates/BasicConditionalFormatting.xlsx b/samples/templates/BasicConditionalFormatting.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..962c0583f3e7ddbb450d6ed72b3ecc7fd2ed5812 GIT binary patch literal 9048 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 MoQ5e8mb0_}2PZ?#oB#j- literal 0 HcmV?d00001 diff --git a/samples/templates/ColourScale.xlsx b/samples/templates/ColourScale.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..606f964dbdec040b628f43c6408cf1fd6fb3055e GIT binary patch literal 8798 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 $this->styleReader->setStyleBaseData($theme, $styles, $cellStyles); $dxfs = $this->styleReader->dxfs($this->readDataOnly); + $tableStyles = $this->styleReader->tableStyles($this->readDataOnly); $styles = $this->styleReader->styles(); // Read content after setting the styles @@ -1000,7 +1001,7 @@ class Xlsx extends BaseReader $this->readBackgroundImage($xmlSheetNS, $docSheet, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels'); } - $this->readTables($xmlSheetNS, $docSheet, $dir, $fileWorksheet, $zip, $mainNS); + $this->readTables($xmlSheetNS, $docSheet, $dir, $fileWorksheet, $zip, $mainNS, $tableStyles, $dxfs); if ($xmlSheetNS && $xmlSheetNS->mergeCells && $xmlSheetNS->mergeCells->mergeCell && !$this->readDataOnly) { foreach ($xmlSheetNS->mergeCells->mergeCell as $mergeCellx) { @@ -2311,12 +2312,14 @@ class Xlsx extends BaseReader string $dir, string $fileWorksheet, ZipArchive $zip, - string $namespaceTable + string $namespaceTable, + array $tableStyles, + array $dxfs ): void { if ($xmlSheet && $xmlSheet->tableParts) { $attributes = $xmlSheet->tableParts->attributes() ?? ['count' => 0]; if (((int) $attributes['count']) > 0) { - $this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet, $namespaceTable); + $this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet, $namespaceTable, $tableStyles, $dxfs); } } } @@ -2327,7 +2330,9 @@ class Xlsx extends BaseReader string $fileWorksheet, ZipArchive $zip, Worksheet $docSheet, - string $namespaceTable + string $namespaceTable, + array $tableStyles, + array $dxfs ): void { foreach ($xmlSheet->tableParts->tablePart as $tablePart) { $relation = self::getAttributes($tablePart, Namespaces::SCHEMA_OFFICE_DOCUMENT); @@ -2346,7 +2351,7 @@ class Xlsx extends BaseReader if ($this->fileExistsInArchive($this->zip, $relationshipFilePath)) { $tableXml = $this->loadZip($relationshipFilePath, $namespaceTable); - (new TableReader($docSheet, $tableXml))->load(); + (new TableReader($docSheet, $tableXml))->load($tableStyles, $dxfs); } } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index a03fa71b2..436d9ffb8 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -193,6 +193,13 @@ class ConditionalStyles // N.B. In Excel UI, intersection is space and union is comma. // But in Xml, intersection is comma and union is space. $cellRangeReference = str_replace(['$', ' ', ',', '^'], ['', '^', ' ', ','], strtoupper($cellRangeReference)); + + foreach ($conditionalStyles as $cs) { + $scale = $cs->getColorScale(); + if ($scale !== null) { + $scale->setSqRef($cellRangeReference, $worksheet); + } + } $worksheet->getStyle($cellRangeReference)->setConditionalStyles($conditionalStyles); } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php index 676ea8176..c1e27e9e3 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php @@ -12,6 +12,7 @@ use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Style\Style; +use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableDxfsStyle; use SimpleXMLElement; use stdClass; @@ -447,6 +448,46 @@ class Styles extends BaseParserClass return $dxfs; } + // get TableStyles + public function tableStyles(bool $readDataOnly = false): array + { + $tableStyles = []; + if (!$readDataOnly && $this->styleXml) { + // Conditional Styles + if ($this->styleXml->tableStyles) { + foreach ($this->styleXml->tableStyles->tableStyle as $s) { + $attrs = Xlsx::getAttributes($s); + if (isset($attrs['name'][0])) { + $style = new TableDxfsStyle((string) ($attrs['name'][0])); + foreach ($s->tableStyleElement as $e) { + $a = Xlsx::getAttributes($e); + if (isset($a['dxfId'][0], $a['type'][0])) { + switch ($a['type'][0]) { + case 'headerRow': + $style->setHeaderRow((int) ($a['dxfId'][0])); + + break; + case 'firstRowStripe': + $style->setFirstRowStripe((int) ($a['dxfId'][0])); + + break; + case 'secondRowStripe': + $style->setSecondRowStripe((int) ($a['dxfId'][0])); + + break; + default: + } + } + } + $tableStyles[] = $style; + } + } + } + } + + return $tableStyles; + } + public function styles(): array { return $this->styles; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php b/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php index a63c817d4..c84b8198f 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php @@ -25,20 +25,20 @@ class TableReader /** * Loads Table into the Worksheet. */ - public function load(): void + public function load(array $tableStyles, array $dxfs): void { $this->tableAttributes = $this->tableXml->attributes() ?? []; // Remove all "$" in the table range $tableRange = (string) preg_replace('/\$/', '', $this->tableAttributes['ref'] ?? ''); if (str_contains($tableRange, ':')) { - $this->readTable($tableRange); + $this->readTable($tableRange, $tableStyles, $dxfs); } } /** * Read Table from xml. */ - private function readTable(string $tableRange): void + private function readTable(string $tableRange, array $tableStyles, array $dxfs): void { $table = new Table($tableRange); $table->setName((string) ($this->tableAttributes['displayName'] ?? '')); @@ -47,7 +47,7 @@ class TableReader $this->readTableAutoFilter($table, $this->tableXml->autoFilter); $this->readTableColumns($table, $this->tableXml->tableColumns); - $this->readTableStyle($table, $this->tableXml->tableStyleInfo); + $this->readTableStyle($table, $this->tableXml->tableStyleInfo, $tableStyles, $dxfs); (new AutoFilter($table, $this->tableXml))->load(); $this->worksheet->addTable($table); @@ -100,7 +100,7 @@ class TableReader /** * Reads TableStyle from xml. */ - private function readTableStyle(Table $table, SimpleXMLElement $tableStyleInfoXml): void + private function readTableStyle(Table $table, SimpleXMLElement $tableStyleInfoXml, array $tableStyles, array $dxfs): void { $tableStyle = new TableStyle(); $attributes = $tableStyleInfoXml->attributes(); @@ -110,6 +110,12 @@ class TableReader $tableStyle->setShowColumnStripes((string) $attributes['showColumnStripes'] === '1'); $tableStyle->setShowFirstColumn((string) $attributes['showFirstColumn'] === '1'); $tableStyle->setShowLastColumn((string) $attributes['showLastColumn'] === '1'); + + foreach ($tableStyles as $style) { + if ($style->getName() === (string) $attributes['name']) { + $tableStyle->setTableDxfsStyle($style, $dxfs); + } + } } $table->setStyle($tableStyle); } diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index d476bdffd..736b72be5 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -269,8 +269,16 @@ class Conditional implements IComparable /** * Get Style. */ - public function getStyle(): Style + public function getStyle(mixed $cellData = null): Style { + if ($this->conditionType === self::CONDITION_COLORSCALE && $cellData !== null && $this->colorScale !== null && is_numeric($cellData)) { + $style = new Style(); + $style->getFill()->setFillType(Fill::FILL_SOLID); + $style->getFill()->getStartColor()->setARGB($this->colorScale->getColorForValue((float) $cellData)); + + return $style; + } + return $this->style; } diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php index 61027975a..6b6e59965 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php @@ -111,6 +111,7 @@ class CellMatcher // Last 7 Days AND(TODAY()-FLOOR(,1)<=6,FLOOR(,1)<=TODAY()) Conditional::CONDITION_TIMEPERIOD, Conditional::CONDITION_EXPRESSION => $this->processExpression($conditional), + Conditional::CONDITION_COLORSCALE => $this->processColorScale($conditional), default => false, }; } @@ -141,8 +142,8 @@ class CellMatcher { $column = $matches[6]; $row = $matches[7]; - if (!str_contains($column, '$')) { + // $column = Coordinate::stringFromColumnIndex($this->cellColumn); $column = Coordinate::columnIndexFromString($column); $column += $this->cellColumn - $this->referenceColumn; $column = Coordinate::stringFromColumnIndex($column); @@ -214,6 +215,15 @@ class CellMatcher return $this->evaluateExpression($expression); } + protected function processColorScale(Conditional $conditional): bool + { + if (is_numeric($this->wrapCellValue()) && $conditional->getColorScale()?->colorScaleReadyForUse()) { + return true; + } + + return false; + } + protected function processRangeOperator(Conditional $conditional): bool { $conditions = $this->adjustConditionsForCellReferences($conditional->getConditions()); diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php index bcf59dee8..f8826f05f 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php @@ -12,8 +12,11 @@ class CellStyleAssessor protected StyleMerger $styleMerger; + protected Cell $cell; + public function __construct(Cell $cell, string $conditionalRange) { + $this->cell = $cell; $this->cellMatcher = new CellMatcher($cell, $conditionalRange); $this->styleMerger = new StyleMerger($cell->getStyle()); } @@ -26,7 +29,7 @@ class CellStyleAssessor foreach ($conditionalStyles as $conditional) { if ($this->cellMatcher->evaluateConditional($conditional) === true) { // Merging the conditional style into the base style goes in here - $this->styleMerger->mergeStyle($conditional->getStyle()); + $this->styleMerger->mergeStyle($conditional->getStyle($this->cell->getValue())); if ($conditional->getStopIfTrue() === true) { break; } @@ -35,4 +38,28 @@ class CellStyleAssessor return $this->styleMerger->getStyle(); } + + /** + * @param Conditional[] $conditionalStyles + */ + public function matchConditionsReturnNullIfNoneMatched(array $conditionalStyles, string $cellData, bool $stopAtFirstMatch = false): ?Style + { + $matched = false; + $value = (float) $cellData; + foreach ($conditionalStyles as $conditional) { + if ($this->cellMatcher->evaluateConditional($conditional) === true) { + $matched = true; + // Merging the conditional style into the base style goes in here + $this->styleMerger->mergeStyle($conditional->getStyle($value)); + if ($conditional->getStopIfTrue() === true || $stopAtFirstMatch) { + break; + } + } + } + if ($matched) { + return $this->styleMerger->getStyle(); + } + + return null; + } } diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php index 7fcc08038..e11abd122 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting; +use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Percentiles; use PhpOffice\PhpSpreadsheet\Style\Color; class ConditionalColorScale @@ -18,6 +19,18 @@ class ConditionalColorScale private ?Color $maximumColor = null; + private ?string $sqref = null; + + private array $valueArray = []; + + private float $minValue = 0; + + private float $maxValue = 0; + + private float $midValue = 0; + + private ?\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet = null; + public function getMinimumConditionalFormatValueObject(): ?ConditionalFormatValueObject { return $this->minimumConditionalFormatValueObject; @@ -89,4 +102,155 @@ class ConditionalColorScale return $this; } + + public function getSqRef(): ?string + { + return $this->sqref; + } + + public function setSqRef(string $sqref, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): self + { + $this->sqref = $sqref; + $this->worksheet = $worksheet; + + return $this; + } + + public function setScaleArray(): self + { + if ($this->sqref !== null && $this->worksheet !== null) { + $values = $this->worksheet->rangesToArray($this->sqref, null, true, true, true); + $this->valueArray = []; + foreach ($values as $key => $value) { + foreach ($value as $k => $v) { + $this->valueArray[] = (float) $v; + } + } + $this->prepareColorScale(); + } + + return $this; + } + + public function getColorForValue(float $value): string + { + if ($this->minimumColor === null || $this->midpointColor === null || $this->maximumColor === null) { + return 'FF000000'; + } + $minColor = $this->minimumColor->getARGB(); + $midColor = $this->midpointColor->getARGB(); + $maxColor = $this->maximumColor->getARGB(); + + if ($minColor === null || $midColor === null || $maxColor === null) { + return 'FF000000'; + } + + if ($value <= $this->minValue) { + return $minColor; + } + if ($value >= $this->maxValue) { + return $maxColor; + } + if ($value == $this->midValue) { + return $midColor; + } + if ($value < $this->midValue) { + $blend = ($value - $this->minValue) / ($this->midValue - $this->minValue); + $alpha1 = hexdec(substr($minColor, 0, 2)); + $alpha2 = hexdec(substr($midColor, 0, 2)); + $red1 = hexdec(substr($minColor, 2, 2)); + $red2 = hexdec(substr($midColor, 2, 2)); + $green1 = hexdec(substr($minColor, 4, 2)); + $green2 = hexdec(substr($midColor, 4, 2)); + $blue1 = hexdec(substr($minColor, 6, 2)); + $blue2 = hexdec(substr($midColor, 6, 2)); + + return strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend)))); + } + $blend = ($value - $this->midValue) / ($this->maxValue - $this->midValue); + $alpha1 = hexdec(substr($midColor, 0, 2)); + $alpha2 = hexdec(substr($maxColor, 0, 2)); + $red1 = hexdec(substr($midColor, 2, 2)); + $red2 = hexdec(substr($maxColor, 2, 2)); + $green1 = hexdec(substr($midColor, 4, 2)); + $green2 = hexdec(substr($maxColor, 4, 2)); + $blue1 = hexdec(substr($midColor, 6, 2)); + $blue2 = hexdec(substr($maxColor, 6, 2)); + + return strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend)))); + } + + private function getLimitValue(string $type, float $value = 0, float $formula = 0): float + { + if (count($this->valueArray) === 0) { + return 0; + } + switch ($type) { + case 'min': + return (float) min($this->valueArray); + case 'max': + return (float) max($this->valueArray); + case 'percentile': + return (float) Percentiles::PERCENTILE($this->valueArray, (float) ($value / 100)); + case 'formula': + return $formula; + case 'percent': + $min = (float) min($this->valueArray); + $max = (float) max($this->valueArray); + + return $min + (float) ($value / 100) * ($max - $min); + default: + return 0; + } + } + + /** + * Prepares color scale for execution, see the first if for variables that must be set beforehand. + */ + public function prepareColorScale(): self + { + if ($this->minimumConditionalFormatValueObject !== null && $this->maximumConditionalFormatValueObject !== null && $this->minimumColor !== null && $this->maximumColor !== null) { + if ($this->midpointConditionalFormatValueObject !== null && $this->midpointConditionalFormatValueObject->getType() !== 'None') { + $this->minValue = $this->getLimitValue($this->minimumConditionalFormatValueObject->getType(), (float) $this->minimumConditionalFormatValueObject->getValue(), (float) $this->minimumConditionalFormatValueObject->getCellFormula()); + $this->midValue = $this->getLimitValue($this->midpointConditionalFormatValueObject->getType(), (float) $this->midpointConditionalFormatValueObject->getValue(), (float) $this->midpointConditionalFormatValueObject->getCellFormula()); + $this->maxValue = $this->getLimitValue($this->maximumConditionalFormatValueObject->getType(), (float) $this->maximumConditionalFormatValueObject->getValue(), (float) $this->maximumConditionalFormatValueObject->getCellFormula()); + } else { + $this->minValue = $this->getLimitValue($this->minimumConditionalFormatValueObject->getType(), (float) $this->minimumConditionalFormatValueObject->getValue(), (float) $this->minimumConditionalFormatValueObject->getCellFormula()); + $this->maxValue = $this->getLimitValue($this->maximumConditionalFormatValueObject->getType(), (float) $this->maximumConditionalFormatValueObject->getValue(), (float) $this->maximumConditionalFormatValueObject->getCellFormula()); + $this->midValue = (float) ($this->minValue + $this->maxValue) / 2; + $blend = 0.5; + + $minColor = $this->minimumColor->getARGB(); + $maxColor = $this->maximumColor->getARGB(); + + if ($minColor !== null && $maxColor !== null) { + $alpha1 = hexdec(substr($minColor, 0, 2)); + $alpha2 = hexdec(substr($maxColor, 0, 2)); + $red1 = hexdec(substr($minColor, 2, 2)); + $red2 = hexdec(substr($maxColor, 2, 2)); + $green1 = hexdec(substr($minColor, 4, 2)); + $green2 = hexdec(substr($maxColor, 4, 2)); + $blue1 = hexdec(substr($minColor, 6, 2)); + $blue2 = hexdec(substr($maxColor, 6, 2)); + $this->midpointColor = new Color(strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend))))); + } else { + $this->midpointColor = null; + } + } + } + + return $this; + } + + /** + * Checks that all needed color scale data is in place. + */ + public function colorScaleReadyForUse(): bool + { + if ($this->minimumColor === null || $this->midpointColor === null || $this->maximumColor === null) { + return false; + } + + return true; + } } diff --git a/src/PhpSpreadsheet/Worksheet/Table.php b/src/PhpSpreadsheet/Worksheet/Table.php index 7f5b876ee..072beab2d 100644 --- a/src/PhpSpreadsheet/Worksheet/Table.php +++ b/src/PhpSpreadsheet/Worksheet/Table.php @@ -540,6 +540,19 @@ class Table implements Stringable return $this; } + /** + * Get the row number on this table for given coordinates. + */ + public function getRowNumber(string $coordinate): int + { + $range = $this->getRange(); + $coords = Coordinate::splitRange($range); + $firstCell = Coordinate::coordinateFromString($coords[0][0]); + $thisCell = Coordinate::coordinateFromString($coordinate); + + return (int) $thisCell[1] - (int) $firstCell[1]; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ diff --git a/src/PhpSpreadsheet/Worksheet/Table/TableDxfsStyle.php b/src/PhpSpreadsheet/Worksheet/Table/TableDxfsStyle.php new file mode 100644 index 000000000..e674a7146 --- /dev/null +++ b/src/PhpSpreadsheet/Worksheet/Table/TableDxfsStyle.php @@ -0,0 +1,170 @@ +name = $name; + } + + /** + * Get name. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Set header row dxfs index. + */ + public function setHeaderRow(int $row): self + { + $this->headerRow = $row; + + return $this; + } + + /** + * Get header row dxfs index. + */ + public function getHeaderRow(): ?int + { + return $this->headerRow; + } + + /** + * Set first row stripe dxfs index. + */ + public function setFirstRowStripe(int $row): self + { + $this->firstRowStripe = $row; + + return $this; + } + + /** + * Get first row stripe dxfs index. + */ + public function getFirstRowStripe(): ?int + { + return $this->firstRowStripe; + } + + /** + * Set second row stripe dxfs index. + */ + public function setSecondRowStripe(int $row): self + { + $this->secondRowStripe = $row; + + return $this; + } + + /** + * Get second row stripe dxfs index. + */ + public function getSecondRowStripe(): ?int + { + return $this->secondRowStripe; + } + + /** + * Set Header row Style. + */ + public function setHeaderRowStyle(Style $style): self + { + $this->headerRowStyle = $style; + + return $this; + } + + /** + * Get Header row Style. + */ + public function getHeaderRowStyle(): ?Style + { + return $this->headerRowStyle; + } + + /** + * Set first row stripe Style. + */ + public function setFirstRowStripeStyle(Style $style): self + { + $this->firstRowStripeStyle = $style; + + return $this; + } + + /** + * Get first row stripe Style. + */ + public function getFirstRowStripeStyle(): ?Style + { + return $this->firstRowStripeStyle; + } + + /** + * Set second row stripe Style. + */ + public function setSecondRowStripeStyle(Style $style): self + { + $this->secondRowStripeStyle = $style; + + return $this; + } + + /** + * Get second row stripe Style. + */ + public function getSecondRowStripeStyle(): ?Style + { + return $this->secondRowStripeStyle; + } +} diff --git a/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php b/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php index 81153027d..2c0173c19 100644 --- a/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php +++ b/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php @@ -93,6 +93,11 @@ class TableStyle */ private bool $showColumnStripes = false; + /** + * TableDxfsStyle. + */ + private ?TableDxfsStyle $tableStyle = null; + /** * Table. */ @@ -198,6 +203,34 @@ class TableStyle return $this; } + /** + * Get this Style's Dxfs TableStyle. + */ + public function getTableDxfsStyle(): ?TableDxfsStyle + { + return $this->tableStyle; + } + + /** + * Set this Style's Dxfs TableStyle. + */ + public function setTableDxfsStyle(TableDxfsStyle $tableStyle, array $dxfs): self + { + $this->tableStyle = $tableStyle; + + if ($this->tableStyle->getHeaderRow() !== null && isset($dxfs[$this->tableStyle->getHeaderRow()])) { + $this->tableStyle->setHeaderRowStyle($dxfs[$this->tableStyle->getHeaderRow()]); + } + if ($this->tableStyle->getFirstRowStripe() !== null && isset($dxfs[$this->tableStyle->getFirstRowStripe()])) { + $this->tableStyle->setFirstRowStripeStyle($dxfs[$this->tableStyle->getFirstRowStripe()]); + } + if ($this->tableStyle->getSecondRowStripe() !== null && isset($dxfs[$this->tableStyle->getSecondRowStripe()])) { + $this->tableStyle->setSecondRowStripeStyle($dxfs[$this->tableStyle->getSecondRowStripe()]); + } + + return $this; + } + /** * Get this Style's Table. */ diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index daa1853bb..60bf3b12a 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -1414,6 +1414,32 @@ class Worksheet return $this->getParentOrThrow()->getCellXfSupervisor(); } + /** + * Get table styles set for the for given cell. + * + * @param Cell $cell + * The Cell for which the tables are retrieved + */ + public function getTablesWithStylesForCell(Cell $cell): array + { + $retVal = []; + + foreach ($this->tableCollection as $table) { + /** @var Table $table */ + $dxfsTableStyle = $table->getStyle()->getTableDxfsStyle(); + if ($dxfsTableStyle !== null) { + if ($dxfsTableStyle->getHeaderRowStyle() !== null || $dxfsTableStyle->getFirstRowStripeStyle() !== null || $dxfsTableStyle->getSecondRowStripeStyle() !== null) { + $range = $table->getRange(); + if ($cell->isInRange($range)) { + $retVal[] = $table; + } + } + } + } + + return $retVal; + } + /** * Get conditional styles for a cell. * @@ -2888,6 +2914,40 @@ class Worksheet return $returnValue; } + /** + * Create array from a multiple ranges of cells. (such as A1:A3,A15,B17:C17). + * + * @param null|bool|float|int|RichText|string $nullValue Value returned in the array entry if a cell doesn't exist + * @param bool $calculateFormulas Should formulas be calculated? + * @param bool $formatData Should formatting be applied to cell values? + * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero + * True - Return rows and columns indexed by their actual row and column IDs + * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden. + * True - Don't return values for rows/columns that are defined as hidden. + */ + public function rangesToArray( + string $ranges, + mixed $nullValue = null, + bool $calculateFormulas = true, + bool $formatData = true, + bool $returnCellRef = false, + bool $ignoreHidden = false, + bool $reduceArrays = false + ): array { + $returnValue = []; + + $parts = explode(',', $ranges); + foreach ($parts as $part) { + // Loop through rows + foreach ($this->rangeToArrayYieldRows($part, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays) as $rowRef => $rowArray) { + $returnValue[$rowRef] = $rowArray; + } + } + + // Return + return $returnValue; + } + /** * Create array from a range of cells, yielding each row in turn. * diff --git a/src/PhpSpreadsheet/Writer/BaseWriter.php b/src/PhpSpreadsheet/Writer/BaseWriter.php index 5e6d3cd49..50df5b167 100644 --- a/src/PhpSpreadsheet/Writer/BaseWriter.php +++ b/src/PhpSpreadsheet/Writer/BaseWriter.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Writer; +use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; + abstract class BaseWriter implements IWriter { /** @@ -17,6 +19,18 @@ abstract class BaseWriter implements IWriter */ protected bool $preCalculateFormulas = true; + /** + * Table formats + * Enables table formats in writer, disabled here, must be enabled in writer via a setter. + */ + protected bool $tableFormats = false; + + /** + * Conditional Formatting + * Enables conditional formatting in writer, disabled here, must be enabled in writer via a setter. + */ + protected bool $conditionalFormatting = false; + /** * Use disk caching where possible? */ @@ -58,6 +72,34 @@ abstract class BaseWriter implements IWriter return $this; } + public function getTableFormats(): bool + { + return $this->tableFormats; + } + + public function setTableFormats(bool $tableFormats): self + { + if ($tableFormats) { + throw new PhpSpreadsheetException('Table formatting not implemented for this writer'); + } + + return $this; + } + + public function getConditionalFormatting(): bool + { + return $this->conditionalFormatting; + } + + public function setConditionalFormatting(bool $conditionalFormatting): self + { + if ($conditionalFormatting) { + throw new PhpSpreadsheetException('Conditional Formatting not implemented for this writer'); + } + + return $this; + } + public function getUseDiskCaching(): bool { return $this->useDiskCaching; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index bc26c0ff2..4d7bfcafb 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer; use Composer\Pcre\Preg; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; @@ -22,6 +23,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Borders; +use PhpOffice\PhpSpreadsheet\Style\Conditional; +use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellStyleAssessor; +use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\StyleMerger; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; @@ -162,13 +166,10 @@ class Html extends BaseWriter public function save($filename, int $flags = 0): void { $this->processFlags($flags); - // Open file $this->openFileHandle($filename); - // Write html fwrite($this->fileHandle, $this->generateHTMLAll()); - // Close file $this->maybeCloseFileHandle(); } @@ -473,12 +474,24 @@ class Html extends BaseWriter // Loop all sheets $sheetId = 0; + + $activeSheet = $this->spreadsheet->getActiveSheetIndex(); + foreach ($sheets as $sheet) { + // save active cells + $selectedCells = $sheet->getSelectedCells(); // Write table header $html .= $this->generateTableHeader($sheet); $this->sheetCharts = []; $this->sheetDrawings = []; - + $condStylesCollection = $sheet->getConditionalStylesCollection(); + foreach ($condStylesCollection as $condStyles) { + foreach ($condStyles as $key => $cs) { + if ($cs->getConditionType() === Conditional::CONDITION_COLORSCALE) { + $cs->getColorScale()->setScaleArray(); + } + } + } // Get worksheet dimension [$min, $max] = explode(':', $sheet->calculateWorksheetDataDimension()); [$minCol, $minRow, $minColString] = Coordinate::indexesFromString($min); @@ -486,7 +499,6 @@ class Html extends BaseWriter $this->extendRowsAndColumns($sheet, $maxCol, $maxRow); [$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $minRow); - // Loop through cells $row = $minRow - 1; while ($row++ < $maxRow) { @@ -514,7 +526,6 @@ class Html extends BaseWriter $html .= $endTag; } - // Write table footer $html .= $this->generateTableFooter(); // Writing PDF? @@ -526,7 +537,9 @@ class Html extends BaseWriter // Next sheet ++$sheetId; + $sheet->setSelectedCells($selectedCells); } + $this->spreadsheet->setActiveSheetIndex($activeSheet); return $html; } @@ -1356,7 +1369,11 @@ class Html extends BaseWriter $cellData .= $this->generateRowCellDataValueRich($cell->getValue()); } else { if ($this->preCalculateFormulas) { - $origData = $cell->getCalculatedValue(); + try { + $origData = $cell->getCalculatedValue(); + } catch (CalculationException $exception) { + $origData = '#ERROR'; // mark as error, rather than crash everything + } if ($this->betterBoolean && is_bool($origData)) { $origData2 = $origData ? $this->getTrue : $this->getFalse; } else { @@ -1473,7 +1490,8 @@ class Html extends BaseWriter array|string $cssClass, int $colNum, int $sheetIndex, - int $row + int $row, + array $condStyles = [] ): void { // Image? $htmlx = $this->writeImageInCell($coordinate); @@ -1540,8 +1558,57 @@ class Html extends BaseWriter $html .= ' class="gridlines gridlinesp"'; } } + $html = $this->generateRowSpans($html, $rowSpan, $colSpan); + $tables = $worksheet->getTablesWithStylesForCell($worksheet->getCell($coordinate)); + if (count($tables) > 0 || count($condStyles) > 0) { + $matched = false; // TODO the style gotten from the merger overrides everything + $styleMerger = new StyleMerger($worksheet->getCell($coordinate)->getStyle()); + if ($this->tableFormats) { + if (count($tables) > 0) { + foreach ($tables as $ts) { + $dxfsTableStyle = $ts->getStyle()->getTableDxfsStyle(); + if ($dxfsTableStyle !== null) { + $tableRow = $ts->getRowNumber($coordinate); + if ($tableRow === 0 && $dxfsTableStyle->getHeaderRowStyle() !== null) { + $styleMerger->mergeStyle($dxfsTableStyle->getHeaderRowStyle()); + $matched = true; + } elseif ($tableRow % 2 === 1 && $dxfsTableStyle->getFirstRowStripeStyle() !== null) { + $styleMerger->mergeStyle($dxfsTableStyle->getFirstRowStripeStyle()); + $matched = true; + } elseif ($tableRow % 2 === 0 && $dxfsTableStyle->getSecondRowStripeStyle() !== null) { + $styleMerger->mergeStyle($dxfsTableStyle->getSecondRowStripeStyle()); + $matched = true; + } + } + } + } + } + if (count($condStyles) > 0 && $this->conditionalFormatting) { + if ($worksheet->getConditionalRange($coordinate) !== null) { + $assessor = new CellStyleAssessor($worksheet->getCell($coordinate), $worksheet->getConditionalRange($coordinate)); + } else { + $assessor = new CellStyleAssessor($worksheet->getCell($coordinate), $coordinate); + } + $matchedStyle = $assessor->matchConditionsReturnNullIfNoneMatched($condStyles, $cellData, true); + + if ($matchedStyle !== null) { + $matched = true; + // this is really slow + $styleMerger->mergeStyle($matchedStyle); + } + } + if ($matched) { + $styles = $this->createCSSStyle($styleMerger->getStyle()); + $html .= ' style="'; + foreach ($styles as $key => $value) { + $html .= $key . ':' . $value . ';'; + } + $html .= '"'; + } + } + $html .= '>'; $html .= $htmlx; @@ -1587,6 +1654,9 @@ class Html extends BaseWriter // Cell Data $cellData = $this->generateRowCellData($worksheet, $cell, $cssClass); + // Get an array of all styles + $condStyles = $worksheet->getStyle($coordinate)->getConditionalStyles(); + // Hyperlink? if ($worksheet->hyperlinkExists($coordinate) && !$worksheet->getHyperlink($coordinate)->isInternal()) { $url = $worksheet->getHyperlink($coordinate)->getUrl(); @@ -1636,7 +1706,7 @@ class Html extends BaseWriter // Write if ($writeCell) { - $this->generateRowWriteCell($html, $worksheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $row); + $this->generateRowWriteCell($html, $worksheet, $coordinate, $cellType, $cellData, $colSpan, $rowSpan, $cssClass, $colNum, $sheetIndex, $row, $condStyles); } // Next column @@ -1738,6 +1808,20 @@ class Html extends BaseWriter return $this; } + public function setTableFormats(bool $tableFormats): self + { + $this->tableFormats = $tableFormats; + + return $this; + } + + public function setConditionalFormatting(bool $conditionalFormatting): self + { + $this->conditionalFormatting = $conditionalFormatting; + + return $this; + } + /** * Add color to formatted string as inline style. * diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php new file mode 100644 index 000000000..979e75bb0 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php @@ -0,0 +1,79 @@ +load($file); + $writer = new HtmlWriter($spreadsheet); + $writer->setConditionalFormatting(true); + $this->data = $writer->generateHtmlAll(); + $spreadsheet->disconnectWorksheets(); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('colourScaleProvider')] + public function testColourScaleHtmlOutput(int $rowNumber, array $expectedMatches): void + { + self::assertSame(1, preg_match('~~ms', $this->data, $matches)); + foreach ($expectedMatches as $i => $expected) { + self::assertStringContainsString($expected, $matches[0]); + } + } + + public static function colourScaleProvider(): array + { + return [ + 'row 0: low/high min/max with 80% midpoint' => [0, ['1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10']], + 'row 1: low/high 40%/80% with 50% midpoint' => [1, ['1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10']], + 'row 2: low/high/midpoint values 3/8/4 ' => [2, ['1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10']], + 'row 3: low/high with 30/80 percentile and 50% midpoint, one cell no value' => [3, ['1', + '2', + '3', + '4', + '2', + '9', + '9', + '9', + '', + '10']]]; + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php new file mode 100644 index 000000000..0339248ca --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php @@ -0,0 +1,65 @@ +load($file); + $writer = new HtmlWriter($spreadsheet); + $writer->setConditionalFormatting(true); + $this->data = $writer->generateHtmlAll(); + $spreadsheet->disconnectWorksheets(); + } + + private function extractCell(string $coordinate): string + { + [$column, $row] = Coordinate::indexesFromString($coordinate); + --$column; + --$row; + // extract row into $matches + $match = preg_match('~~s', $this->data, $matches); + if ($match !== 1) { + return 'unable to match row'; + } + $rowData = $matches[0]; + // extract cell into $matches + $match = preg_match('~Jan<', 'no conditional styling for B1'], + ['F2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">120<', 'conditional style for F2'], + ['H2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C5700;font-family:\'Arial\';font-size:11pt;background-color:#FFEB9C;">90<', 'conditional style for H2'], + ['F3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">70<', 'conditional style for cell F3'], + ['H3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C5700;font-family:\'Arial\';font-size:11pt;background-color:#FFEB9C;">60<', 'conditional style for cell H3'], + ['F4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">1<', 'conditional style for cell F4'], + ['L4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C0006;font-family:\'Arial\';font-size:11pt;background-color:#FFC7CE;">5<', 'conditional style for cell L4'], + ['F5', 'class="column5 style1 n">0<', 'no conditional styling for F5'], + ]; + foreach ($expectedMatches as $expected) { + [$coordinate, $expectedString, $message] = $expected; + $string = $this->extractCell($coordinate); + self::assertStringContainsString($expectedString, $string, $message); + } + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php new file mode 100644 index 000000000..38b7e976d --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php @@ -0,0 +1,94 @@ +load($file); + $writer = new HtmlWriter($spreadsheet); + $writer->setConditionalFormatting(true); + $this->data = $writer->generateHtmlAll(); + $spreadsheet->disconnectWorksheets(); + } + + private function extractCell(string $coordinate): string + { + [$column, $row] = Coordinate::indexesFromString($coordinate); + --$column; + --$row; + // extract row into $matches + $match = preg_match('~~s', $this->data, $matches); + if ($match !== 1) { + return 'unable to match row'; + } + $rowData = $matches[0]; + // extract cell into $matches + $match = preg_match('~1<', 'A1 equals hit'], + ['B1', 'class="column1 style1 n">2<', 'B1 equals miss'], + ['E1', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">1<', 'E1 equals horizontal reference hit'], + ['F1', 'class="column5 style1 n">2<', 'F1 equals horizontal reference miss'], + ['G1', 'class="column6 style1 n">3<', 'G1 equals horizontal reference miss'], + ['A2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A2 text contains hit'], + ['B2', 'class="column1 style1 s">moi<', 'B2 text contains miss'], + ['A3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A3 text does not contain hit'], + ['B3', 'class="column1 style1 s">moi<', 'B2 text does not contain miss'], + ['A4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A4 text starts with hit'], + ['B4', 'class="column1 style1 s">moi<', 'B2 text starts with miss'], + ['A5', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A5 text ends with hit'], + ['B5', 'class="column1 style1 s">moi<', 'B5 text ends with miss'], + ['A6', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">2025/01/01<', 'A6 date after hit'], + ['B6', 'class="column1 style2 n">2020/01/01<', 'B6 date after miss'], + ['A7', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve vaan<', 'A7 text contains hit'], + ['B7', 'class="column1 style1 s">moi<', 'B7 text contains miss'], + ['A8', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A8 text does not contain hit'], + ['B8', 'class="column1 style1 s">terve vaan<', 'B2 does not contain miss'], + ['A9', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">#DIV/0!<', 'A10 own formula is error hit'], + ['B9', 'class="column1 style1 s">moi<', 'B9 own formula is error miss'], + ['A10', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">moi<', 'A10 own formula is not error hit'], + ['B10', 'class="column1 style3 s">#DIV/0!<', 'B10 own formula is not error miss'], + ['A11', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A11 own formula count instances of cell on line and hit when more than one hit'], + ['B11', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'B11 own formula count instances of cell on line and hit when more than one hit'], + ['C11', 'class="column2 style1 s">moi<', 'C11 own formula count instances of cell on line and hit when more than one miss'], + ['A12', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">moi<', 'A12 own formula count instances of cell on line and hit when at most 1 hit'], + ['B12', 'class="column1 style1 s">terve<', 'B12 own formula count instances of cell on line and hit when at most 1 miss'], + ['C12', 'class="column2 style1 s">terve<', 'C11 own formula count instances of cell on line and hit when at most 1 miss'], + ['A13', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">12<', 'A13 own formula self reference hit'], + ['B13', 'class="column1 style1 n">10<', 'B13 own formula self reference miss'], + ['A14', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">10<', 'A14 multiple conditional hits'], + ['B14', 'class="column1 style1 n">1<', 'B14 multiple conditionals miss'], + ['F7', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">1<', 'F7 equals vertical reference hit'], + ['F8', 'class="column5 style1 n">2<', 'F8 equals vertical reference miss'], + ['F9', 'class="column5 style1 n">3<', 'F9 equals vertical reference miss'], + ['F10', 'class="column5 style1 n">4<', 'F10 equals vertical reference miss'], + ]; + foreach ($expectedMatches as $expected) { + [$coordinate, $expectedString, $message] = $expected; + $string = $this->extractCell($coordinate); + self::assertStringContainsString($expectedString, $string, $message); + } + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatTest.php new file mode 100644 index 000000000..c78dda259 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatTest.php @@ -0,0 +1,64 @@ +load($file); + $writer = new HtmlWriter($spreadsheet); + $writer->setTableFormats(true); + $this->data = $writer->generateHtmlAll(); + $spreadsheet->disconnectWorksheets(); + } + + private function extractCell(string $coordinate): string + { + [$column, $row] = Coordinate::indexesFromString($coordinate); + --$column; + --$row; + // extract row into $matches + $match = preg_match('~~s', $this->data, $matches); + if ($match !== 1) { + return 'unable to match row'; + } + $rowData = $matches[0]; + // extract cell into $matches + $match = preg_match('~Sep<', 'table style for header row cell J1'], + ['J2', 'background-color:#C0E4F5;">110<', 'table style for cell J2'], + ['I3', 'background-color:#82CAEB;">70<', 'table style for cell I3'], + ['J3', 'background-color:#82CAEB;">70<', 'table style for cell J3'], // as conditional calculations are off + ['K3', 'background-color:#82CAEB;">70<', 'table style for cell K3'], + ['J4', 'background-color:#C0E4F5;">1<', 'table style for cell J4'], + ['J5', 'background-color:#82CAEB;">1<', 'table style for cell J5'], + ]; + foreach ($expectedMatches as $expected) { + [$coordinate, $expectedString, $message] = $expected; + $string = $this->extractCell($coordinate); + self::assertStringContainsString($expectedString, $string, $message); + } + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatWithConditionalTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatWithConditionalTest.php new file mode 100644 index 000000000..1c8a16d27 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlTableFormatWithConditionalTest.php @@ -0,0 +1,65 @@ +load($file); + $writer = new HtmlWriter($spreadsheet); + $writer->setTableFormats(true); + $writer->setConditionalFormatting(true); + $this->data = $writer->generateHtmlAll(); + $spreadsheet->disconnectWorksheets(); + } + + private function extractCell(string $coordinate): string + { + [$column, $row] = Coordinate::indexesFromString($coordinate); + --$column; + --$row; + // extract row into $matches + $match = preg_match('~~s', $this->data, $matches); + if ($match !== 1) { + return 'unable to match row'; + } + $rowData = $matches[0]; + // extract cell into $matches + $match = preg_match('~Sep<', 'table style for header row cell J1'], + ['J2', 'background-color:#C0E4F5;">110<', 'table style for cell J2'], + ['I3', 'background-color:#82CAEB;">70<', 'table style for cell I3'], + ['J3', 'background-color:#B7E1CD;">70<', 'conditional style for cell J3'], // as conditional calculations are on + ['K3', 'background-color:#82CAEB;">70<', 'table style for cell K3'], + ['J4', 'background-color:#C0E4F5;">1<', 'table style for cell J4'], + ['J5', 'background-color:#82CAEB;">1<', 'table style for cell J5'], + ]; + foreach ($expectedMatches as $expected) { + [$coordinate, $expectedString, $message] = $expected; + $string = $this->extractCell($coordinate); + self::assertStringContainsString($expectedString, $string, $message); + } + } +} From 584c8662d77332aa6c0b16f2e66be44b38ecfe35 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 26 Mar 2025 23:47:03 -0700 Subject: [PATCH 24/32] Final Touch-up --- ... html_01_Basic_Conditional_Formatting.php} | 0 ...> html_02_More_Conditional_Formatting.php} | 0 ...olor_Scale.php => html_03_Color_Scale.php} | 0 ...l_04_Table_Format_without_Conditional.php} | 0 ...html_05_Table_Format_with_Conditional.php} | 0 .../Writer/Html/HtmlColourScaleTest.php | 84 ++++++++----------- .../Html/HtmlConditionalFormattingTest.php | 12 +-- ...tmlDifferentConditionalFormattingsTest.php | 34 ++++---- 8 files changed, 60 insertions(+), 70 deletions(-) rename samples/Html/{01_Basic_Conditional_Formatting.php => html_01_Basic_Conditional_Formatting.php} (100%) rename samples/Html/{02_More_Conditional_Formatting.php => html_02_More_Conditional_Formatting.php} (100%) rename samples/Html/{03_Color_Scale.php => html_03_Color_Scale.php} (100%) rename samples/Html/{04_Table_Format_without_Conditional.php => html_04_Table_Format_without_Conditional.php} (100%) rename samples/Html/{05_Table_Format_with_Conditional.php => html_05_Table_Format_with_Conditional.php} (100%) diff --git a/samples/Html/01_Basic_Conditional_Formatting.php b/samples/Html/html_01_Basic_Conditional_Formatting.php similarity index 100% rename from samples/Html/01_Basic_Conditional_Formatting.php rename to samples/Html/html_01_Basic_Conditional_Formatting.php diff --git a/samples/Html/02_More_Conditional_Formatting.php b/samples/Html/html_02_More_Conditional_Formatting.php similarity index 100% rename from samples/Html/02_More_Conditional_Formatting.php rename to samples/Html/html_02_More_Conditional_Formatting.php diff --git a/samples/Html/03_Color_Scale.php b/samples/Html/html_03_Color_Scale.php similarity index 100% rename from samples/Html/03_Color_Scale.php rename to samples/Html/html_03_Color_Scale.php diff --git a/samples/Html/04_Table_Format_without_Conditional.php b/samples/Html/html_04_Table_Format_without_Conditional.php similarity index 100% rename from samples/Html/04_Table_Format_without_Conditional.php rename to samples/Html/html_04_Table_Format_without_Conditional.php diff --git a/samples/Html/05_Table_Format_with_Conditional.php b/samples/Html/html_05_Table_Format_with_Conditional.php similarity index 100% rename from samples/Html/05_Table_Format_with_Conditional.php rename to samples/Html/html_05_Table_Format_with_Conditional.php diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php index 979e75bb0..87a9fb698 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlColourScaleTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace PhpOffice\PhpSpreadsheetTests\Writer\Html; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; use PHPUnit\Framework\TestCase; @@ -23,57 +24,46 @@ class HtmlColourScaleTest extends TestCase $spreadsheet->disconnectWorksheets(); } - #[\PHPUnit\Framework\Attributes\DataProvider('colourScaleProvider')] - public function testColourScaleHtmlOutput(int $rowNumber, array $expectedMatches): void + private function extractCell(string $coordinate): string { - self::assertSame(1, preg_match('~~ms', $this->data, $matches)); - foreach ($expectedMatches as $i => $expected) { - self::assertStringContainsString($expected, $matches[0]); + [$column, $row] = Coordinate::indexesFromString($coordinate); + --$column; + --$row; + // extract row into $matches + $match = preg_match('~~s', $this->data, $matches); + if ($match !== 1) { + return 'unable to match row'; } + $rowData = $matches[0]; + // extract cell into $matches + $match = preg_match('~1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10']], - 'row 1: low/high 40%/80% with 50% midpoint' => [1, ['1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10']], - 'row 2: low/high/midpoint values 3/8/4 ' => [2, ['1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10']], - 'row 3: low/high with 30/80 percentile and 50% midpoint, one cell no value' => [3, ['1', - '2', - '3', - '4', - '2', - '9', - '9', - '9', - '', - '10']]]; + $expectedMatches = [ + ['E1', 'background-color:#B4CA76;">5<', 'cell E1'], + ['F1', 'background-color:#CBCD71;">6<', 'cell F1'], + ['G1', 'background-color:#E3D16C;">7<', 'cell G1'], + ['D2', 'background-color:#57BB8A;">4<', 'cell D2'], + ['E2', 'background-color:#A1C77A;">5<', 'cell E2'], + ['F2', 'background-color:#F1A36D;">6<', 'cell F2'], + ['D3', 'background-color:#FFD666;">4<', 'cell D3'], + ['G3', 'background-color:#EC926F;">7<', 'cell G3'], + ['H3', 'background-color:#E67C73;">8<', 'cell H3'], + ['A4', 'background-color:#57BB8A;">1<', 'cell A4'], + ['I4', 'null"><', 'empty cell I4'], + ['J4', 'background-color:#E67C73;">10<', 'cell J4'], + ]; + foreach ($expectedMatches as $expected) { + [$coordinate, $expectedString, $message] = $expected; + $string = $this->extractCell($coordinate); + self::assertStringContainsString($expectedString, $string, $message); + } } } diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php index 0339248ca..c5a31e415 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlConditionalFormattingTest.php @@ -48,12 +48,12 @@ class HtmlConditionalFormattingTest extends TestCase { $expectedMatches = [ ['B1', 'class="column1 style1 s">Jan<', 'no conditional styling for B1'], - ['F2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">120<', 'conditional style for F2'], - ['H2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C5700;font-family:\'Arial\';font-size:11pt;background-color:#FFEB9C;">90<', 'conditional style for H2'], - ['F3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">70<', 'conditional style for cell F3'], - ['H3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C5700;font-family:\'Arial\';font-size:11pt;background-color:#FFEB9C;">60<', 'conditional style for cell H3'], - ['F4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#006100;font-family:\'Arial\';font-size:11pt;background-color:#C6EFCE;">1<', 'conditional style for cell F4'], - ['L4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#9C0006;font-family:\'Arial\';font-size:11pt;background-color:#FFC7CE;">5<', 'conditional style for cell L4'], + ['F2', 'background-color:#C6EFCE;">120<', 'conditional style for F2'], + ['H2', 'background-color:#FFEB9C;">90<', 'conditional style for H2'], + ['F3', 'background-color:#C6EFCE;">70<', 'conditional style for cell F3'], + ['H3', 'background-color:#FFEB9C;">60<', 'conditional style for cell H3'], + ['F4', 'background-color:#C6EFCE;">1<', 'conditional style for cell F4'], + ['L4', 'background-color:#FFC7CE;">5<', 'conditional style for cell L4'], ['F5', 'class="column5 style1 n">0<', 'no conditional styling for F5'], ]; foreach ($expectedMatches as $expected) { diff --git a/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php b/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php index 38b7e976d..4bdb88621 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/HtmlDifferentConditionalFormattingsTest.php @@ -47,40 +47,40 @@ class HtmlDifferentConditionalFormattingsTest extends TestCase public function testConditionalFormattingRulesHtml(): void { $expectedMatches = [ - ['A1', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">1<', 'A1 equals hit'], + ['A1', 'background-color:#B7E1CD;">1<', 'A1 equals hit'], ['B1', 'class="column1 style1 n">2<', 'B1 equals miss'], - ['E1', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">1<', 'E1 equals horizontal reference hit'], + ['E1', 'background-color:#B7E1CD;">1<', 'E1 equals horizontal reference hit'], ['F1', 'class="column5 style1 n">2<', 'F1 equals horizontal reference miss'], ['G1', 'class="column6 style1 n">3<', 'G1 equals horizontal reference miss'], - ['A2', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A2 text contains hit'], + ['A2', 'background-color:#B7E1CD;">terve<', 'A2 text contains hit'], ['B2', 'class="column1 style1 s">moi<', 'B2 text contains miss'], - ['A3', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A3 text does not contain hit'], + ['A3', 'background-color:#B7E1CD;">terve<', 'A3 text does not contain hit'], ['B3', 'class="column1 style1 s">moi<', 'B2 text does not contain miss'], - ['A4', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A4 text starts with hit'], + ['A4', 'background-color:#B7E1CD;">terve<', 'A4 text starts with hit'], ['B4', 'class="column1 style1 s">moi<', 'B2 text starts with miss'], - ['A5', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A5 text ends with hit'], + ['A5', 'background-color:#B7E1CD;">terve<', 'A5 text ends with hit'], ['B5', 'class="column1 style1 s">moi<', 'B5 text ends with miss'], - ['A6', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">2025/01/01<', 'A6 date after hit'], + ['A6', 'background-color:#B7E1CD;">2025/01/01<', 'A6 date after hit'], ['B6', 'class="column1 style2 n">2020/01/01<', 'B6 date after miss'], - ['A7', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve vaan<', 'A7 text contains hit'], + ['A7', 'background-color:#B7E1CD;">terve vaan<', 'A7 text contains hit'], ['B7', 'class="column1 style1 s">moi<', 'B7 text contains miss'], - ['A8', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A8 text does not contain hit'], + ['A8', 'background-color:#B7E1CD;">terve<', 'A8 text does not contain hit'], ['B8', 'class="column1 style1 s">terve vaan<', 'B2 does not contain miss'], - ['A9', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">#DIV/0!<', 'A10 own formula is error hit'], + ['A9', 'background-color:#B7E1CD;">#DIV/0!<', 'A10 own formula is error hit'], ['B9', 'class="column1 style1 s">moi<', 'B9 own formula is error miss'], - ['A10', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">moi<', 'A10 own formula is not error hit'], + ['A10', 'background-color:#B7E1CD;">moi<', 'A10 own formula is not error hit'], ['B10', 'class="column1 style3 s">#DIV/0!<', 'B10 own formula is not error miss'], - ['A11', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'A11 own formula count instances of cell on line and hit when more than one hit'], - ['B11', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">terve<', 'B11 own formula count instances of cell on line and hit when more than one hit'], + ['A11', 'background-color:#B7E1CD;">terve<', 'A11 own formula count instances of cell on line and hit when more than one hit'], + ['B11', 'background-color:#B7E1CD;">terve<', 'B11 own formula count instances of cell on line and hit when more than one hit'], ['C11', 'class="column2 style1 s">moi<', 'C11 own formula count instances of cell on line and hit when more than one miss'], - ['A12', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">moi<', 'A12 own formula count instances of cell on line and hit when at most 1 hit'], + ['A12', 'background-color:#B7E1CD;">moi<', 'A12 own formula count instances of cell on line and hit when at most 1 hit'], ['B12', 'class="column1 style1 s">terve<', 'B12 own formula count instances of cell on line and hit when at most 1 miss'], ['C12', 'class="column2 style1 s">terve<', 'C11 own formula count instances of cell on line and hit when at most 1 miss'], - ['A13', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">12<', 'A13 own formula self reference hit'], + ['A13', 'background-color:#B7E1CD;">12<', 'A13 own formula self reference hit'], ['B13', 'class="column1 style1 n">10<', 'B13 own formula self reference miss'], - ['A14', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">10<', 'A14 multiple conditional hits'], + ['A14', 'background-color:#B7E1CD;">10<', 'A14 multiple conditional hits'], ['B14', 'class="column1 style1 n">1<', 'B14 multiple conditionals miss'], - ['F7', '"vertical-align:bottom;border-bottom:1px solid #000000 !important;border-top:1px solid #000000 !important;border-left:1px solid #000000 !important;border-right:1px solid #000000 !important;color:#000000;font-family:\'Arial\';font-size:11pt;background-color:#B7E1CD;">1<', 'F7 equals vertical reference hit'], + ['F7', 'background-color:#B7E1CD;">1<', 'F7 equals vertical reference hit'], ['F8', 'class="column5 style1 n">2<', 'F8 equals vertical reference miss'], ['F9', 'class="column5 style1 n">3<', 'F9 equals vertical reference miss'], ['F10', 'class="column5 style1 n">4<', 'F10 equals vertical reference miss'], From 664b9c7c24468b8f6c7bcd67780fb0f8cd5ec2d7 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 27 Mar 2025 00:00:28 -0700 Subject: [PATCH 25/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef7366a..4447f7af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389) - Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339) +- Column widths not preserved when using read filter. [Issue #4416](https://github.com/PHPOffice/PhpSpreadsheet/issues/4416) [PR #4423](https://github.com/PHPOffice/PhpSpreadsheet/pull/4423) ## 2025-03-02 - 4.1.0 From 00ededb3798cbfcb2d34e92e05fd8ebd4ad037e9 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 29 Mar 2025 06:53:41 -0700 Subject: [PATCH 26/32] Phpstan Level 9 - Part 7 of Many: Functions, LookupRef --- phpstan-baseline.neon | 150 ------------------ src/PhpSpreadsheet/Calculation/Functions.php | 21 ++- .../Calculation/LookupRef/Address.php | 7 +- .../Calculation/LookupRef/ExcelMatch.php | 2 +- .../Calculation/LookupRef/Formula.php | 4 +- .../Calculation/LookupRef/HLookup.php | 8 +- 6 files changed, 29 insertions(+), 163 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ae6b43099..4897d49cb 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,155 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Binary operation "\." between ''\='' and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Binary operation "\." between string and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$dateValue of static method PhpOffice\\PhpSpreadsheet\\Shared\\Date\:\:stringToExcel\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$haystack of function substr_count expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$string of function rtrim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$string of function strtoupper expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#3 \$subject of function preg_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#3 \$subject of function str_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Functions.php - - - - message: '#^Parameter \#1 \$row of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsA1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#1 \$row of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsR1C1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#1 \$sheetName of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:sheetName\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#2 \$column of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsA1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#2 \$column of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsR1C1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#3 \$relativity of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsA1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Parameter \#3 \$relativity of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Address\:\:formatAsR1C1\(\) expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php - - - - message: '#^Cannot use \+\+ on mixed\.$#' - identifier: preInc.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Formula\:\:text\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Formula.php - - - - message: '#^Parameter \#1 \$coordinate of method PhpOffice\\PhpSpreadsheet\\Worksheet\\Worksheet\:\:cellExists\(\) expects array\{int, int\}\|PhpOffice\\PhpSpreadsheet\\Cell\\CellAddress\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Formula.php - - - - message: '#^Parameter \#1 \$coordinate of method PhpOffice\\PhpSpreadsheet\\Worksheet\\Worksheet\:\:getCell\(\) expects array\{int, int\}\|PhpOffice\\PhpSpreadsheet\\Cell\\CellAddress\|string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/LookupRef/Formula.php - - - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Formula.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php - - - - message: '#^Parameter \#1 \$lookupArray of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\HLookup\:\:convertLiteralArray\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php - - - - message: '#^Parameter \#2 \$index_number of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupBase\:\:validateIndexLookup\(\) expects float\|int\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Hyperlink\:\:set\(\) should return string but returns mixed\.$#' identifier: return.type diff --git a/src/PhpSpreadsheet/Calculation/Functions.php b/src/PhpSpreadsheet/Calculation/Functions.php index 3e6242286..c8ff2ebe2 100644 --- a/src/PhpSpreadsheet/Calculation/Functions.php +++ b/src/PhpSpreadsheet/Calculation/Functions.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Functions { @@ -130,16 +131,22 @@ class Functions public static function isMatrixValue(mixed $idx): bool { + $idx = StringHelper::convertToString($idx); + return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0); } public static function isValue(mixed $idx): bool { + $idx = StringHelper::convertToString($idx); + return substr_count($idx, '.') === 0; } public static function isCellValue(mixed $idx): bool { + $idx = StringHelper::convertToString($idx); + return substr_count($idx, '.') > 1; } @@ -154,7 +161,8 @@ class Functions $condition = self::operandSpecialHandling($condition); if (is_bool($condition)) { return '=' . ($condition ? 'TRUE' : 'FALSE'); - } elseif (!is_numeric($condition)) { + } + if (!is_numeric($condition)) { if ($condition !== '""') { // Not an empty string // Escape any quotes in the string value $condition = (string) preg_replace('/"/ui', '""', $condition); @@ -162,29 +170,32 @@ class Functions $condition = Calculation::wrapResult(strtoupper($condition)); } - return str_replace('""""', '""', '=' . $condition); + return str_replace('""""', '""', '=' . StringHelper::convertToString($condition)); } $operator = $operand = ''; if (1 === preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches)) { [, $operator, $operand] = $matches; } - $operand = self::operandSpecialHandling($operand); + $operand = (string) self::operandSpecialHandling($operand); if (is_numeric(trim($operand, '"'))) { $operand = trim($operand, '"'); } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') { $operand = str_replace('"', '""', $operand); $operand = Calculation::wrapResult(strtoupper($operand)); + $operand = StringHelper::convertToString($operand); } return str_replace('""""', '""', $operator . $operand); } - private static function operandSpecialHandling(mixed $operand): mixed + private static function operandSpecialHandling(mixed $operand): bool|float|int|string { if (is_numeric($operand) || is_bool($operand)) { return $operand; - } elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) { + } + $operand = StringHelper::convertToString($operand); + if (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) { return strtoupper($operand); } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php index 0a5347b83..cfd23b14a 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Cell\AddressHelper; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Address { @@ -63,14 +64,16 @@ class Address ); } - $relativity = $relativity ?? 1; + $relativity = ($relativity === null) ? 1 : (int) StringHelper::convertToString($relativity); $referenceStyle = $referenceStyle ?? true; + $row = (int) StringHelper::convertToString($row); + $column = (int) StringHelper::convertToString($column); if (($row < 1) || ($column < 1)) { return ExcelError::VALUE(); } - $sheetName = self::sheetName($sheetName); + $sheetName = self::sheetName(StringHelper::convertToString($sheetName)); if (is_int($referenceStyle)) { $referenceStyle = (bool) $referenceStyle; diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php b/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php index 43e89c9b9..decbcd8a8 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php @@ -70,7 +70,7 @@ class ExcelMatch }; if ($valueKey !== null) { - return ++$valueKey; + return ++$valueKey; //* @phpstan-ignore-line } // Unsuccessful in finding a match, return #N/A error value diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php b/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php index 55a2a8fff..f4982a0ea 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Cell\Cell; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Formula { @@ -21,6 +22,7 @@ class Formula } $worksheet = null; + $cellReference = StringHelper::convertToString($cellReference); if (1 === preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches)) { $cellReference = $matches[6] . $matches[7]; $worksheetName = trim($matches[3], "'"); @@ -37,6 +39,6 @@ class Formula return ExcelError::NA(); } - return $worksheet->getCell($cellReference)->getValue(); + return $worksheet->getCell($cellReference)->getValueString(); } } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php b/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php index fd83700b4..a27875021 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php @@ -18,14 +18,14 @@ class HLookup extends LookupBase * in the same column based on the index_number. * * @param mixed $lookupValue The value that you want to match in lookup_array - * @param mixed $lookupArray The range of cells being searched - * @param mixed $indexNumber The row number in table_array from which the matching value must be returned. + * @param array $lookupArray The range of cells being searched + * @param array|float|int|string $indexNumber The row number in table_array from which the matching value must be returned. * The first row is 1. * @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value * * @return mixed The value of the found cell */ - public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed + public static function lookup(mixed $lookupValue, $lookupArray, $indexNumber, mixed $notExactMatch = true): mixed { if (is_array($lookupValue) || is_array($indexNumber)) { return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch); @@ -66,7 +66,7 @@ class HLookup extends LookupBase */ private static function hLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int { - $lookupLower = StringHelper::strToLower((string) $lookupValue); + $lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue)); $rowNumber = null; foreach ($lookupArray[$column] as $rowKey => $rowData) { From 078846cc8e232f1d9690179274036c337b29cf7f Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 29 Mar 2025 07:22:44 -0700 Subject: [PATCH 27/32] Phpstan Level 9 - Part 8 of Many - TextData --- phpstan-baseline.neon | 114 ------------------ .../Calculation/TextData/CharacterConvert.php | 1 + .../Calculation/TextData/Concatenate.php | 13 +- .../Calculation/TextData/Extract.php | 14 +-- .../Calculation/TextData/Format.php | 17 +-- .../Calculation/TextData/Helpers.php | 5 +- .../Calculation/TextData/Replace.php | 2 +- 7 files changed, 29 insertions(+), 137 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ae6b43099..4c9400593 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -419,117 +419,3 @@ parameters: identifier: argument.type count: 9 path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\CharacterConvert\:\:unicodeToOrd\(\) should return int but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: '#^Binary operation "\." between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: '#^Parameter \#1 \$ignoreEmpty of static method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Concatenate\:\:evaluateTextJoinArray\(\) expects bool, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: '#^Parameter \#1 \$separator of function implode expects array\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 6 - path: src/PhpSpreadsheet/Calculation/TextData/Extract.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Extract\:\:validateTextBeforeAfter\(\) should return array\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Extract.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 3 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Format\:\:VALUE\(\) should return array\|DateTimeInterface\|float\|int\|string but returns mixed\.$#' - identifier: return.type - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#1 \$dateValue of static method PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\DateValue\:\:fromString\(\) expects array\|bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#1 \$haystack of function str_contains expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#1 \$haystack of function strpos expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#1 \$timeValue of static method PhpOffice\\PhpSpreadsheet\\Calculation\\DateTimeExcel\\TimeValue\:\:fromString\(\) expects array\|bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#2 \$subject of static method Composer\\Pcre\\Preg\:\:matchAllWithOffsets\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Parameter \#3 \$subject of function str_replace expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Helpers.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Helpers.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\TextData\\Replace\:\:executeSubstitution\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Replace.php diff --git a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php index 06d0f9009..0f14ee468 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php +++ b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php @@ -81,6 +81,7 @@ class CharacterConvert $retVal = 0; $iconv = iconv('UTF-8', 'UCS-4LE', $character); if ($iconv !== false) { + /** @var false|int[] */ $result = unpack('V', $iconv); if (is_array($result) && isset($result[1])) { $retVal = $result[1]; diff --git a/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php index a48d05365..dfb490af9 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php @@ -78,8 +78,8 @@ class Concatenate return $operand2[$row][$column]; } $operand1[$row][$column] - = Calculation::boolToString($operand1[$row][$column]) - . Calculation::boolToString($operand2[$row][$column]); + = StringHelper::convertToString($operand1[$row][$column], convertBool: true) + . StringHelper::convertToString($operand2[$row][$column], convertBool: true); if (mb_strlen($operand1[$row][$column]) > DataType::MAX_STRING_LENGTH) { $operand1 = ExcelError::CALC(); $errorFound = true; @@ -91,7 +91,7 @@ class Concatenate } elseif (ErrorValue::isError($operand2, true) === true) { $operand1 = (string) $operand2; } else { - $operand1 .= (string) Calculation::boolToString($operand2); + $operand1 .= StringHelper::convertToString($operand2, convertBool: true); if (mb_strlen($operand1) > DataType::MAX_STRING_LENGTH) { $operand1 = ExcelError::CALC(); } @@ -103,9 +103,9 @@ class Concatenate /** * TEXTJOIN. * - * @param mixed $delimiter The delimter to use between the joined arguments + * @param null|string|string[] $delimiter The delimiter to use between the joined arguments * Or can be an array of values - * @param mixed $ignoreEmpty true/false Flag indicating whether empty arguments should be skipped + * @param null|bool|bool[] $ignoreEmpty true/false Flag indicating whether empty arguments should be skipped * Or can be an array of values * @param mixed $args The values to join * @@ -113,7 +113,7 @@ class Concatenate * If an array of values is passed for the $delimiter or $ignoreEmpty arguments, then the returned result * will also be an array with matching dimensions */ - public static function TEXTJOIN(mixed $delimiter = '', mixed $ignoreEmpty = true, mixed ...$args): array|string + public static function TEXTJOIN($delimiter = '', $ignoreEmpty = true, mixed ...$args): array|string { if (is_array($delimiter) || is_array($ignoreEmpty)) { return self::evaluateArrayArgumentsSubset( @@ -127,6 +127,7 @@ class Concatenate $delimiter ??= ''; $ignoreEmpty ??= true; + /** @var array */ $aArgs = Functions::flattenArray($args); $returnValue = self::evaluateTextJoinArray($ignoreEmpty, $aArgs); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/src/PhpSpreadsheet/Calculation/TextData/Extract.php index 1dfb724cd..2cfec1ae4 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Extract.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Extract.php @@ -139,9 +139,9 @@ class Extract return $e->getMessage(); } - $instance = (int) $instance; - $matchMode = (int) $matchMode; - $matchEnd = (int) $matchEnd; + $instance = (int) StringHelper::convertToString($instance); + $matchMode = (int) StringHelper::convertToString($matchMode); + $matchEnd = (int) StringHelper::convertToString($matchEnd); $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound); if (is_string($split)) { @@ -203,9 +203,9 @@ class Extract return $e->getMessage(); } - $instance = (int) $instance; - $matchMode = (int) $matchMode; - $matchEnd = (int) $matchEnd; + $instance = (int) StringHelper::convertToString($instance); + $matchMode = (int) StringHelper::convertToString($matchMode); + $matchEnd = (int) StringHelper::convertToString($matchEnd); $split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound); if (is_string($split)) { @@ -234,7 +234,7 @@ class Extract $delimiter = self::buildDelimiter($delimiter); if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) { - return $ifNotFound; + return is_array($ifNotFound) ? $ifNotFound : StringHelper::convertToString($ifNotFound); } $split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Format.php b/src/PhpSpreadsheet/Calculation/TextData/Format.php index 72514179c..f1b08a504 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Format.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Format.php @@ -192,6 +192,7 @@ class Format return $e->getMessage(); } if (!is_numeric($value)) { + $value = StringHelper::convertToString($value); $numberValue = str_replace( StringHelper::getThousandsSeparator(), '', @@ -212,14 +213,14 @@ class Format if ($timeValue !== ExcelError::VALUE()) { Functions::setReturnDateType($dateSetting); - return $timeValue; + return $timeValue; //* @phpstan-ignore-line } } $dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value)); if ($dateValue !== ExcelError::VALUE()) { Functions::setReturnDateType($dateSetting); - return $dateValue; + return $dateValue; //* @phpstan-ignore-line } Functions::setReturnDateType($dateSetting); @@ -250,23 +251,23 @@ class Format $value = $value->getPlainText(); } if (is_string($value)) { - $value = ($format === true) ? Calculation::wrapResult($value) : $value; + $value = ($format === true) ? StringHelper::convertToString(Calculation::wrapResult($value)) : $value; $value = str_replace("\n", '', $value); } elseif (is_bool($value)) { $value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE'); } - return (string) $value; + return StringHelper::convertToString($value); } private static function getDecimalSeparator(mixed $decimalSeparator): string { - return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator; + return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : StringHelper::convertToString($decimalSeparator); } private static function getGroupSeparator(mixed $groupSeparator): string { - return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator; + return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : StringHelper::convertToString($groupSeparator); } /** @@ -293,7 +294,9 @@ class Format return $e->getMessage(); } - if (!is_numeric($value)) { + /** @var null|array|scalar $value */ + if (!is_array($value) && !is_numeric($value)) { + $value = StringHelper::convertToString($value); $decimalPositions = Preg::matchAllWithOffsets('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches); if ($decimalPositions > 1) { return ExcelError::VALUE(); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Helpers.php b/src/PhpSpreadsheet/Calculation/TextData/Helpers.php index 719de04a8..e6dad291a 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Helpers.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Helpers { @@ -31,7 +32,7 @@ class Helpers throw new CalcExp($value); } - return (string) $value; + return StringHelper::convertToString($value); } public static function extractInt(mixed $value, int $minValue, int $gnumericNull = 0, bool $ooBoolOk = false): int @@ -87,6 +88,6 @@ class Helpers throw new CalcExp($value); } - return (int) $value; + return (int) StringHelper::convertToString($value); } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Replace.php b/src/PhpSpreadsheet/Calculation/TextData/Replace.php index 8f6f196fe..d2494c0ee 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Replace.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Replace.php @@ -111,6 +111,6 @@ class Replace --$instance; } - return Functions::scalar(self::REPLACE($text, ++$pos, StringHelper::countCharacters($fromText), $toText)); + return StringHelper::convertToString(Functions::scalar(self::REPLACE($text, ++$pos, StringHelper::countCharacters($fromText), $toText))); } } From e32447c4be66e4d25284263a53b6e5bf45792f5b Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:53:47 -0700 Subject: [PATCH 28/32] Phpstan Level 9: Last --- composer.lock | 12 +- phpstan-baseline.neon | 269 ------------------ samples/Wizards/NumberFormat/Accounting.php | 4 +- samples/Wizards/NumberFormat/Currency.php | 4 +- .../Calculation/LookupRef/Hyperlink.php | 12 +- .../Calculation/LookupRef/Lookup.php | 4 +- .../LookupRef/LookupRefValidations.php | 2 +- .../Calculation/LookupRef/Matrix.php | 4 +- .../Calculation/LookupRef/Offset.php | 26 +- .../Calculation/LookupRef/Sort.php | 3 +- .../Calculation/LookupRef/VLookup.php | 8 +- .../Calculation/MathTrig/Subtotal.php | 12 +- .../Calculation/Statistical/Averages.php | 3 + .../Calculation/Statistical/Conditional.php | 5 +- .../Calculation/Statistical/Confidence.php | 5 +- .../Statistical/Distributions/ChiSquared.php | 7 +- .../Statistical/Distributions/Fisher.php | 4 +- .../Calculation/Statistical/Trends.php | 3 + .../Writer/Xls/ConditionalHelper.php | 5 +- .../Functions/Statistical/ChiTestTest.php | 5 +- 20 files changed, 80 insertions(+), 317 deletions(-) diff --git a/composer.lock b/composer.lock index a540446f2..7b381801b 100644 --- a/composer.lock +++ b/composer.lock @@ -1797,16 +1797,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.6", + "version": "2.1.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" + "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30", "shasum": "" }, "require": { @@ -1851,7 +1851,7 @@ "type": "github" } ], - "time": "2025-02-19T15:46:42+00:00" + "time": "2025-03-24T13:45:00+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -5620,5 +5620,5 @@ "platform-overrides": { "php": "8.1.99" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 77ad2d51f..364905f71 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,271 +1,2 @@ parameters: ignoreErrors: - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Hyperlink\:\:set\(\) should return string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php - - - - message: '#^Parameter \#1 \$string of function trim expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php - - - - message: '#^Parameter \#1 \$tooltip of method PhpOffice\\PhpSpreadsheet\\Cell\\Hyperlink\:\:setTooltip\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php - - - - message: '#^Parameter \#1 \$url of method PhpOffice\\PhpSpreadsheet\\Cell\\Hyperlink\:\:setUrl\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php - - - - message: '#^Parameter \#1 \$lookupVector of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Lookup\:\:verifyLookupValues\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php - - - - message: '#^Parameter \#1 \$resultVector of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Lookup\:\:verifyResultVector\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php - - - - message: '#^Parameter \#2 \$resultVector of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Lookup\:\:verifyLookupValues\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php - - - - message: '#^Parameter \#1 \$message of class PhpOffice\\PhpSpreadsheet\\Calculation\\Exception constructor expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php - - - - message: '#^Cannot access offset int\|string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php - - - - message: '#^Binary operation "\+\=" between int and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php - - - - message: '#^Binary operation "\+\=" between mixed and int results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php - - - - message: '#^Binary operation "\-" between mixed and 1 results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php - - - - message: '#^Cannot cast mixed to int\.$#' - identifier: cast.int - count: 4 - path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php - - - - message: '#^Parameter \#1 \$sortIndex of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Sort\:\:validateArrayArgumentsForSort\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Sort.php - - - - message: '#^Parameter \#3 \$sortOrder of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Sort\:\:sortByColumn\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Sort.php - - - - message: '#^Parameter \#3 \$sortOrder of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\Sort\:\:sortByRow\(\) expects array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Sort.php - - - - message: '#^Cannot access offset \(int\|string\) on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Cannot access offset int on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Cannot access offset int\|string\|null on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Cannot cast mixed to string\.$#' - identifier: cast.string - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Parameter \#1 \$array of function uasort expects TArray of array\, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Parameter \#1 \$lookup_array of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupBase\:\:validateIndexLookup\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Parameter \#2 \$index_number of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\LookupBase\:\:validateIndexLookup\(\) expects float\|int\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Parameter \#2 \$lookupArray of static method PhpOffice\\PhpSpreadsheet\\Calculation\\LookupRef\\VLookup\:\:vLookupSearch\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - - message: '#^Cannot call method getWorksheet\(\) on mixed\.$#' - identifier: method.nonObject - count: 4 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\MathTrig\\Subtotal\:\:evaluate\(\) should return float\|int\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: '#^Parameter \#1 \$array of function array_filter expects array, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: '#^Parameter \#2 \$string of function explode expects string, mixed given\.$#' - identifier: argument.type - count: 2 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: '#^Binary operation "\+\=" between \(float\|int\) and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php - - - - message: '#^Binary operation "\-" between mixed and float\|int\|string results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php - - - - message: '#^Parameter \#2 \$condition of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Conditional\:\:AVERAGEIF\(\) expects array\|string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Confidence\:\:CONFIDENCE\(\) should return array\|float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Confidence.php - - - - message: '#^Method PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Distributions\\ChiSquared\:\:test\(\) should return float\|string but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php - - - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php - - - - message: '#^Binary operation "\*" between 2 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php - - - - message: '#^Binary operation "\+" between 1 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php - - - - message: '#^Binary operation "\-" between 1 and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php - - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php - - - - message: '#^Parameter \#1 \$yValues of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Trends\:\:validateTrendArrays\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 9 - path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php - - - - message: '#^Parameter \#2 \$xValues of static method PhpOffice\\PhpSpreadsheet\\Calculation\\Statistical\\Trends\:\:validateTrendArrays\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 9 - path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php - - - - message: '#^Parameter \#2 \$yValues of static method PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\Trend\:\:calculate\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 9 - path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php - - - - message: '#^Parameter \#3 \$xValues of static method PhpOffice\\PhpSpreadsheet\\Shared\\Trend\\Trend\:\:calculate\(\) expects array, mixed given\.$#' - identifier: argument.type - count: 9 - path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php diff --git a/samples/Wizards/NumberFormat/Accounting.php b/samples/Wizards/NumberFormat/Accounting.php index 07eddfc68..f4fe8ef93 100644 --- a/samples/Wizards/NumberFormat/Accounting.php +++ b/samples/Wizards/NumberFormat/Accounting.php @@ -1,14 +1,12 @@ isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); @@ -60,7 +58,7 @@ $currencies = [

- >Leading + >Leading >Trailing
diff --git a/samples/Wizards/NumberFormat/Currency.php b/samples/Wizards/NumberFormat/Currency.php index 7707aaa05..bb1bdd0c4 100644 --- a/samples/Wizards/NumberFormat/Currency.php +++ b/samples/Wizards/NumberFormat/Currency.php @@ -1,7 +1,6 @@ isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); @@ -75,7 +73,7 @@ $currencies = [
- >Leading + >Leading >Trailing
diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php index 455442a8e..e7752aa40 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Cell\Cell; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Hyperlink { @@ -22,18 +23,23 @@ class Hyperlink */ public static function set(mixed $linkURL = '', mixed $displayName = null, ?Cell $cell = null): string { - $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL); + $linkURL = ($linkURL === null) ? '' : StringHelper::convertToString(Functions::flattenSingleValue($linkURL)); $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName); if ((!is_object($cell)) || (trim($linkURL) == '')) { return ExcelError::REF(); } - if ((is_object($displayName)) || trim($displayName) == '') { + if (is_object($displayName)) { + $displayName = $linkURL; + } + $displayName = StringHelper::convertToString($displayName); + if (trim($displayName) === '') { $displayName = $linkURL; } - $cell->getHyperlink()->setUrl($linkURL); + $cell->getHyperlink() + ->setUrl($linkURL); $cell->getHyperlink()->setTooltip($displayName); return $displayName; diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php b/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php index b18762078..8f576203f 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php @@ -38,13 +38,15 @@ class Lookup $lookupColumns = self::columnCount($lookupVector); } - $resultVector = self::verifyResultVector($resultVector ?? $lookupVector); + $resultVector = self::verifyResultVector($resultVector ?? $lookupVector); //* @phpstan-ignore-line if ($lookupRows === 2 && !$hasResultVector) { $resultVector = array_pop($lookupVector); $lookupVector = array_shift($lookupVector); } + /** @var array $lookupVector */ + /** @var array $resultVector */ if ($lookupColumns !== 2) { $lookupVector = self::verifyLookupValues($lookupVector, $resultVector); } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php b/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php index 74c313ccf..620705e98 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php @@ -11,7 +11,7 @@ class LookupRefValidations public static function validateInt(mixed $value): int { if (!is_numeric($value)) { - if (ErrorValue::isError($value)) { + if (is_string($value) && ErrorValue::isError($value)) { throw new Exception($value); } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php index b8e84a669..60d68aa49 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php @@ -40,6 +40,7 @@ class Matrix } $column = 0; + /** @var iterable $matrixData */ foreach ($matrixData as $matrixRow) { $row = 0; foreach ($matrixRow as $matrixCell) { @@ -115,7 +116,7 @@ class Matrix } $rowKeys = array_keys($matrix); - $columnKeys = @array_keys($matrix[$rowKeys[0]]); + $columnKeys = @array_keys($matrix[$rowKeys[0]]); //* @phpstan-ignore-line if ($columnNum > count($columnKeys)) { return ExcelError::REF(); @@ -133,6 +134,7 @@ class Matrix ); } $rowNum = $rowKeys[--$rowNum]; + /** @var array[] $matrix */ return $matrix[$rowNum][$columnNum]; } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php index 9d201ff22..e04167b14 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php @@ -25,28 +25,32 @@ class Offset * @param null|string $cellAddress The reference from which you want to base the offset. * Reference must refer to a cell or range of adjacent cells; * otherwise, OFFSET returns the #VALUE! error value. - * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to. + * @param int $rows The number of rows, up or down, that you want the upper-left cell to refer to. * Using 5 as the rows argument specifies that the upper-left cell in the * reference is five rows below reference. Rows can be positive (which means * below the starting reference) or negative (which means above the starting * reference). - * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell + * @param int $columns The number of columns, to the left or right, that you want the upper-left cell * of the result to refer to. Using 5 as the cols argument specifies that the * upper-left cell in the reference is five columns to the right of reference. * Cols can be positive (which means to the right of the starting reference) * or negative (which means to the left of the starting reference). - * @param mixed $height The height, in number of rows, that you want the returned reference to be. + * @param ?int $height The height, in number of rows, that you want the returned reference to be. * Height must be a positive number. - * @param mixed $width The width, in number of columns, that you want the returned reference to be. + * @param ?int $width The width, in number of columns, that you want the returned reference to be. * Width must be a positive number. * * @return array|string An array containing a cell or range of cells, or a string on error */ - public static function OFFSET(?string $cellAddress = null, mixed $rows = 0, mixed $columns = 0, mixed $height = null, mixed $width = null, ?Cell $cell = null): string|array + public static function OFFSET(?string $cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null, ?Cell $cell = null): string|array { + /** @var int */ $rows = Functions::flattenSingleValue($rows); + /** @var int */ $columns = Functions::flattenSingleValue($columns); + /** @var int */ $height = Functions::flattenSingleValue($height); + /** @var int */ $width = Functions::flattenSingleValue($width); if ($cellAddress === null || $cellAddress === '') { @@ -126,7 +130,11 @@ class Offset return $cellAddress; } - private static function adjustEndCellColumnForWidth(string $endCellColumn, mixed $width, int $startCellColumn, mixed $columns): int + /** + * @param null|object|scalar $width + * @param scalar $columns + */ + private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns): int { $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1; if (($width !== null) && (!is_object($width))) { @@ -138,7 +146,11 @@ class Offset return $endCellColumn; } - private static function adustEndCellRowForHeight(mixed $height, int $startCellRow, mixed $rows, mixed $endCellRow): int + /** + * @param null|object|scalar $height + * @param scalar $rows + */ + private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, int $endCellRow): int { if (($height !== null) && (!is_object($height))) { $endCellRow = $startCellRow + (int) $height - 1; diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php b/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php index 9ad47b4ed..97abb2e77 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php @@ -43,7 +43,7 @@ class Sort extends LookupRefValidations try { // If $sortIndex and $sortOrder are scalars, then convert them into arrays - if (is_scalar($sortIndex)) { + if (!is_array($sortIndex)) { $sortIndex = [$sortIndex]; $sortOrder = is_scalar($sortOrder) ? [$sortOrder] : $sortOrder; } @@ -53,6 +53,7 @@ class Sort extends LookupRefValidations } catch (Exception $e) { return $e->getMessage(); } + /** @var array $sortOrder */ // 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)); diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php index 1c24b2a33..d8799358d 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php @@ -17,14 +17,14 @@ class VLookup extends LookupBase * in the same row based on the index_number. * * @param mixed $lookupValue The value that you want to match in lookup_array - * @param mixed $lookupArray The range of cells being searched - * @param mixed $indexNumber The column number in table_array from which the matching value must be returned. + * @param array $lookupArray The range of cells being searched + * @param array|float|int|string $indexNumber The column number in table_array from which the matching value must be returned. * The first column is 1. * @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value * * @return mixed The value of the found cell */ - public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed + public static function lookup(mixed $lookupValue, $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed { if (is_array($lookupValue) || is_array($indexNumber)) { return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch); @@ -84,7 +84,7 @@ class VLookup extends LookupBase */ private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int { - $lookupLower = StringHelper::strToLower((string) $lookupValue); + $lookupLower = StringHelper::strToLower(StringHelper::convertToString($lookupValue)); $rowNumber = null; foreach ($lookupArray as $rowKey => $rowData) { diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php index cfced9e43..1a6a72fe0 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php @@ -6,10 +6,11 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Calculation\Statistical; +use PhpOffice\PhpSpreadsheet\Cell\Cell; class Subtotal { - protected static function filterHiddenArgs(mixed $cellReference, mixed $args): array + protected static function filterHiddenArgs(Cell $cellReference, array $args): array { return array_filter( $args, @@ -20,13 +21,13 @@ class Subtotal return true; } - return $cellReference->getWorksheet()->getRowDimension($row)->getVisible(); + return $cellReference->getWorksheet()->getRowDimension((int) $row)->getVisible(); }, ARRAY_FILTER_USE_KEY ); } - protected static function filterFormulaArgs(mixed $cellReference, mixed $args): array + protected static function filterFormulaArgs(Cell $cellReference, array $args): array { return array_filter( $args, @@ -40,7 +41,7 @@ class Subtotal $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula(); $cellFormula = !preg_match( '/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i', - $cellReference->getWorksheet()->getCell($column . $row)->getValue() ?? '' + $cellReference->getWorksheet()->getCell($column . $row)->getValueString() ); $retVal = !$isFormula || $cellFormula; @@ -85,6 +86,7 @@ class Subtotal */ public static function evaluate(mixed $functionType, ...$args): float|int|string { + /** @var Cell */ $cellReference = array_pop($args); $bArgs = Functions::flattenArrayIndexed($args); $aArgs = []; @@ -119,7 +121,7 @@ class Subtotal if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) { $call = self::CALL_FUNCTIONS[$subtotal]; - return call_user_func_array($call, $aArgs); + return call_user_func_array($call, $aArgs); //* @phpstan-ignore-line } return ExcelError::VALUE(); diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Averages.php b/src/PhpSpreadsheet/Calculation/Statistical/Averages.php index 789e529d3..8c36af100 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Averages.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Averages.php @@ -44,6 +44,8 @@ class Averages extends AggregateBase return ExcelError::VALUE(); } if (self::isAcceptedCountable($arg, $k)) { + /** @var float|int|numeric-string $arg */ + /** @var float|int|numeric-string $aMean */ $returnValue += abs($arg - $aMean); ++$aCount; } @@ -83,6 +85,7 @@ class Averages extends AggregateBase return ExcelError::VALUE(); } if (self::isAcceptedCountable($arg, $k)) { + /** @var float|int|numeric-string $arg */ $returnValue += $arg; ++$aCount; } diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php b/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php index 5f75aef58..f54437970 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php @@ -59,8 +59,9 @@ class Conditional { if (empty($args)) { return 0.0; - } elseif (count($args) === 3) { - return self::AVERAGEIF($args[1], $args[2], $args[0]); + } + if (count($args) === 3) { + return self::AVERAGEIF($args[1], $args[2], $args[0]); //* @phpstan-ignore-line } foreach ($args as $arg) { if (is_array($arg) && array_key_exists(0, $arg)) { diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php index 492438add..1479b7da6 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php @@ -46,6 +46,9 @@ class Confidence /** @var float $temp */ $temp = Distributions\StandardNormal::inverse(1 - $alpha / 2); - return Functions::scalar($temp * $stdDev / sqrt($size)); + /** @var float */ + $result = Functions::scalar($temp * $stdDev / sqrt($size)); + + return $result; } } diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php index edc50ad7a..911ead084 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php @@ -181,10 +181,10 @@ class ChiSquared * (of observed and expected frequencies), are likely to be simply due to sampling error, * or if they are likely to be real. * - * @param mixed $actual an array of observed frequencies - * @param mixed $expected an array of expected frequencies + * @param array $actual an array of observed frequencies + * @param array $expected an array of expected frequencies */ - public static function test(mixed $actual, mixed $expected): float|string + public static function test($actual, $expected): float|string { $rows = count($actual); $actual = Functions::flattenArray($actual); @@ -209,6 +209,7 @@ class ChiSquared $degrees = self::degrees($rows, $columns); + /** @var float|string */ $result = Functions::scalar(self::distributionRightTail($result, $degrees)); return $result; diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php index 9ad10dbc7..bf2f9f16c 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php @@ -30,7 +30,7 @@ class Fisher } try { - DistributionValidations::validateFloat($value); + $value = DistributionValidations::validateFloat($value); } catch (Exception $e) { return $e->getMessage(); } @@ -62,7 +62,7 @@ class Fisher } try { - DistributionValidations::validateFloat($probability); + $probability = DistributionValidations::validateFloat($probability); } catch (Exception $e) { return $e->getMessage(); } diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php index 365001fd6..8218beb42 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php @@ -24,6 +24,9 @@ class Trends /** * @param mixed $array1 should be array, but scalar is made into one * @param mixed $array2 should be array, but scalar is made into one + * + * @param-out array $array1 + * @param-out array $array2 */ private static function checkTrendArrays(mixed &$array1, mixed &$array2): void { diff --git a/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php b/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php index 016f0b3a9..f367692b1 100644 --- a/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php +++ b/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xls; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard; class ConditionalHelper @@ -35,9 +36,7 @@ class ConditionalHelper $this->tokens = pack('Cv', 0x1E, $condition); } else { try { - /** @var float|int|string */ - $conditionx = $condition; // @phpstan-ignore-line - $formula = Wizard\WizardAbstract::reverseAdjustCellRef((string) $conditionx, $cellRange); + $formula = Wizard\WizardAbstract::reverseAdjustCellRef(StringHelper::convertToString($condition), $cellRange); $this->parser->parse($formula); $this->tokens = $this->parser->toReversePolish(); $this->size = strlen($this->tokens ?? ''); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiTestTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiTestTest.php index ea3b2ab0c..ffefeac88 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiTestTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/ChiTestTest.php @@ -5,13 +5,14 @@ declare(strict_types=1); namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical; use PhpOffice\PhpSpreadsheet\Calculation\Statistical; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; // TODO Convert to Spreadsheet context. class ChiTestTest extends TestCase { - #[\PHPUnit\Framework\Attributes\DataProvider('providerCHITEST')] - public function testCHITEST(mixed $expectedResult, mixed $actual, mixed $expected): void + #[DataProvider('providerCHITEST')] + public function testCHITEST(mixed $expectedResult, array $actual, array $expected): void { $result = Statistical\Distributions\ChiSquared::test($actual, $expected); self::assertEqualsWithDelta($expectedResult, $result, 1E-12); From 4175edc5d42fa37b2955e2cfadce8dae5c513857 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:45:19 +0000 Subject: [PATCH 29/32] Bump friendsofphp/php-cs-fixer from 3.70.0 to 3.75.0 Bumps [friendsofphp/php-cs-fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) from 3.70.0 to 3.75.0. - [Release notes](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases) - [Changelog](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/CHANGELOG.md) - [Commits](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/compare/v3.70.0...v3.75.0) --- updated-dependencies: - dependency-name: friendsofphp/php-cs-fixer dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- composer.lock | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/composer.lock b/composer.lock index a540446f2..3c3f70426 100644 --- a/composer.lock +++ b/composer.lock @@ -1051,16 +1051,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.70.0", + "version": "v3.75.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "2ecd5aae0edc937f0d5aa4a22d1d705c6b2e084e" + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2ecd5aae0edc937f0d5aa4a22d1d705c6b2e084e", - "reference": "2ecd5aae0edc937f0d5aa4a22d1d705c6b2e084e", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", "shasum": "" }, "require": { @@ -1068,6 +1068,7 @@ "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.3", "ext-filter": "*", + "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", "fidry/cpu-core-counter": "^1.2", @@ -1090,18 +1091,18 @@ "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.5", - "infection/infection": "^0.29.10", - "justinrainbow/json-schema": "^5.3 || ^6.0", + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.7", - "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.0", - "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.0" + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -1142,7 +1143,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.70.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" }, "funding": [ { @@ -1150,7 +1151,7 @@ "type": "github" } ], - "time": "2025-02-22T23:30:51+00:00" + "time": "2025-03-31T18:40:42+00:00" }, { "name": "masterminds/html5", @@ -4191,16 +4192,16 @@ }, { "name": "symfony/console", - "version": "v6.4.17", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04" + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04", + "url": "https://api.github.com/repos/symfony/console/zipball/2e4af9c952617cc3f9559ff706aee420a8464c36", + "reference": "2e4af9c952617cc3f9559ff706aee420a8464c36", "shasum": "" }, "require": { @@ -4265,7 +4266,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.17" + "source": "https://github.com/symfony/console/tree/v6.4.20" }, "funding": [ { @@ -4281,7 +4282,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T12:07:30+00:00" + "time": "2025-03-03T17:16:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5179,16 +5180,16 @@ }, { "name": "symfony/process", - "version": "v6.4.19", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3" + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", - "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { @@ -5220,7 +5221,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.19" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -5236,7 +5237,7 @@ "type": "tidelift" } ], - "time": "2025-02-04T13:35:48+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { "name": "symfony/service-contracts", From 5b3bd85d295d9ca92c94dd9ee8a29392df3ab8d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:45:27 +0000 Subject: [PATCH 30/32] Bump squizlabs/php_codesniffer from 3.11.3 to 3.12.0 Bumps [squizlabs/php_codesniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) from 3.11.3 to 3.12.0. - [Release notes](https://github.com/PHPCSStandards/PHP_CodeSniffer/releases) - [Changelog](https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/CHANGELOG.md) - [Commits](https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.11.3...3.12.0) --- updated-dependencies: - dependency-name: squizlabs/php_codesniffer dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index a540446f2..64081ef35 100644 --- a/composer.lock +++ b/composer.lock @@ -4107,16 +4107,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.3", + "version": "3.12.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10" + "reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", - "reference": "ba05f990e79cbe69b9f35c8c1ac8dca7eecc3a10", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/2d1b63db139c3c6ea0c927698e5160f8b3b8d630", + "reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630", "shasum": "" }, "require": { @@ -4183,11 +4183,11 @@ "type": "open_collective" }, { - "url": "https://thanks.dev/phpcsstandards", + "url": "https://thanks.dev/u/gh/phpcsstandards", "type": "thanks_dev" } ], - "time": "2025-01-23T17:04:15+00:00" + "time": "2025-03-18T05:04:51+00:00" }, { "name": "symfony/console", From 349e33899330b014b1d0a96b1f2c5a2a97d2d093 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:45:38 +0000 Subject: [PATCH 31/32] Bump tecnickcom/tcpdf from 6.8.2 to 6.9.0 Bumps [tecnickcom/tcpdf](https://github.com/tecnickcom/TCPDF) from 6.8.2 to 6.9.0. - [Changelog](https://github.com/tecnickcom/TCPDF/blob/main/CHANGELOG.TXT) - [Commits](https://github.com/tecnickcom/TCPDF/compare/6.8.2...6.9.0) --- updated-dependencies: - dependency-name: tecnickcom/tcpdf dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- composer.lock | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index a540446f2..0b75bb575 100644 --- a/composer.lock +++ b/composer.lock @@ -5471,16 +5471,16 @@ }, { "name": "tecnickcom/tcpdf", - "version": "6.8.2", + "version": "6.9.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "f7a781073e1645062f163e058139e2f89355d420" + "reference": "f67b761b61f2370a9000d98c3b9111284544f722" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/f7a781073e1645062f163e058139e2f89355d420", - "reference": "f7a781073e1645062f163e058139e2f89355d420", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/f67b761b61f2370a9000d98c3b9111284544f722", + "reference": "f67b761b61f2370a9000d98c3b9111284544f722", "shasum": "" }, "require": { @@ -5493,8 +5493,6 @@ "config", "include", "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", "tcpdf_barcodes_1d.php", "tcpdf_barcodes_2d.php", "include/tcpdf_colors.php", @@ -5532,7 +5530,7 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.8.2" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.9.0" }, "funding": [ { @@ -5540,7 +5538,7 @@ "type": "custom" } ], - "time": "2025-01-26T14:03:12+00:00" + "time": "2025-03-30T16:56:09+00:00" }, { "name": "theseer/tokenizer", From ecbd702628cea545c2502d9f23f8b022c509e353 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 5 Apr 2025 13:54:20 -0700 Subject: [PATCH 32/32] Phpstan and Samples Phpstan hasn't identified any errors in Samples in a long time. But, as it turns out, one particular error is suppressed: ``` Variable $helper might not be defined. ``` This error would be generated by almost all samples, and the errors that it suppresses will become problematic if we move to Level 10. We are not yet committed to doing that, but it is pretty easy to write a script to change the samples so that error no longer happens, and there isn't really any reason to delay doing so. The results of that script constitute this PR. --- phpstan.neon.dist | 1 - samples/Autofilter/10_Autofilter.php | 1 + samples/Autofilter/10_Autofilter_dynamic_dates.php | 1 + samples/Autofilter/10_Autofilter_selection_1.php | 1 + samples/Autofilter/10_Autofilter_selection_2.php | 1 + samples/Autofilter/10_Autofilter_selection_display.php | 1 + samples/Basic/01_Simple.php | 2 +- samples/Basic/02_Types.php | 1 + samples/Basic/03_Formulas.php | 1 + samples/Basic/04_Printing.php | 1 + samples/Basic/05_Feature_demo.php | 1 + samples/Basic/05_UnexpectedCharacters.php | 1 + samples/Basic/06_Largescale.php | 2 +- samples/Basic/07_Reader.php | 1 + samples/Basic/08_Conditional_formatting.php | 1 + samples/Basic/08_Conditional_formatting_2.php | 1 + samples/Basic/09_Pagebreaks.php | 1 + samples/Basic1/11_Documentsecurity.php | 1 + samples/Basic1/12_CellProtection.php | 1 + samples/Basic1/13_Calculation.php | 1 + samples/Basic1/13_CalculationCyclicFormulae.php | 1 + samples/Basic1/14_Xls.php | 1 + samples/Basic1/15_Datavalidation.php | 1 + samples/Basic1/16_Csv.php | 1 + samples/Basic1/17_Html.php | 1 + samples/Basic1/17a_Html.php | 1 + samples/Basic1/17b_Html.php | 1 + samples/Basic1/18_Extendedcalculation.php | 1 + samples/Basic1/19_Namedrange.php | 1 + samples/Basic2/20_Read_Excel2003XML.php | 2 +- samples/Basic2/20_Read_Gnumeric.php | 2 +- samples/Basic2/20_Read_Ods.php | 2 +- samples/Basic2/20_Read_Sylk.php | 2 +- samples/Basic2/20_Read_Xls.php | 2 +- samples/Basic2/22_Heavily_formatted.php | 1 + samples/Basic2/23_Sharedstyles.php | 1 + samples/Basic2/24_Readfilter.php | 1 + samples/Basic2/25_In_memory_image.php | 1 + samples/Basic2/26_Utf8.php | 1 + samples/Basic2/27_Images_Html_Pdf.php | 1 + samples/Basic2/27_Images_Xls.php | 1 + samples/Basic2/27_Images_Xlsx.php | 1 + samples/Basic2/28_Iterator.php | 2 +- samples/Basic2/29_Advanced_value_binder.php | 1 + samples/Basic3/30_Template.php | 2 +- samples/Basic3/30_Templatebiff5.php | 2 +- samples/Basic3/31_Document_properties_write.php | 2 +- samples/Basic3/31_Document_properties_write_xls.php | 2 +- samples/Basic3/37_Page_layout_view.php | 1 + samples/Basic3/38_Clone_worksheet.php | 1 + samples/Basic3/39_Dropdown.php | 1 + samples/Basic4/40_Duplicate_style.php | 2 +- samples/Basic4/41_Password.php | 1 + samples/Basic4/42_RichText.php | 1 + samples/Basic4/43_Merge_workbooks.php | 2 +- samples/Basic4/44_Worksheet_info.php | 1 + samples/Basic4/45_Quadratic_equation_solver.php | 2 +- samples/Basic4/46_ReadHtml.php | 2 +- samples/Basic4/47_xlsfill.php | 1 + samples/Basic4/47_xlsxfill.php | 1 + samples/Basic4/48_Image_move_size_with_cells.php | 2 +- samples/Basic4/49_alignment.php | 2 +- samples/Basic4/50_xlsverticalbreak.php | 1 + samples/Basic4/51_ProtectedSort.php | 1 + samples/Basic4/52_Currency.php | 1 + samples/Basic4/53_ImageOpacity.php | 1 + samples/Bitwise/BITAND.php | 2 +- samples/Bitwise/BITLSHIFT.php | 2 +- samples/Bitwise/BITOR.php | 2 +- samples/Bitwise/BITRSHIFT.php | 2 +- samples/Bitwise/BITXOR.php | 2 +- samples/Chart/32_Chart_read_write.php | 2 +- samples/Chart/32_Chart_read_write_HTML.php | 1 + samples/Chart/32_Chart_read_write_PDF.php | 2 +- samples/Chart/34_Chart_update.php | 1 + samples/Chart/35_Chart_render.php | 1 + samples/Chart/35_Chart_render33.php | 1 + samples/Chart/37_Chart_dynamic_title.php | 2 +- samples/Chart33a/33_Chart_create_area.php | 2 +- samples/Chart33a/33_Chart_create_area_2.php | 2 +- samples/Chart33a/33_Chart_create_bar.php | 2 +- samples/Chart33a/33_Chart_create_bar_custom_colors.php | 2 +- samples/Chart33a/33_Chart_create_bar_labels_lines.php | 2 +- samples/Chart33a/33_Chart_create_bar_stacked.php | 2 +- samples/Chart33a/33_Chart_create_bubble.php | 2 +- samples/Chart33a/33_Chart_create_column.php | 2 +- samples/Chart33a/33_Chart_create_column_2.php | 2 +- samples/Chart33a/33_Chart_create_composite.alternate.php | 2 +- samples/Chart33a/33_Chart_create_composite.php | 2 +- samples/Chart33a/33_Chart_create_line.php | 2 +- samples/Chart33a/33_Chart_create_line_dateaxis.php | 2 +- samples/Chart33b/33_Chart_create_multiple_charts.php | 2 +- samples/Chart33b/33_Chart_create_pie.php | 2 +- samples/Chart33b/33_Chart_create_pie_custom_colors.php | 2 +- samples/Chart33b/33_Chart_create_radar.php | 2 +- samples/Chart33b/33_Chart_create_scatter.php | 2 +- samples/Chart33b/33_Chart_create_scatter2.php | 2 +- samples/Chart33b/33_Chart_create_scatter3.php | 2 +- samples/Chart33b/33_Chart_create_scatter4.php | 2 +- samples/Chart33b/33_Chart_create_scatter5_trendlines.php | 2 +- samples/Chart33b/33_Chart_create_scatter6_value_xaxis.php | 2 +- samples/Chart33b/33_Chart_create_scatter7_blanks.php | 2 +- samples/Chart33b/33_Chart_create_stock.php | 2 +- samples/Chart33b/33_Chart_create_stock2.php | 2 +- samples/ComplexNumbers1/COMPLEX.php | 2 +- samples/ComplexNumbers1/IMABS.php | 2 +- samples/ComplexNumbers1/IMAGINARY.php | 2 +- samples/ComplexNumbers1/IMARGUMENT.php | 2 +- samples/ComplexNumbers1/IMCONJUGATE.php | 2 +- samples/ComplexNumbers1/IMREAL.php | 2 +- samples/ComplexNumbers2/IMCOS.php | 2 +- samples/ComplexNumbers2/IMCOSH.php | 2 +- samples/ComplexNumbers2/IMCOT.php | 2 +- samples/ComplexNumbers2/IMCSC.php | 2 +- samples/ComplexNumbers2/IMCSCH.php | 2 +- samples/ComplexNumbers2/IMDIV.php | 2 +- samples/ComplexNumbers2/IMEXP.php | 2 +- samples/ComplexNumbers2/IMLN.php | 2 +- samples/ComplexNumbers2/IMLOG10.php | 2 +- samples/ComplexNumbers2/IMLOG2.php | 2 +- samples/ComplexNumbers3/IMPOWER.php | 2 +- samples/ComplexNumbers3/IMPRODUCT.php | 2 +- samples/ComplexNumbers3/IMSEC.php | 2 +- samples/ComplexNumbers3/IMSECH.php | 2 +- samples/ComplexNumbers3/IMSIN.php | 2 +- samples/ComplexNumbers3/IMSINH.php | 2 +- samples/ComplexNumbers3/IMSQRT.php | 2 +- samples/ComplexNumbers3/IMSUB.php | 2 +- samples/ComplexNumbers3/IMSUM.php | 2 +- samples/ComplexNumbers3/IMTAN.php | 2 +- samples/ConditionalFormatting/01_Basic_Comparisons.php | 1 + samples/ConditionalFormatting/02_Text_Comparisons.php | 1 + samples/ConditionalFormatting/03_Blank_Comparisons.php | 1 + samples/ConditionalFormatting/04_Error_Comparisons.php | 1 + samples/ConditionalFormatting/05_Date_Comparisons.php | 1 + samples/ConditionalFormatting/06_Duplicate_Comparisons.php | 1 + samples/ConditionalFormatting/07_Expression_Comparisons.php | 1 + samples/ConditionalFormatting/cond08_colorscale.php | 1 + samples/Database/DAVERAGE.php | 2 +- samples/Database/DCOUNT.php | 2 +- samples/Database/DCOUNTA.php | 2 +- samples/Database/DGET.php | 2 +- samples/Database/DMAX.php | 2 +- samples/Database/DMIN.php | 2 +- samples/Database/DPRODUCT.php | 2 +- samples/Database/DSTDEV.php | 2 +- samples/Database/DSTDEVP.php | 2 +- samples/Database/DSUM.php | 2 +- samples/Database/DVAR.php | 2 +- samples/Database/DVARP.php | 2 +- samples/DateTime/DATE.php | 2 +- samples/DateTime/DATEDIF.php | 2 +- samples/DateTime/DATEVALUE.php | 2 +- samples/DateTime/DAY.php | 2 +- samples/DateTime/DAYS.php | 2 +- samples/DateTime/DAYS360.php | 2 +- samples/DateTime/EDATE.php | 2 +- samples/DateTime/EOMONTH.php | 2 +- samples/DateTime/HOUR.php | 2 +- samples/DateTime/ISOWEEKNUM.php | 2 +- samples/DateTime/MINUTE.php | 2 +- samples/DateTime/MONTH.php | 2 +- samples/DateTime2/NETWORKDAYS.php | 2 +- samples/DateTime2/NOW.php | 2 +- samples/DateTime2/SECOND.php | 2 +- samples/DateTime2/TIME.php | 2 +- samples/DateTime2/TIMEVALUE.php | 2 +- samples/DateTime2/TODAY.php | 2 +- samples/DateTime2/WEEKDAY.php | 2 +- samples/DateTime2/WEEKNUM.php | 2 +- samples/DateTime2/WORKDAY.php | 2 +- samples/DateTime2/YEAR.php | 2 +- samples/DateTime2/YEARFRAC.php | 2 +- samples/DefinedNames/AbsoluteNamedRange.php | 2 +- samples/DefinedNames/CrossWorksheetNamedFormula.php | 2 +- samples/DefinedNames/NamedFormulaeAndRanges.php | 2 +- samples/DefinedNames/RelativeNamedRange.php | 2 +- samples/DefinedNames/RelativeNamedRange2.php | 2 +- samples/DefinedNames/RelativeNamedRangeAsFunction.php | 2 +- samples/DefinedNames/ScopedNamedRange.php | 2 +- samples/DefinedNames/ScopedNamedRange2.php | 2 +- samples/DefinedNames/SimpleNamedFormula.php | 2 +- samples/DefinedNames/SimpleNamedRange.php | 2 +- samples/Engineering/BESSELI.php | 2 +- samples/Engineering/BESSELJ.php | 2 +- samples/Engineering/BESSELK.php | 2 +- samples/Engineering/BESSELY.php | 2 +- samples/Engineering/CONVERT.php | 2 +- samples/Engineering/Convert-Online.php | 2 +- samples/Engineering/DELTA.php | 2 +- samples/Engineering/ERF.php | 2 +- samples/Engineering/ERFC.php | 2 +- samples/Engineering/GESTEP.php | 2 +- samples/Financial1/ACCRINT.php | 2 +- samples/Financial1/ACCRINTM.php | 2 +- samples/Financial1/AMORDEGRC.php | 2 +- samples/Financial1/AMORLINC.php | 2 +- samples/Financial1/COUPDAYBS.php | 2 +- samples/Financial1/COUPDAYS.php | 2 +- samples/Financial1/COUPDAYSNC.php | 2 +- samples/Financial1/COUPNCD.php | 2 +- samples/Financial1/COUPNUM.php | 2 +- samples/Financial1/COUPPCD.php | 2 +- samples/Financial1/CUMIPMT.php | 2 +- samples/Financial1/CUMPRINC.php | 2 +- samples/Financial2/DB.php | 2 +- samples/Financial2/DDB.php | 2 +- samples/Financial2/DISC.php | 2 +- samples/Financial2/DOLLARDE.php | 2 +- samples/Financial2/DOLLARFR.php | 2 +- samples/Financial2/EFFECT.php | 2 +- samples/Financial2/FV.php | 2 +- samples/Financial2/FVSCHEDULE.php | 2 +- samples/Financial3/INTRATE.php | 2 +- samples/Financial3/IPMT.php | 2 +- samples/Financial3/IRR.php | 2 +- samples/Financial3/ISPMT.php | 2 +- samples/Financial3/MIRR.php | 2 +- samples/Financial3/NOMINAL.php | 2 +- samples/Financial3/NPER.php | 2 +- samples/Financial3/NPV.php | 2 +- samples/HexEtcConversions/BIN2DEC.php | 2 +- samples/HexEtcConversions/BIN2HEX.php | 2 +- samples/HexEtcConversions/BIN2OCT.php | 2 +- samples/HexEtcConversions/DEC2BIN.php | 2 +- samples/HexEtcConversions/DEC2HEX.php | 2 +- samples/HexEtcConversions/DEC2OCT.php | 2 +- samples/HexEtcConversions/HEX2BIN.php | 2 +- samples/HexEtcConversions/HEX2DEC.php | 2 +- samples/HexEtcConversions/HEX2OCT.php | 2 +- samples/HexEtcConversions/OCT2BIN.php | 2 +- samples/HexEtcConversions/OCT2DEC.php | 2 +- samples/HexEtcConversions/OCT2HEX.php | 2 +- samples/Html/html_01_Basic_Conditional_Formatting.php | 2 +- samples/Html/html_02_More_Conditional_Formatting.php | 2 +- samples/Html/html_03_Color_Scale.php | 2 +- samples/Html/html_04_Table_Format_without_Conditional.php | 2 +- samples/Html/html_05_Table_Format_with_Conditional.php | 2 +- samples/LookupRef/ADDRESS.php | 2 +- samples/LookupRef/COLUMN.php | 2 +- samples/LookupRef/COLUMNS.php | 2 +- samples/LookupRef/INDEX.php | 2 +- samples/LookupRef/INDIRECT.php | 2 +- samples/LookupRef/OFFSET.php | 2 +- samples/LookupRef/ROW.php | 2 +- samples/LookupRef/ROWS.php | 2 +- samples/LookupRef/VLOOKUP.php | 2 +- samples/Pdf/21_Pdf_Domdf.php | 1 + samples/Pdf/21_Pdf_TCPDF.php | 1 + samples/Pdf/21_Pdf_mPDF.php | 1 + samples/Pdf/21a_Pdf.php | 1 + samples/Pdf/21b_Pdf.php | 1 + samples/Pdf/21c_Pdf.php | 1 + samples/Pdf/21d_FitToHeightPdf.php | 1 + samples/Pdf/21e_UnusualFont_mpdf.php | 1 + samples/Pdf/21f_Drawing_mpdf.php | 1 + samples/Reader/01_Simple_file_reader_using_IOFactory.php | 2 +- .../Reader/02_Simple_file_reader_using_a_specified_reader.php | 1 + ...ple_file_reader_using_the_IOFactory_to_return_a_reader.php | 2 +- ...reader_using_the_IOFactory_to_identify_a_reader_to_use.php | 2 +- .../05_Simple_file_reader_using_the_read_data_only_option.php | 2 +- .../Reader/06_Simple_file_reader_loading_all_worksheets.php | 2 +- ...07_Simple_file_reader_loading_a_single_named_worksheet.php | 2 +- ...08_Simple_file_reader_loading_several_named_worksheets.php | 1 + samples/Reader/09_Simple_file_reader_using_a_read_filter.php | 2 +- ...10_Simple_file_reader_using_a_configurable_read_filter.php | 2 +- ...in_chunks_using_a_configurable_read_filter_(version_1).php | 2 +- ...in_chunks_using_a_configurable_read_filter_(version_2).php | 2 +- .../Reader2/13_Simple_file_reader_for_multiple_CSV_files.php | 2 +- ...CSV_file_in_chunks_to_split_across_multiple_worksheets.php | 2 +- ...b_separated_value_file_using_the_Advanced_Value_Binder.php | 2 +- .../Reader2/16_Handling_loader_exceptions_using_TryCatch.php | 2 +- ...17_Simple_file_reader_loading_several_named_worksheets.php | 2 +- ...Reading_list_of_worksheets_without_loading_entire_file.php | 2 +- ...ding_worksheet_information_without_loading_entire_file.php | 2 +- samples/Reader2/20_Reader_worksheet_hyperlink_image.php | 1 + .../21_Reader_CSV_Long_Integers_with_String_Value_Binder.php | 2 +- samples/Reader2/22_Reader_formscomments.php | 2 +- samples/Reader2/22_Reader_issue1767.php | 2 +- samples/Reader2/23_iterateRowsYield.php | 2 +- samples/Reading_workbook_data/Custom_properties.php | 2 +- samples/Reading_workbook_data/Custom_property_names.php | 2 +- samples/Reading_workbook_data/Properties.php | 2 +- samples/Reading_workbook_data/Worksheet_count_and_names.php | 2 +- samples/Table/01_Table.php | 1 + samples/Table/02_Table_Total.php | 1 + samples/Table/03_Column_Formula.php | 1 + samples/Table/04_Column_Formula_with_Totals.php | 1 + samples/Wizards/NumberFormat/Accounting.php | 2 +- samples/Wizards/NumberFormat/Currency.php | 2 +- samples/Wizards/NumberFormat/Number.php | 2 +- samples/Wizards/NumberFormat/Percentage.php | 2 +- samples/Wizards/NumberFormat/Scientific.php | 2 +- samples/index.php | 3 ++- samples/templates/chartSpreadsheet.php | 1 + samples/templates/largeSpreadsheet.php | 1 + samples/templates/sampleSpreadsheet.php | 1 + samples/templates/sampleSpreadsheet2.php | 1 + src/PhpSpreadsheet/Helper/Sample.php | 4 ++-- 299 files changed, 300 insertions(+), 221 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 279cfdabd..aa465103d 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -28,5 +28,4 @@ parameters: ignoreErrors: # Accept a bit anything for assert methods - '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~' - - '~^Variable \$helper might not be defined\.$~' - identifier: missingType.iterableValue diff --git a/samples/Autofilter/10_Autofilter.php b/samples/Autofilter/10_Autofilter.php index cea1e1b9c..4002f1806 100644 --- a/samples/Autofilter/10_Autofilter.php +++ b/samples/Autofilter/10_Autofilter.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Autofilter/10_Autofilter_dynamic_dates.php b/samples/Autofilter/10_Autofilter_dynamic_dates.php index ef9a2c780..54ec63b01 100644 --- a/samples/Autofilter/10_Autofilter_dynamic_dates.php +++ b/samples/Autofilter/10_Autofilter_dynamic_dates.php @@ -67,6 +67,7 @@ function createSheet(Sample $helper, Spreadsheet $spreadsheet, string $rule, boo } // Create new Spreadsheet object +/** @var Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); diff --git a/samples/Autofilter/10_Autofilter_selection_1.php b/samples/Autofilter/10_Autofilter_selection_1.php index f5b1729a1..ce1bcd435 100644 --- a/samples/Autofilter/10_Autofilter_selection_1.php +++ b/samples/Autofilter/10_Autofilter_selection_1.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Autofilter/10_Autofilter_selection_2.php b/samples/Autofilter/10_Autofilter_selection_2.php index 6b82bcdf5..456f149f0 100644 --- a/samples/Autofilter/10_Autofilter_selection_2.php +++ b/samples/Autofilter/10_Autofilter_selection_2.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Autofilter/10_Autofilter_selection_display.php b/samples/Autofilter/10_Autofilter_selection_display.php index dd98dbc92..c2d8f871c 100644 --- a/samples/Autofilter/10_Autofilter_selection_display.php +++ b/samples/Autofilter/10_Autofilter_selection_display.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column; use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic/01_Simple.php b/samples/Basic/01_Simple.php index 1ab182e16..c61c1d660 100644 --- a/samples/Basic/01_Simple.php +++ b/samples/Basic/01_Simple.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); diff --git a/samples/Basic/02_Types.php b/samples/Basic/02_Types.php index d28af579d..865b1d267 100644 --- a/samples/Basic/02_Types.php +++ b/samples/Basic/02_Types.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic/03_Formulas.php b/samples/Basic/03_Formulas.php index 0163e828c..4d8aaf4d4 100644 --- a/samples/Basic/03_Formulas.php +++ b/samples/Basic/03_Formulas.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic/04_Printing.php b/samples/Basic/04_Printing.php index 5e90fc919..936e01a01 100644 --- a/samples/Basic/04_Printing.php +++ b/samples/Basic/04_Printing.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic/05_Feature_demo.php b/samples/Basic/05_Feature_demo.php index a85ebbc27..0b06d0637 100644 --- a/samples/Basic/05_Feature_demo.php +++ b/samples/Basic/05_Feature_demo.php @@ -1,6 +1,7 @@ log('Create new Spreadsheet object'); diff --git a/samples/Basic/08_Conditional_formatting_2.php b/samples/Basic/08_Conditional_formatting_2.php index 818cdd9f0..40b5e47c6 100644 --- a/samples/Basic/08_Conditional_formatting_2.php +++ b/samples/Basic/08_Conditional_formatting_2.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic/09_Pagebreaks.php b/samples/Basic/09_Pagebreaks.php index ab99a0790..7b81c7772 100644 --- a/samples/Basic/09_Pagebreaks.php +++ b/samples/Basic/09_Pagebreaks.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic1/11_Documentsecurity.php b/samples/Basic1/11_Documentsecurity.php index 2fa7abebd..567445a75 100644 --- a/samples/Basic1/11_Documentsecurity.php +++ b/samples/Basic1/11_Documentsecurity.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic1/12_CellProtection.php b/samples/Basic1/12_CellProtection.php index 8a1b2a0b6..b30f7b0c9 100644 --- a/samples/Basic1/12_CellProtection.php +++ b/samples/Basic1/12_CellProtection.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Protection; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic1/13_Calculation.php b/samples/Basic1/13_Calculation.php index 688cb9de4..3fcf808c6 100644 --- a/samples/Basic1/13_Calculation.php +++ b/samples/Basic1/13_Calculation.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; mt_srand(1234567890); require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // List functions $helper->log('List implemented functions'); diff --git a/samples/Basic1/13_CalculationCyclicFormulae.php b/samples/Basic1/13_CalculationCyclicFormulae.php index 7e7ea80f0..5f0fc3632 100644 --- a/samples/Basic1/13_CalculationCyclicFormulae.php +++ b/samples/Basic1/13_CalculationCyclicFormulae.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic1/14_Xls.php b/samples/Basic1/14_Xls.php index ce27eb8cd..2c5b84095 100644 --- a/samples/Basic1/14_Xls.php +++ b/samples/Basic1/14_Xls.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $filename = $helper->getFilename(__FILE__, 'xls'); diff --git a/samples/Basic1/15_Datavalidation.php b/samples/Basic1/15_Datavalidation.php index c2804cfbb..619d03235 100644 --- a/samples/Basic1/15_Datavalidation.php +++ b/samples/Basic1/15_Datavalidation.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic1/16_Csv.php b/samples/Basic1/16_Csv.php index 381d8c79d..711f86ab0 100644 --- a/samples/Basic1/16_Csv.php +++ b/samples/Basic1/16_Csv.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Csv as CsvReader; use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Write to CSV format'); diff --git a/samples/Basic1/17_Html.php b/samples/Basic1/17_Html.php index 54f031ece..2c973634f 100644 --- a/samples/Basic1/17_Html.php +++ b/samples/Basic1/17_Html.php @@ -1,6 +1,7 @@ getProperties()->setTitle('Non-embedded images'); diff --git a/samples/Basic1/17a_Html.php b/samples/Basic1/17a_Html.php index 5804c77c7..d39c81b3d 100644 --- a/samples/Basic1/17a_Html.php +++ b/samples/Basic1/17a_Html.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Html; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $spreadsheet->getProperties()->setTitle('Embedded images'); diff --git a/samples/Basic1/17b_Html.php b/samples/Basic1/17b_Html.php index 0e677e3ed..7480a8ac0 100644 --- a/samples/Basic1/17b_Html.php +++ b/samples/Basic1/17b_Html.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Html; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; function changeGridlines(string $html): string diff --git a/samples/Basic1/18_Extendedcalculation.php b/samples/Basic1/18_Extendedcalculation.php index ba78ddcae..5acc1a594 100644 --- a/samples/Basic1/18_Extendedcalculation.php +++ b/samples/Basic1/18_Extendedcalculation.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // List functions $helper->log('List implemented functions'); diff --git a/samples/Basic1/19_Namedrange.php b/samples/Basic1/19_Namedrange.php index 3ff07abf1..e4b79f995 100644 --- a/samples/Basic1/19_Namedrange.php +++ b/samples/Basic1/19_Namedrange.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic2/20_Read_Excel2003XML.php b/samples/Basic2/20_Read_Excel2003XML.php index 48ac3373c..dc74ccb1f 100644 --- a/samples/Basic2/20_Read_Excel2003XML.php +++ b/samples/Basic2/20_Read_Excel2003XML.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $filename = __DIR__ . '/../templates/excel2003.xml'; $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); diff --git a/samples/Basic2/20_Read_Gnumeric.php b/samples/Basic2/20_Read_Gnumeric.php index 2d6ce2215..76b5e3025 100644 --- a/samples/Basic2/20_Read_Gnumeric.php +++ b/samples/Basic2/20_Read_Gnumeric.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $filename = __DIR__ . '/../templates/GnumericTest.gnumeric'; $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); diff --git a/samples/Basic2/20_Read_Ods.php b/samples/Basic2/20_Read_Ods.php index 64f54827b..e9ec14f36 100644 --- a/samples/Basic2/20_Read_Ods.php +++ b/samples/Basic2/20_Read_Ods.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $filename = __DIR__ . '/../templates/OOCalcTest.ods'; $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); diff --git a/samples/Basic2/20_Read_Sylk.php b/samples/Basic2/20_Read_Sylk.php index 1a0645938..12a325919 100644 --- a/samples/Basic2/20_Read_Sylk.php +++ b/samples/Basic2/20_Read_Sylk.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $filename = __DIR__ . '/../templates/SylkTest.slk'; $callStartTime = microtime(true); $spreadsheet = IOFactory::load($filename); diff --git a/samples/Basic2/20_Read_Xls.php b/samples/Basic2/20_Read_Xls.php index daeaf6643..d070362ee 100644 --- a/samples/Basic2/20_Read_Xls.php +++ b/samples/Basic2/20_Read_Xls.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; // Write temporary file diff --git a/samples/Basic2/22_Heavily_formatted.php b/samples/Basic2/22_Heavily_formatted.php index aadb5b929..5fb293e5b 100644 --- a/samples/Basic2/22_Heavily_formatted.php +++ b/samples/Basic2/22_Heavily_formatted.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Fill; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic2/23_Sharedstyles.php b/samples/Basic2/23_Sharedstyles.php index 8e75a4880..5fc1555c5 100644 --- a/samples/Basic2/23_Sharedstyles.php +++ b/samples/Basic2/23_Sharedstyles.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic2/24_Readfilter.php b/samples/Basic2/24_Readfilter.php index 0f73818f9..7e40738a4 100644 --- a/samples/Basic2/24_Readfilter.php +++ b/samples/Basic2/24_Readfilter.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; require __DIR__ . '/../Header.php'; +/** @var Helper\Sample $helper */ // Write temporary file $largeSpreadsheet = require __DIR__ . '/../templates/largeSpreadsheet.php'; diff --git a/samples/Basic2/25_In_memory_image.php b/samples/Basic2/25_In_memory_image.php index 7ba4871b3..b6d483c18 100644 --- a/samples/Basic2/25_In_memory_image.php +++ b/samples/Basic2/25_In_memory_image.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing; use PhpOffice\PhpSpreadsheet\Writer\BaseWriter; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic2/26_Utf8.php b/samples/Basic2/26_Utf8.php index dac676a21..eb53fe605 100644 --- a/samples/Basic2/26_Utf8.php +++ b/samples/Basic2/26_Utf8.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Csv; use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Read from Xlsx (.xlsx) template $helper->log('Load Xlsx template file'); diff --git a/samples/Basic2/27_Images_Html_Pdf.php b/samples/Basic2/27_Images_Html_Pdf.php index 751775a78..3f8da7dda 100644 --- a/samples/Basic2/27_Images_Html_Pdf.php +++ b/samples/Basic2/27_Images_Html_Pdf.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Read from Xls (.xls) template $helper->log('Load Xlsx template file'); diff --git a/samples/Basic2/27_Images_Xls.php b/samples/Basic2/27_Images_Xls.php index b4cb29632..929be0894 100644 --- a/samples/Basic2/27_Images_Xls.php +++ b/samples/Basic2/27_Images_Xls.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Read from Xls (.xls) template $helper->log('Load Xls template file'); diff --git a/samples/Basic2/27_Images_Xlsx.php b/samples/Basic2/27_Images_Xlsx.php index ebeab3c55..370d7e35a 100644 --- a/samples/Basic2/27_Images_Xlsx.php +++ b/samples/Basic2/27_Images_Xlsx.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Read from Xlsx (.xls) template $helper->log('Load Xlsx template file'); diff --git a/samples/Basic2/28_Iterator.php b/samples/Basic2/28_Iterator.php index 953554af6..a1e61d0d2 100644 --- a/samples/Basic2/28_Iterator.php +++ b/samples/Basic2/28_Iterator.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $sampleSpreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $filename = $helper->getTemporaryFilename(); $writer = new XlsxWriter($sampleSpreadsheet); diff --git a/samples/Basic2/29_Advanced_value_binder.php b/samples/Basic2/29_Advanced_value_binder.php index 74c16c21a..07e9dccb0 100644 --- a/samples/Basic2/29_Advanced_value_binder.php +++ b/samples/Basic2/29_Advanced_value_binder.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Set timezone $helper->log('Set timezone'); diff --git a/samples/Basic3/30_Template.php b/samples/Basic3/30_Template.php index 409acb668..246a44ab7 100644 --- a/samples/Basic3/30_Template.php +++ b/samples/Basic3/30_Template.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Shared\Date; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Load from Xls template'); $reader = IOFactory::createReader('Xls'); $spreadsheet = $reader->load(__DIR__ . '/../templates/30template.xls'); diff --git a/samples/Basic3/30_Templatebiff5.php b/samples/Basic3/30_Templatebiff5.php index 53c4c2a83..a5272989e 100644 --- a/samples/Basic3/30_Templatebiff5.php +++ b/samples/Basic3/30_Templatebiff5.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Shared\Date; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Load from Xls template'); $reader = IOFactory::createReader('Xls'); $spreadsheet = $reader->load(__DIR__ . '/../templates/30templatebiff5.xls'); diff --git a/samples/Basic3/31_Document_properties_write.php b/samples/Basic3/31_Document_properties_write.php index 4fdd1cfa6..20ba4860f 100644 --- a/samples/Basic3/31_Document_properties_write.php +++ b/samples/Basic3/31_Document_properties_write.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xlsx'; $inputFileName = __DIR__ . '/../templates/31docproperties.xlsx'; diff --git a/samples/Basic3/31_Document_properties_write_xls.php b/samples/Basic3/31_Document_properties_write_xls.php index 5c4815519..03ce3b400 100644 --- a/samples/Basic3/31_Document_properties_write_xls.php +++ b/samples/Basic3/31_Document_properties_write_xls.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/../templates/31docproperties.xls'; diff --git a/samples/Basic3/37_Page_layout_view.php b/samples/Basic3/37_Page_layout_view.php index d9bac80a8..e54ccf299 100644 --- a/samples/Basic3/37_Page_layout_view.php +++ b/samples/Basic3/37_Page_layout_view.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\SheetView; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic3/38_Clone_worksheet.php b/samples/Basic3/38_Clone_worksheet.php index 83f2d9ce5..ce17a03c8 100644 --- a/samples/Basic3/38_Clone_worksheet.php +++ b/samples/Basic3/38_Clone_worksheet.php @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic3/39_Dropdown.php b/samples/Basic3/39_Dropdown.php index 1d516f618..3fdaa5554 100644 --- a/samples/Basic3/39_Dropdown.php +++ b/samples/Basic3/39_Dropdown.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Basic4/40_Duplicate_style.php b/samples/Basic4/40_Duplicate_style.php index 38f7fb495..deb365c2d 100644 --- a/samples/Basic4/40_Duplicate_style.php +++ b/samples/Basic4/40_Duplicate_style.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); diff --git a/samples/Basic4/41_Password.php b/samples/Basic4/41_Password.php index 9aa8e6db9..3f49ef49c 100644 --- a/samples/Basic4/41_Password.php +++ b/samples/Basic4/41_Password.php @@ -1,6 +1,7 @@ log('Create new Spreadsheet object'); diff --git a/samples/Basic4/43_Merge_workbooks.php b/samples/Basic4/43_Merge_workbooks.php index 28353cc60..80a2cd336 100644 --- a/samples/Basic4/43_Merge_workbooks.php +++ b/samples/Basic4/43_Merge_workbooks.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Load MergeBook1 from Xlsx file'); $filename1 = __DIR__ . '/../templates/43mergeBook1.xlsx'; $callStartTime = microtime(true); diff --git a/samples/Basic4/44_Worksheet_info.php b/samples/Basic4/44_Worksheet_info.php index 406c7be2f..f5b2e6543 100644 --- a/samples/Basic4/44_Worksheet_info.php +++ b/samples/Basic4/44_Worksheet_info.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx as Reader; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as Writer; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create temporary file that will be read $sampleSpreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; diff --git a/samples/Basic4/45_Quadratic_equation_solver.php b/samples/Basic4/45_Quadratic_equation_solver.php index adc8c714a..9cb974c67 100644 --- a/samples/Basic4/45_Quadratic_equation_solver.php +++ b/samples/Basic4/45_Quadratic_equation_solver.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Helper\Sample; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; require __DIR__ . '/../Header.php'; - +/** @var Sample $helper */ $helper = new Sample(); if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Basic4/46_ReadHtml.php b/samples/Basic4/46_ReadHtml.php index bd37af9b2..796733ecc 100644 --- a/samples/Basic4/46_ReadHtml.php +++ b/samples/Basic4/46_ReadHtml.php @@ -6,7 +6,7 @@ error_reporting(0); use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $html = __DIR__ . '/../templates/46readHtml.html'; $callStartTime = microtime(true); diff --git a/samples/Basic4/47_xlsfill.php b/samples/Basic4/47_xlsfill.php index 217d7dca6..57a39ec76 100644 --- a/samples/Basic4/47_xlsfill.php +++ b/samples/Basic4/47_xlsfill.php @@ -1,6 +1,7 @@ log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); diff --git a/samples/Basic4/49_alignment.php b/samples/Basic4/49_alignment.php index 83fdb3d4d..664f46926 100644 --- a/samples/Basic4/49_alignment.php +++ b/samples/Basic4/49_alignment.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); $spreadsheet->getProperties()->setTitle('Alignment'); diff --git a/samples/Basic4/50_xlsverticalbreak.php b/samples/Basic4/50_xlsverticalbreak.php index 680620fbf..b0d71915d 100644 --- a/samples/Basic4/50_xlsverticalbreak.php +++ b/samples/Basic4/50_xlsverticalbreak.php @@ -1,6 +1,7 @@ getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_area_2.php b/samples/Chart33a/33_Chart_create_area_2.php index 7d98ef445..41b342beb 100644 --- a/samples/Chart33a/33_Chart_create_area_2.php +++ b/samples/Chart33a/33_Chart_create_area_2.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Theme as SpreadsheetTheme; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); // same as 33_Chart_create_area, but with 2013+ schemes $spreadsheet->getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2013_PLUS_NAME); diff --git a/samples/Chart33a/33_Chart_create_bar.php b/samples/Chart33a/33_Chart_create_bar.php index a05af8cf4..a4fe2d898 100644 --- a/samples/Chart33a/33_Chart_create_bar.php +++ b/samples/Chart33a/33_Chart_create_bar.php @@ -1,7 +1,7 @@ getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_bar_labels_lines.php b/samples/Chart33a/33_Chart_create_bar_labels_lines.php index b3eab089d..1980643ab 100644 --- a/samples/Chart33a/33_Chart_create_bar_labels_lines.php +++ b/samples/Chart33a/33_Chart_create_bar_labels_lines.php @@ -13,7 +13,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_bar_stacked.php b/samples/Chart33a/33_Chart_create_bar_stacked.php index d0d68cae9..b1ee0a3ab 100644 --- a/samples/Chart33a/33_Chart_create_bar_stacked.php +++ b/samples/Chart33a/33_Chart_create_bar_stacked.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_bubble.php b/samples/Chart33a/33_Chart_create_bubble.php index 11cd8cf2e..e54eac136 100644 --- a/samples/Chart33a/33_Chart_create_bubble.php +++ b/samples/Chart33a/33_Chart_create_bubble.php @@ -8,7 +8,7 @@ use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_column.php b/samples/Chart33a/33_Chart_create_column.php index 784adf6e9..41e1dbcbe 100644 --- a/samples/Chart33a/33_Chart_create_column.php +++ b/samples/Chart33a/33_Chart_create_column.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_column_2.php b/samples/Chart33a/33_Chart_create_column_2.php index d21e21b18..7c15d3b95 100644 --- a/samples/Chart33a/33_Chart_create_column_2.php +++ b/samples/Chart33a/33_Chart_create_column_2.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_composite.alternate.php b/samples/Chart33a/33_Chart_create_composite.alternate.php index 799dbbc8a..9dfa8736b 100644 --- a/samples/Chart33a/33_Chart_create_composite.alternate.php +++ b/samples/Chart33a/33_Chart_create_composite.alternate.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_composite.php b/samples/Chart33a/33_Chart_create_composite.php index fb440648a..25ad08d95 100644 --- a/samples/Chart33a/33_Chart_create_composite.php +++ b/samples/Chart33a/33_Chart_create_composite.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_line.php b/samples/Chart33a/33_Chart_create_line.php index 11027cd8d..ed9674ebf 100644 --- a/samples/Chart33a/33_Chart_create_line.php +++ b/samples/Chart33a/33_Chart_create_line.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33a/33_Chart_create_line_dateaxis.php b/samples/Chart33a/33_Chart_create_line_dateaxis.php index 00eacbd0e..77da32601 100644 --- a/samples/Chart33a/33_Chart_create_line_dateaxis.php +++ b/samples/Chart33a/33_Chart_create_line_dateaxis.php @@ -8,7 +8,7 @@ use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $dataSheet = $spreadsheet->getActiveSheet(); $dataSheet->setTitle('Data'); diff --git a/samples/Chart33b/33_Chart_create_multiple_charts.php b/samples/Chart33b/33_Chart_create_multiple_charts.php index 57ede8063..99f9d6066 100644 --- a/samples/Chart33b/33_Chart_create_multiple_charts.php +++ b/samples/Chart33b/33_Chart_create_multiple_charts.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_pie.php b/samples/Chart33b/33_Chart_create_pie.php index 47f5edaef..3b55dc11f 100644 --- a/samples/Chart33b/33_Chart_create_pie.php +++ b/samples/Chart33b/33_Chart_create_pie.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_pie_custom_colors.php b/samples/Chart33b/33_Chart_create_pie_custom_colors.php index e056b2456..eea1e7953 100644 --- a/samples/Chart33b/33_Chart_create_pie_custom_colors.php +++ b/samples/Chart33b/33_Chart_create_pie_custom_colors.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_radar.php b/samples/Chart33b/33_Chart_create_radar.php index 4d140477d..251ec9541 100644 --- a/samples/Chart33b/33_Chart_create_radar.php +++ b/samples/Chart33b/33_Chart_create_radar.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_scatter.php b/samples/Chart33b/33_Chart_create_scatter.php index 779e42505..f1bc71de7 100644 --- a/samples/Chart33b/33_Chart_create_scatter.php +++ b/samples/Chart33b/33_Chart_create_scatter.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_scatter2.php b/samples/Chart33b/33_Chart_create_scatter2.php index 7c0608cd3..8eaef9ee3 100644 --- a/samples/Chart33b/33_Chart_create_scatter2.php +++ b/samples/Chart33b/33_Chart_create_scatter2.php @@ -13,7 +13,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); // changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date diff --git a/samples/Chart33b/33_Chart_create_scatter3.php b/samples/Chart33b/33_Chart_create_scatter3.php index 3b8c5e083..123d2cb16 100644 --- a/samples/Chart33b/33_Chart_create_scatter3.php +++ b/samples/Chart33b/33_Chart_create_scatter3.php @@ -12,7 +12,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); // changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date diff --git a/samples/Chart33b/33_Chart_create_scatter4.php b/samples/Chart33b/33_Chart_create_scatter4.php index 211a0908a..29168b917 100644 --- a/samples/Chart33b/33_Chart_create_scatter4.php +++ b/samples/Chart33b/33_Chart_create_scatter4.php @@ -10,7 +10,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_scatter5_trendlines.php b/samples/Chart33b/33_Chart_create_scatter5_trendlines.php index 727441d33..387453fa1 100644 --- a/samples/Chart33b/33_Chart_create_scatter5_trendlines.php +++ b/samples/Chart33b/33_Chart_create_scatter5_trendlines.php @@ -13,7 +13,7 @@ use PhpOffice\PhpSpreadsheet\Chart\TrendLine; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $dataSheet = $spreadsheet->getActiveSheet(); $dataSheet->setTitle('Data'); diff --git a/samples/Chart33b/33_Chart_create_scatter6_value_xaxis.php b/samples/Chart33b/33_Chart_create_scatter6_value_xaxis.php index 3c33001ff..c66e0594d 100644 --- a/samples/Chart33b/33_Chart_create_scatter6_value_xaxis.php +++ b/samples/Chart33b/33_Chart_create_scatter6_value_xaxis.php @@ -12,7 +12,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $dataSheet = $spreadsheet->getActiveSheet(); $dataSheet->setTitle('Data'); diff --git a/samples/Chart33b/33_Chart_create_scatter7_blanks.php b/samples/Chart33b/33_Chart_create_scatter7_blanks.php index 55305d741..d1a8df7b5 100644 --- a/samples/Chart33b/33_Chart_create_scatter7_blanks.php +++ b/samples/Chart33b/33_Chart_create_scatter7_blanks.php @@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_stock.php b/samples/Chart33b/33_Chart_create_stock.php index 75ee44f4e..428ff7f08 100644 --- a/samples/Chart33b/33_Chart_create_stock.php +++ b/samples/Chart33b/33_Chart_create_stock.php @@ -12,7 +12,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/Chart33b/33_Chart_create_stock2.php b/samples/Chart33b/33_Chart_create_stock2.php index bc9faf3db..d8ac5d698 100644 --- a/samples/Chart33b/33_Chart_create_stock2.php +++ b/samples/Chart33b/33_Chart_create_stock2.php @@ -12,7 +12,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray( diff --git a/samples/ComplexNumbers1/COMPLEX.php b/samples/ComplexNumbers1/COMPLEX.php index 6a6af5f60..7cc847d39 100644 --- a/samples/ComplexNumbers1/COMPLEX.php +++ b/samples/ComplexNumbers1/COMPLEX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'COMPLEX'; $description = 'Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj'; diff --git a/samples/ComplexNumbers1/IMABS.php b/samples/ComplexNumbers1/IMABS.php index 3096e1fce..9a2acb217 100644 --- a/samples/ComplexNumbers1/IMABS.php +++ b/samples/ComplexNumbers1/IMABS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMABS'; $description = 'Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers1/IMAGINARY.php b/samples/ComplexNumbers1/IMAGINARY.php index 587fe6642..d435db1ae 100644 --- a/samples/ComplexNumbers1/IMAGINARY.php +++ b/samples/ComplexNumbers1/IMAGINARY.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMAGINARY'; $description = 'Returns the imaginary coefficient of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers1/IMARGUMENT.php b/samples/ComplexNumbers1/IMARGUMENT.php index 1c100f7b0..66c9491f0 100644 --- a/samples/ComplexNumbers1/IMARGUMENT.php +++ b/samples/ComplexNumbers1/IMARGUMENT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMARGUMENT'; $description = 'Returns the argument Theta, an angle expressed in radians'; diff --git a/samples/ComplexNumbers1/IMCONJUGATE.php b/samples/ComplexNumbers1/IMCONJUGATE.php index 04f57f716..0f7537d4b 100644 --- a/samples/ComplexNumbers1/IMCONJUGATE.php +++ b/samples/ComplexNumbers1/IMCONJUGATE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCONJUGATE'; $description = 'Returns the complex conjugate of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers1/IMREAL.php b/samples/ComplexNumbers1/IMREAL.php index c2df07fac..0c4bb9ce6 100644 --- a/samples/ComplexNumbers1/IMREAL.php +++ b/samples/ComplexNumbers1/IMREAL.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMREAL'; $description = 'Returns the real coefficient of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMCOS.php b/samples/ComplexNumbers2/IMCOS.php index bf5c44421..f7c57c5ff 100644 --- a/samples/ComplexNumbers2/IMCOS.php +++ b/samples/ComplexNumbers2/IMCOS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCOS'; $description = 'Returns the cosine of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMCOSH.php b/samples/ComplexNumbers2/IMCOSH.php index 3ecf351a1..3f03cd51c 100644 --- a/samples/ComplexNumbers2/IMCOSH.php +++ b/samples/ComplexNumbers2/IMCOSH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCOSH'; $description = 'Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMCOT.php b/samples/ComplexNumbers2/IMCOT.php index 36e867f80..3c873ca5a 100644 --- a/samples/ComplexNumbers2/IMCOT.php +++ b/samples/ComplexNumbers2/IMCOT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCOT'; $description = 'Returns the cotangent of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMCSC.php b/samples/ComplexNumbers2/IMCSC.php index 7cc209760..36b820ceb 100644 --- a/samples/ComplexNumbers2/IMCSC.php +++ b/samples/ComplexNumbers2/IMCSC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCSC'; $description = 'Returns the cosecant of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMCSCH.php b/samples/ComplexNumbers2/IMCSCH.php index 1529e6f44..4c9cf81b3 100644 --- a/samples/ComplexNumbers2/IMCSCH.php +++ b/samples/ComplexNumbers2/IMCSCH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMCSCH'; $description = 'Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMDIV.php b/samples/ComplexNumbers2/IMDIV.php index 0e719e3dd..07d73c7ef 100644 --- a/samples/ComplexNumbers2/IMDIV.php +++ b/samples/ComplexNumbers2/IMDIV.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMDIV'; $description = 'Returns the quotient of two complex numbers in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMEXP.php b/samples/ComplexNumbers2/IMEXP.php index 1cc81a9f5..e312b378c 100644 --- a/samples/ComplexNumbers2/IMEXP.php +++ b/samples/ComplexNumbers2/IMEXP.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMEXP'; $description = 'Returns the exponential of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMLN.php b/samples/ComplexNumbers2/IMLN.php index e27156ca1..917cca797 100644 --- a/samples/ComplexNumbers2/IMLN.php +++ b/samples/ComplexNumbers2/IMLN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMLN'; $description = 'Returns the natural logarithm of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMLOG10.php b/samples/ComplexNumbers2/IMLOG10.php index 4a0cdac7e..57aac04be 100644 --- a/samples/ComplexNumbers2/IMLOG10.php +++ b/samples/ComplexNumbers2/IMLOG10.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMLOG10'; $description = 'Returns the base-10 logarithm of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers2/IMLOG2.php b/samples/ComplexNumbers2/IMLOG2.php index 787455e6c..d35ac2ae4 100644 --- a/samples/ComplexNumbers2/IMLOG2.php +++ b/samples/ComplexNumbers2/IMLOG2.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMLOG2'; $description = 'Returns the base-2 logarithm of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMPOWER.php b/samples/ComplexNumbers3/IMPOWER.php index 5c86bfb29..12ad32b7b 100644 --- a/samples/ComplexNumbers3/IMPOWER.php +++ b/samples/ComplexNumbers3/IMPOWER.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMPOWER'; $description = 'Returns a complex number in x + yi or x + yj text format raised to a power'; diff --git a/samples/ComplexNumbers3/IMPRODUCT.php b/samples/ComplexNumbers3/IMPRODUCT.php index 4fbacff97..d1ce4dac6 100644 --- a/samples/ComplexNumbers3/IMPRODUCT.php +++ b/samples/ComplexNumbers3/IMPRODUCT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMPRODUCT'; $description = 'Returns the product of two or more complex numbers in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSEC.php b/samples/ComplexNumbers3/IMSEC.php index c5395c7b8..5f8020ec5 100644 --- a/samples/ComplexNumbers3/IMSEC.php +++ b/samples/ComplexNumbers3/IMSEC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSEC'; $description = 'Returns the secant of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSECH.php b/samples/ComplexNumbers3/IMSECH.php index b11c3b809..d25f09507 100644 --- a/samples/ComplexNumbers3/IMSECH.php +++ b/samples/ComplexNumbers3/IMSECH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSECH'; $description = 'Returns the hyperbolic secant of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSIN.php b/samples/ComplexNumbers3/IMSIN.php index 57853ba42..a883c6b11 100644 --- a/samples/ComplexNumbers3/IMSIN.php +++ b/samples/ComplexNumbers3/IMSIN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSIN'; $description = 'Returns the sine of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSINH.php b/samples/ComplexNumbers3/IMSINH.php index 2e82f9cc9..560c20d88 100644 --- a/samples/ComplexNumbers3/IMSINH.php +++ b/samples/ComplexNumbers3/IMSINH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSINH'; $description = 'Returns the hyperbolic sine of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSQRT.php b/samples/ComplexNumbers3/IMSQRT.php index 7e1395097..dff585711 100644 --- a/samples/ComplexNumbers3/IMSQRT.php +++ b/samples/ComplexNumbers3/IMSQRT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSQRT'; $description = 'Returns the square root of a complex number in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSUB.php b/samples/ComplexNumbers3/IMSUB.php index 47c00d791..c6576488a 100644 --- a/samples/ComplexNumbers3/IMSUB.php +++ b/samples/ComplexNumbers3/IMSUB.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSUB'; $description = 'Returns the difference of two complex numbers in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMSUM.php b/samples/ComplexNumbers3/IMSUM.php index c9d5b91da..be818680f 100644 --- a/samples/ComplexNumbers3/IMSUM.php +++ b/samples/ComplexNumbers3/IMSUM.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMSUM'; $description = 'Returns the sum of two or more complex numbers in x + yi or x + yj text format'; diff --git a/samples/ComplexNumbers3/IMTAN.php b/samples/ComplexNumbers3/IMTAN.php index 49d676732..e9541d0cc 100644 --- a/samples/ComplexNumbers3/IMTAN.php +++ b/samples/ComplexNumbers3/IMTAN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'IMTAN'; $description = 'Returns the tangent of a complex number in x + yi or x + yj text format'; diff --git a/samples/ConditionalFormatting/01_Basic_Comparisons.php b/samples/ConditionalFormatting/01_Basic_Comparisons.php index 781a5bd21..5d612ecf6 100644 --- a/samples/ConditionalFormatting/01_Basic_Comparisons.php +++ b/samples/ConditionalFormatting/01_Basic_Comparisons.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/02_Text_Comparisons.php b/samples/ConditionalFormatting/02_Text_Comparisons.php index dfccf8571..2df349e8e 100644 --- a/samples/ConditionalFormatting/02_Text_Comparisons.php +++ b/samples/ConditionalFormatting/02_Text_Comparisons.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/03_Blank_Comparisons.php b/samples/ConditionalFormatting/03_Blank_Comparisons.php index 17dca3545..a64b1cbcd 100644 --- a/samples/ConditionalFormatting/03_Blank_Comparisons.php +++ b/samples/ConditionalFormatting/03_Blank_Comparisons.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/04_Error_Comparisons.php b/samples/ConditionalFormatting/04_Error_Comparisons.php index cb11c81d8..4b8770280 100644 --- a/samples/ConditionalFormatting/04_Error_Comparisons.php +++ b/samples/ConditionalFormatting/04_Error_Comparisons.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/05_Date_Comparisons.php b/samples/ConditionalFormatting/05_Date_Comparisons.php index 34d0615e0..0d2488df2 100644 --- a/samples/ConditionalFormatting/05_Date_Comparisons.php +++ b/samples/ConditionalFormatting/05_Date_Comparisons.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php index 98b8dcfbb..cf79c888f 100644 --- a/samples/ConditionalFormatting/06_Duplicate_Comparisons.php +++ b/samples/ConditionalFormatting/06_Duplicate_Comparisons.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/07_Expression_Comparisons.php b/samples/ConditionalFormatting/07_Expression_Comparisons.php index aa064201d..109082ab2 100644 --- a/samples/ConditionalFormatting/07_Expression_Comparisons.php +++ b/samples/ConditionalFormatting/07_Expression_Comparisons.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\Style; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/ConditionalFormatting/cond08_colorscale.php b/samples/ConditionalFormatting/cond08_colorscale.php index d63145970..839479bea 100644 --- a/samples/ConditionalFormatting/cond08_colorscale.php +++ b/samples/ConditionalFormatting/cond08_colorscale.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalColorScale; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormatValueObject; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Database/DAVERAGE.php b/samples/Database/DAVERAGE.php index a06cd5036..0416dd3a2 100644 --- a/samples/Database/DAVERAGE.php +++ b/samples/Database/DAVERAGE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DAVERAGE'; $description = 'Returns the average of selected database entries that match criteria'; diff --git a/samples/Database/DCOUNT.php b/samples/Database/DCOUNT.php index cef489e97..476e365de 100644 --- a/samples/Database/DCOUNT.php +++ b/samples/Database/DCOUNT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DCOUNT'; $description = 'Counts the cells that contain numbers in a set of database records that match criteria'; diff --git a/samples/Database/DCOUNTA.php b/samples/Database/DCOUNTA.php index 734bfa8c4..ecee4206c 100644 --- a/samples/Database/DCOUNTA.php +++ b/samples/Database/DCOUNTA.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DCOUNTA'; $description = 'Counts the cells in a set of database records that match criteria'; diff --git a/samples/Database/DGET.php b/samples/Database/DGET.php index 62503dbd7..cfe650961 100644 --- a/samples/Database/DGET.php +++ b/samples/Database/DGET.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DGET'; $description = 'Extracts a single value from a column of a list or database that matches criteria that you specify'; diff --git a/samples/Database/DMAX.php b/samples/Database/DMAX.php index 79ae6723d..fbb6b906a 100644 --- a/samples/Database/DMAX.php +++ b/samples/Database/DMAX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DMAX'; $description = 'Returns the maximum value from selected database entries'; diff --git a/samples/Database/DMIN.php b/samples/Database/DMIN.php index 4c8386935..565c9501a 100644 --- a/samples/Database/DMIN.php +++ b/samples/Database/DMIN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DMIN'; $description = 'Returns the minimum value from selected database entries'; diff --git a/samples/Database/DPRODUCT.php b/samples/Database/DPRODUCT.php index 2299e7a23..d9cf46930 100644 --- a/samples/Database/DPRODUCT.php +++ b/samples/Database/DPRODUCT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DPRODUCT'; $description = 'Multiplies the values in a column of a list or database that match conditions that you specify'; diff --git a/samples/Database/DSTDEV.php b/samples/Database/DSTDEV.php index a29128f16..b8e23b6e2 100644 --- a/samples/Database/DSTDEV.php +++ b/samples/Database/DSTDEV.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DSTDEV'; $description = 'Estimates the standard deviation based on a sample of selected database entries'; diff --git a/samples/Database/DSTDEVP.php b/samples/Database/DSTDEVP.php index 65b81d638..0e20ce800 100644 --- a/samples/Database/DSTDEVP.php +++ b/samples/Database/DSTDEVP.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DSTDEVP'; $description = 'Calculates the standard deviation based on the entire population of selected database entries'; diff --git a/samples/Database/DSUM.php b/samples/Database/DSUM.php index 21151a0af..2a88008a5 100644 --- a/samples/Database/DSUM.php +++ b/samples/Database/DSUM.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DSUM'; $description = 'Returns the sum of selected database entries'; diff --git a/samples/Database/DVAR.php b/samples/Database/DVAR.php index 58c356316..69809cc17 100644 --- a/samples/Database/DVAR.php +++ b/samples/Database/DVAR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DVAR'; $description = 'Estimates variance based on a sample from selected database entries'; diff --git a/samples/Database/DVARP.php b/samples/Database/DVARP.php index c3107c2cc..d362a8200 100644 --- a/samples/Database/DVARP.php +++ b/samples/Database/DVARP.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Database'; $functionName = 'DVARP'; $description = 'Calculates variance based on the entire population of selected database entries'; diff --git a/samples/DateTime/DATE.php b/samples/DateTime/DATE.php index bd1c5ff6d..d4c1d0cba 100644 --- a/samples/DateTime/DATE.php +++ b/samples/DateTime/DATE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DATE'; $description = 'Returns the Excel serial number of a particular date'; diff --git a/samples/DateTime/DATEDIF.php b/samples/DateTime/DATEDIF.php index f1cc13966..ca9f249de 100644 --- a/samples/DateTime/DATEDIF.php +++ b/samples/DateTime/DATEDIF.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DATEDIF'; $description = 'Calculates the number of days, months, or years between two dates'; diff --git a/samples/DateTime/DATEVALUE.php b/samples/DateTime/DATEVALUE.php index 64346ce01..f8a52fe3a 100644 --- a/samples/DateTime/DATEVALUE.php +++ b/samples/DateTime/DATEVALUE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DATEVALUE'; $description = 'Converts a date in the form of text to an Excel serial number'; diff --git a/samples/DateTime/DAY.php b/samples/DateTime/DAY.php index cb8e1e5f4..4e14e32a2 100644 --- a/samples/DateTime/DAY.php +++ b/samples/DateTime/DAY.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DAY'; $description = 'Returns the day of a date, an integer ranging from 1 to 31'; diff --git a/samples/DateTime/DAYS.php b/samples/DateTime/DAYS.php index 32504ec2d..106ec450f 100644 --- a/samples/DateTime/DAYS.php +++ b/samples/DateTime/DAYS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DAYS'; $description = 'Returns the number of days between two dates'; diff --git a/samples/DateTime/DAYS360.php b/samples/DateTime/DAYS360.php index 1d245b1cc..557e82462 100644 --- a/samples/DateTime/DAYS360.php +++ b/samples/DateTime/DAYS360.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DAYS360'; $description = 'Returns the number of days between two dates based on a 360-day year'; diff --git a/samples/DateTime/EDATE.php b/samples/DateTime/EDATE.php index b9453e741..f224c391c 100644 --- a/samples/DateTime/EDATE.php +++ b/samples/DateTime/EDATE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'EDATE'; $description = 'Returns the serial number that represents the date that is the indicated number of months before or after a specified date'; diff --git a/samples/DateTime/EOMONTH.php b/samples/DateTime/EOMONTH.php index 7c5833c3a..e04f6859e 100644 --- a/samples/DateTime/EOMONTH.php +++ b/samples/DateTime/EOMONTH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'EOMONTH'; $description = 'Returns the serial number for the last day of the month that is the indicated number of months before or after start_date'; diff --git a/samples/DateTime/HOUR.php b/samples/DateTime/HOUR.php index 6dc57a107..2282b6211 100644 --- a/samples/DateTime/HOUR.php +++ b/samples/DateTime/HOUR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'HOUR'; $description = 'Returns the hour of a time value. The hour is given as an integer, ranging from 0 (12:00 AM) to 23 (11:00 PM)'; diff --git a/samples/DateTime/ISOWEEKNUM.php b/samples/DateTime/ISOWEEKNUM.php index 9b3f5b683..e27e70a51 100644 --- a/samples/DateTime/ISOWEEKNUM.php +++ b/samples/DateTime/ISOWEEKNUM.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'ISOWEEKNUM'; $description = 'Returns number of the ISO week number of the year for a given date. (ISO-8601)'; diff --git a/samples/DateTime/MINUTE.php b/samples/DateTime/MINUTE.php index 74d38cca4..9b47d47e8 100644 --- a/samples/DateTime/MINUTE.php +++ b/samples/DateTime/MINUTE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'MINUTE'; $description = 'Returns the minute of a time value. The minute is given as an integer, ranging from 0 to 59'; diff --git a/samples/DateTime/MONTH.php b/samples/DateTime/MONTH.php index 82d17eee8..f3c814dc7 100644 --- a/samples/DateTime/MONTH.php +++ b/samples/DateTime/MONTH.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'MONTH'; $description = 'Returns the month of a date, an integer ranging from 1 to 12'; diff --git a/samples/DateTime2/NETWORKDAYS.php b/samples/DateTime2/NETWORKDAYS.php index f15dc8d92..063b0b2d3 100644 --- a/samples/DateTime2/NETWORKDAYS.php +++ b/samples/DateTime2/NETWORKDAYS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'NETWORKDAYS'; $description = 'Returns the number of whole working days between start_date and end_date. Working days exclude weekends and any dates identified in holidays'; diff --git a/samples/DateTime2/NOW.php b/samples/DateTime2/NOW.php index ca819168d..1dcf50d3a 100644 --- a/samples/DateTime2/NOW.php +++ b/samples/DateTime2/NOW.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'NOW'; $description = 'Returns the serial number of the current date and time'; diff --git a/samples/DateTime2/SECOND.php b/samples/DateTime2/SECOND.php index 3d7c147f3..b49186f88 100644 --- a/samples/DateTime2/SECOND.php +++ b/samples/DateTime2/SECOND.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'SECOND'; $description = 'Returns the second of a time value. The second is given as an integer, ranging from 0 to 59'; diff --git a/samples/DateTime2/TIME.php b/samples/DateTime2/TIME.php index cd2e08630..9cbfc4763 100644 --- a/samples/DateTime2/TIME.php +++ b/samples/DateTime2/TIME.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'TIME'; $description = 'Returns the Excel serial number of a particular time'; diff --git a/samples/DateTime2/TIMEVALUE.php b/samples/DateTime2/TIMEVALUE.php index cf8c71a58..819e39ef7 100644 --- a/samples/DateTime2/TIMEVALUE.php +++ b/samples/DateTime2/TIMEVALUE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DATEVALUE'; $description = 'Converts a time in the form of text to an Excel serial number'; diff --git a/samples/DateTime2/TODAY.php b/samples/DateTime2/TODAY.php index 4c11f4dcf..8fa61d1eb 100644 --- a/samples/DateTime2/TODAY.php +++ b/samples/DateTime2/TODAY.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'TODAY'; $description = 'Returns the serial number of the current date'; diff --git a/samples/DateTime2/WEEKDAY.php b/samples/DateTime2/WEEKDAY.php index c6ba0beae..f28c89efd 100644 --- a/samples/DateTime2/WEEKDAY.php +++ b/samples/DateTime2/WEEKDAY.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'WEEKDAY'; $description = 'Returns the day of the week corresponding to a date'; diff --git a/samples/DateTime2/WEEKNUM.php b/samples/DateTime2/WEEKNUM.php index 9b2850aa4..33cfed6dc 100644 --- a/samples/DateTime2/WEEKNUM.php +++ b/samples/DateTime2/WEEKNUM.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'WEEKNUM'; $description = 'Returns the week number of a specific date'; diff --git a/samples/DateTime2/WORKDAY.php b/samples/DateTime2/WORKDAY.php index a7e79efe2..635808725 100644 --- a/samples/DateTime2/WORKDAY.php +++ b/samples/DateTime2/WORKDAY.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'WORKDAY'; $description = 'Returns a number that represents a date that is the indicated number of working days before or after a starting date. Working days exclude weekends and any dates identified as holidays'; diff --git a/samples/DateTime2/YEAR.php b/samples/DateTime2/YEAR.php index ecfd25b18..0924595a6 100644 --- a/samples/DateTime2/YEAR.php +++ b/samples/DateTime2/YEAR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'YEAR'; $description = 'Returns the year of a date, an integer ranging from 1900 to 9999'; diff --git a/samples/DateTime2/YEARFRAC.php b/samples/DateTime2/YEARFRAC.php index 4c7b44265..465624cc0 100644 --- a/samples/DateTime2/YEARFRAC.php +++ b/samples/DateTime2/YEARFRAC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Date/Time'; $functionName = 'DAYS360'; $description = 'Returns the number of days between two dates based on a 360-day year'; diff --git a/samples/DefinedNames/AbsoluteNamedRange.php b/samples/DefinedNames/AbsoluteNamedRange.php index 4b27799b3..d9451bafa 100644 --- a/samples/DefinedNames/AbsoluteNamedRange.php +++ b/samples/DefinedNames/AbsoluteNamedRange.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/CrossWorksheetNamedFormula.php b/samples/DefinedNames/CrossWorksheetNamedFormula.php index 6c2faec2b..af7210de1 100644 --- a/samples/DefinedNames/CrossWorksheetNamedFormula.php +++ b/samples/DefinedNames/CrossWorksheetNamedFormula.php @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $data2019 = [ diff --git a/samples/DefinedNames/NamedFormulaeAndRanges.php b/samples/DefinedNames/NamedFormulaeAndRanges.php index 8d9dd75a5..36986ceb9 100644 --- a/samples/DefinedNames/NamedFormulaeAndRanges.php +++ b/samples/DefinedNames/NamedFormulaeAndRanges.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/RelativeNamedRange.php b/samples/DefinedNames/RelativeNamedRange.php index 99de212cf..11c8534ce 100644 --- a/samples/DefinedNames/RelativeNamedRange.php +++ b/samples/DefinedNames/RelativeNamedRange.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/RelativeNamedRange2.php b/samples/DefinedNames/RelativeNamedRange2.php index 0814e0aa6..b8bf237c0 100644 --- a/samples/DefinedNames/RelativeNamedRange2.php +++ b/samples/DefinedNames/RelativeNamedRange2.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/RelativeNamedRangeAsFunction.php b/samples/DefinedNames/RelativeNamedRangeAsFunction.php index ebc85aafe..e4b5f11f4 100644 --- a/samples/DefinedNames/RelativeNamedRangeAsFunction.php +++ b/samples/DefinedNames/RelativeNamedRangeAsFunction.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/ScopedNamedRange.php b/samples/DefinedNames/ScopedNamedRange.php index 9066e7381..0d8f2b936 100644 --- a/samples/DefinedNames/ScopedNamedRange.php +++ b/samples/DefinedNames/ScopedNamedRange.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); $worksheet->setTitle('Base Data'); diff --git a/samples/DefinedNames/ScopedNamedRange2.php b/samples/DefinedNames/ScopedNamedRange2.php index 94845a461..da5331087 100644 --- a/samples/DefinedNames/ScopedNamedRange2.php +++ b/samples/DefinedNames/ScopedNamedRange2.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/SimpleNamedFormula.php b/samples/DefinedNames/SimpleNamedFormula.php index f0018100e..a40717690 100644 --- a/samples/DefinedNames/SimpleNamedFormula.php +++ b/samples/DefinedNames/SimpleNamedFormula.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/DefinedNames/SimpleNamedRange.php b/samples/DefinedNames/SimpleNamedRange.php index 12c086016..b80b213be 100644 --- a/samples/DefinedNames/SimpleNamedRange.php +++ b/samples/DefinedNames/SimpleNamedRange.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require_once __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = new Spreadsheet(); $worksheet = $spreadsheet->setActiveSheetIndex(0); diff --git a/samples/Engineering/BESSELI.php b/samples/Engineering/BESSELI.php index 5e6588d51..eeed87e0a 100644 --- a/samples/Engineering/BESSELI.php +++ b/samples/Engineering/BESSELI.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BESSELI'; $description = 'Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments'; diff --git a/samples/Engineering/BESSELJ.php b/samples/Engineering/BESSELJ.php index 77277dd63..647737f72 100644 --- a/samples/Engineering/BESSELJ.php +++ b/samples/Engineering/BESSELJ.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BESSELJ'; $description = 'Returns the Bessel function'; diff --git a/samples/Engineering/BESSELK.php b/samples/Engineering/BESSELK.php index 10802a5b5..ae16587c4 100644 --- a/samples/Engineering/BESSELK.php +++ b/samples/Engineering/BESSELK.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BESSELK'; $description = 'Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments'; diff --git a/samples/Engineering/BESSELY.php b/samples/Engineering/BESSELY.php index 9d71563b7..5671b1161 100644 --- a/samples/Engineering/BESSELY.php +++ b/samples/Engineering/BESSELY.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BESSELY'; $description = 'Returns the Bessel function, which is also called the Weber function or the Neumann function'; diff --git a/samples/Engineering/CONVERT.php b/samples/Engineering/CONVERT.php index 335799135..a2f8d74f0 100644 --- a/samples/Engineering/CONVERT.php +++ b/samples/Engineering/CONVERT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'CONVERT'; $description = 'Converts a number from one measurement system to another'; diff --git a/samples/Engineering/Convert-Online.php b/samples/Engineering/Convert-Online.php index 3d97d05a1..3f8da04bd 100644 --- a/samples/Engineering/Convert-Online.php +++ b/samples/Engineering/Convert-Online.php @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Settings; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; require __DIR__ . '/../Header.php'; - +/** @var Sample $helper */ $helper = new Sample(); if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Engineering/DELTA.php b/samples/Engineering/DELTA.php index 03ee08749..3ec883114 100644 --- a/samples/Engineering/DELTA.php +++ b/samples/Engineering/DELTA.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'DELTA'; $description = 'Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise. This function is also known as the Kronecker Delta function'; diff --git a/samples/Engineering/ERF.php b/samples/Engineering/ERF.php index b5e66db06..bd6d0b4b6 100644 --- a/samples/Engineering/ERF.php +++ b/samples/Engineering/ERF.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'ERF'; $description = 'Returns the error function integrated between lower_limit and upper_limit'; diff --git a/samples/Engineering/ERFC.php b/samples/Engineering/ERFC.php index 3ef4b36db..addd02bb2 100644 --- a/samples/Engineering/ERFC.php +++ b/samples/Engineering/ERFC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'ERFC'; $description = 'Returns the complementary ERF function integrated between x and infinity'; diff --git a/samples/Engineering/GESTEP.php b/samples/Engineering/GESTEP.php index 024e9d594..e52574d59 100644 --- a/samples/Engineering/GESTEP.php +++ b/samples/Engineering/GESTEP.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'GESTEP'; $description = 'Returns 1 if number ≥ step; returns 0 (zero) otherwise'; diff --git a/samples/Financial1/ACCRINT.php b/samples/Financial1/ACCRINT.php index 8b0bb189f..a57e95c8e 100644 --- a/samples/Financial1/ACCRINT.php +++ b/samples/Financial1/ACCRINT.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the accrued interest for a security that pays periodic interest.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/ACCRINTM.php b/samples/Financial1/ACCRINTM.php index 8e7951c46..62ccaf05e 100644 --- a/samples/Financial1/ACCRINTM.php +++ b/samples/Financial1/ACCRINTM.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the accrued interest for a security that pays interest at maturity.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/AMORDEGRC.php b/samples/Financial1/AMORDEGRC.php index 1b650b991..3c1d05749 100644 --- a/samples/Financial1/AMORDEGRC.php +++ b/samples/Financial1/AMORDEGRC.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the prorated linear depreciation of an asset for a specified accounting period.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/AMORLINC.php b/samples/Financial1/AMORLINC.php index 17196fd05..61f29e7aa 100644 --- a/samples/Financial1/AMORLINC.php +++ b/samples/Financial1/AMORLINC.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the prorated linear depreciation of an asset for a specified accounting period.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/COUPDAYBS.php b/samples/Financial1/COUPDAYBS.php index ddf420e94..4405a1fc0 100644 --- a/samples/Financial1/COUPDAYBS.php +++ b/samples/Financial1/COUPDAYBS.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of days from the beginning of a coupon\'s period to the settlement date.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/COUPDAYS.php b/samples/Financial1/COUPDAYS.php index c53ec66cc..4f94ccf9a 100644 --- a/samples/Financial1/COUPDAYS.php +++ b/samples/Financial1/COUPDAYS.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of days in the coupon period that contains the settlement date.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/COUPDAYSNC.php b/samples/Financial1/COUPDAYSNC.php index 741a410be..851a01035 100644 --- a/samples/Financial1/COUPDAYSNC.php +++ b/samples/Financial1/COUPDAYSNC.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of days from the settlement date to the next coupon date.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/COUPNCD.php b/samples/Financial1/COUPNCD.php index c5d61360e..1311716d8 100644 --- a/samples/Financial1/COUPNCD.php +++ b/samples/Financial1/COUPNCD.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the next coupon date, after the settlement date.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/COUPNUM.php b/samples/Financial1/COUPNUM.php index 495298f50..255aea295 100644 --- a/samples/Financial1/COUPNUM.php +++ b/samples/Financial1/COUPNUM.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of coupons payable, between a security\'s settlement date and maturity date,'); $helper->log('rounded up to the nearest whole coupon.'); diff --git a/samples/Financial1/COUPPCD.php b/samples/Financial1/COUPPCD.php index ef04955e0..0465821ce 100644 --- a/samples/Financial1/COUPPCD.php +++ b/samples/Financial1/COUPPCD.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the previous coupon date, before the settlement date for a security.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/CUMIPMT.php b/samples/Financial1/CUMIPMT.php index c3b5b7e83..45331cf9b 100644 --- a/samples/Financial1/CUMIPMT.php +++ b/samples/Financial1/CUMIPMT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the cumulative interest paid on a loan or investment, between two specified periods.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial1/CUMPRINC.php b/samples/Financial1/CUMPRINC.php index 161314a93..fee1eaa24 100644 --- a/samples/Financial1/CUMPRINC.php +++ b/samples/Financial1/CUMPRINC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the cumulative payment on the principal of a loan or investment, between two specified periods.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial2/DB.php b/samples/Financial2/DB.php index a8dd14944..b615585c2 100644 --- a/samples/Financial2/DB.php +++ b/samples/Financial2/DB.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the depreciation of an asset, using the Fixed Declining Balance Method,'); $helper->log('for each period of the asset\'s lifetime.'); diff --git a/samples/Financial2/DDB.php b/samples/Financial2/DDB.php index d260e67a7..a00268a95 100644 --- a/samples/Financial2/DDB.php +++ b/samples/Financial2/DDB.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the depreciation of an asset, using the Double Declining Balance Method,'); $helper->log('for each period of the asset\'s lifetime.'); diff --git a/samples/Financial2/DISC.php b/samples/Financial2/DISC.php index ec2ce4417..2da3e0324 100644 --- a/samples/Financial2/DISC.php +++ b/samples/Financial2/DISC.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Discount Rate for a security.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial2/DOLLARDE.php b/samples/Financial2/DOLLARDE.php index d47f049da..3282a8249 100644 --- a/samples/Financial2/DOLLARDE.php +++ b/samples/Financial2/DOLLARDE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the dollar value in fractional notation, into a dollar value expressed as a decimal.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial2/DOLLARFR.php b/samples/Financial2/DOLLARFR.php index f9121a333..9a4185e51 100644 --- a/samples/Financial2/DOLLARFR.php +++ b/samples/Financial2/DOLLARFR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the dollar value expressed as a decimal number, into a dollar price, expressed as a fraction.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial2/EFFECT.php b/samples/Financial2/EFFECT.php index fd526bc7d..263a964b8 100644 --- a/samples/Financial2/EFFECT.php +++ b/samples/Financial2/EFFECT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the effective annual interest rate for a given nominal interest rate and number of'); $helper->log('compounding periods per year.'); diff --git a/samples/Financial2/FV.php b/samples/Financial2/FV.php index ede445c08..b89dc8b0a 100644 --- a/samples/Financial2/FV.php +++ b/samples/Financial2/FV.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Future Value of an investment with periodic constant payments and a constant interest rate.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial2/FVSCHEDULE.php b/samples/Financial2/FVSCHEDULE.php index e4516b469..eab984dba 100644 --- a/samples/Financial2/FVSCHEDULE.php +++ b/samples/Financial2/FVSCHEDULE.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Future Value of an initial principal, after applying a series of compound interest rates.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial3/INTRATE.php b/samples/Financial3/INTRATE.php index 1d40ae98a..3c836e4f2 100644 --- a/samples/Financial3/INTRATE.php +++ b/samples/Financial3/INTRATE.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Helpers as DateHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the interest rate for a fully invested security.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial3/IPMT.php b/samples/Financial3/IPMT.php index 80c5cd175..e0c88a028 100644 --- a/samples/Financial3/IPMT.php +++ b/samples/Financial3/IPMT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the interest payment, during a specific period of a loan or investment that is paid in,'); $helper->log('constant periodic payments, with a constant interest rate.'); diff --git a/samples/Financial3/IRR.php b/samples/Financial3/IRR.php index 7dfbcea48..461f583d3 100644 --- a/samples/Financial3/IRR.php +++ b/samples/Financial3/IRR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Internal Rate of Return for a supplied series of periodic cash flows.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial3/ISPMT.php b/samples/Financial3/ISPMT.php index eaf76d68a..86a95ca84 100644 --- a/samples/Financial3/ISPMT.php +++ b/samples/Financial3/ISPMT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the interest paid during a specific period of a loan or investment.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial3/MIRR.php b/samples/Financial3/MIRR.php index 47273244f..57fefe042 100644 --- a/samples/Financial3/MIRR.php +++ b/samples/Financial3/MIRR.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Modified Internal Rate of Return for a supplied series of periodic cash flows.'); // Create new PhpSpreadsheet object diff --git a/samples/Financial3/NOMINAL.php b/samples/Financial3/NOMINAL.php index 9c24287d4..d1904b3fd 100644 --- a/samples/Financial3/NOMINAL.php +++ b/samples/Financial3/NOMINAL.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the nominal interest rate for a given effective interest rate and number of'); $helper->log('compounding periods per year.'); diff --git a/samples/Financial3/NPER.php b/samples/Financial3/NPER.php index 7d5833549..fcd6ce095 100644 --- a/samples/Financial3/NPER.php +++ b/samples/Financial3/NPER.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of periods required to pay off a loan, for a constant periodic payment'); $helper->log('and a constant interest rate.'); diff --git a/samples/Financial3/NPV.php b/samples/Financial3/NPV.php index a7016a712..39759d09e 100644 --- a/samples/Financial3/NPV.php +++ b/samples/Financial3/NPV.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the Net Present Value of an investment, based on a supplied discount rate,'); $helper->log('and a series of future payments and income.'); diff --git a/samples/HexEtcConversions/BIN2DEC.php b/samples/HexEtcConversions/BIN2DEC.php index d5beba3be..e63a3f031 100644 --- a/samples/HexEtcConversions/BIN2DEC.php +++ b/samples/HexEtcConversions/BIN2DEC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BIN2DEC'; $description = 'Converts a binary number to decimal'; diff --git a/samples/HexEtcConversions/BIN2HEX.php b/samples/HexEtcConversions/BIN2HEX.php index 630fe7c48..d8ea9d06c 100644 --- a/samples/HexEtcConversions/BIN2HEX.php +++ b/samples/HexEtcConversions/BIN2HEX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BIN2HEX'; $description = 'Converts a binary number to hexadecimal'; diff --git a/samples/HexEtcConversions/BIN2OCT.php b/samples/HexEtcConversions/BIN2OCT.php index 37ccb752c..049bc4923 100644 --- a/samples/HexEtcConversions/BIN2OCT.php +++ b/samples/HexEtcConversions/BIN2OCT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'BIN2OCT'; $description = 'Converts a binary number to octal'; diff --git a/samples/HexEtcConversions/DEC2BIN.php b/samples/HexEtcConversions/DEC2BIN.php index 1948995da..382094400 100644 --- a/samples/HexEtcConversions/DEC2BIN.php +++ b/samples/HexEtcConversions/DEC2BIN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'DEC2BIN'; $description = 'Converts a decimal number to binary'; diff --git a/samples/HexEtcConversions/DEC2HEX.php b/samples/HexEtcConversions/DEC2HEX.php index 0b3f069dc..7cf5047d3 100644 --- a/samples/HexEtcConversions/DEC2HEX.php +++ b/samples/HexEtcConversions/DEC2HEX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'DEC2HEX'; $description = 'Converts a decimal number to hexadecimal'; diff --git a/samples/HexEtcConversions/DEC2OCT.php b/samples/HexEtcConversions/DEC2OCT.php index bb0f19c17..55ca9827a 100644 --- a/samples/HexEtcConversions/DEC2OCT.php +++ b/samples/HexEtcConversions/DEC2OCT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'DEC2OCT'; $description = 'Converts a decimal number to octal'; diff --git a/samples/HexEtcConversions/HEX2BIN.php b/samples/HexEtcConversions/HEX2BIN.php index 5b9780c80..78fc9c4d3 100644 --- a/samples/HexEtcConversions/HEX2BIN.php +++ b/samples/HexEtcConversions/HEX2BIN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'HEX2BIN'; $description = 'Converts a hexadecimal number to binary'; diff --git a/samples/HexEtcConversions/HEX2DEC.php b/samples/HexEtcConversions/HEX2DEC.php index c0a3e2f61..762b4a9a4 100644 --- a/samples/HexEtcConversions/HEX2DEC.php +++ b/samples/HexEtcConversions/HEX2DEC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'HEX2DEC'; $description = 'Converts a hexadecimal number to decimal'; diff --git a/samples/HexEtcConversions/HEX2OCT.php b/samples/HexEtcConversions/HEX2OCT.php index 344dcab23..24ac8c3e9 100644 --- a/samples/HexEtcConversions/HEX2OCT.php +++ b/samples/HexEtcConversions/HEX2OCT.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'HEX2OCT'; $description = 'Converts a hexadecimal number to octal'; diff --git a/samples/HexEtcConversions/OCT2BIN.php b/samples/HexEtcConversions/OCT2BIN.php index 387a9030c..55d479e19 100644 --- a/samples/HexEtcConversions/OCT2BIN.php +++ b/samples/HexEtcConversions/OCT2BIN.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'OCT2BIN'; $description = 'Converts an octal number to binary'; diff --git a/samples/HexEtcConversions/OCT2DEC.php b/samples/HexEtcConversions/OCT2DEC.php index 604b4517e..a5b5d0f6f 100644 --- a/samples/HexEtcConversions/OCT2DEC.php +++ b/samples/HexEtcConversions/OCT2DEC.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'OCT2DEC'; $description = 'Converts an octal number to decimal'; diff --git a/samples/HexEtcConversions/OCT2HEX.php b/samples/HexEtcConversions/OCT2HEX.php index c637a3844..c7613e82b 100644 --- a/samples/HexEtcConversions/OCT2HEX.php +++ b/samples/HexEtcConversions/OCT2HEX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $category = 'Engineering'; $functionName = 'OCT2HEX'; $description = 'Converts an octal number to hexadecimal'; diff --git a/samples/Html/html_01_Basic_Conditional_Formatting.php b/samples/Html/html_01_Basic_Conditional_Formatting.php index c2a1efd1a..359be239b 100644 --- a/samples/Html/html_01_Basic_Conditional_Formatting.php +++ b/samples/Html/html_01_Basic_Conditional_Formatting.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = 'BasicConditionalFormatting.xlsx'; $inputFilePath = __DIR__ . '/../templates/' . $inputFileName; diff --git a/samples/Html/html_02_More_Conditional_Formatting.php b/samples/Html/html_02_More_Conditional_Formatting.php index b8971f0e3..4a6cea3ad 100644 --- a/samples/Html/html_02_More_Conditional_Formatting.php +++ b/samples/Html/html_02_More_Conditional_Formatting.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = 'ConditionalFormattingConditions.xlsx'; $inputFilePath = __DIR__ . '/../templates/' . $inputFileName; diff --git a/samples/Html/html_03_Color_Scale.php b/samples/Html/html_03_Color_Scale.php index ed2296bfb..c9191865d 100644 --- a/samples/Html/html_03_Color_Scale.php +++ b/samples/Html/html_03_Color_Scale.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = 'ColourScale.xlsx'; $inputFilePath = __DIR__ . '/../templates/' . $inputFileName; diff --git a/samples/Html/html_04_Table_Format_without_Conditional.php b/samples/Html/html_04_Table_Format_without_Conditional.php index 396af5375..53a6731d6 100644 --- a/samples/Html/html_04_Table_Format_without_Conditional.php +++ b/samples/Html/html_04_Table_Format_without_Conditional.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = 'TableFormat.xlsx'; $inputFilePath = __DIR__ . '/../templates/' . $inputFileName; diff --git a/samples/Html/html_05_Table_Format_with_Conditional.php b/samples/Html/html_05_Table_Format_with_Conditional.php index bdfa6ab88..385372639 100644 --- a/samples/Html/html_05_Table_Format_with_Conditional.php +++ b/samples/Html/html_05_Table_Format_with_Conditional.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = 'TableFormat.xlsx'; $inputFilePath = __DIR__ . '/../templates/' . $inputFileName; diff --git a/samples/LookupRef/ADDRESS.php b/samples/LookupRef/ADDRESS.php index 3e80d3e7c..a6b727a2f 100644 --- a/samples/LookupRef/ADDRESS.php +++ b/samples/LookupRef/ADDRESS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns a text reference to a single cell in a worksheet.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/COLUMN.php b/samples/LookupRef/COLUMN.php index f66ecdbb9..8e36d3136 100644 --- a/samples/LookupRef/COLUMN.php +++ b/samples/LookupRef/COLUMN.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the column index of a cell.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/COLUMNS.php b/samples/LookupRef/COLUMNS.php index 4a112c80c..a004c0a1f 100644 --- a/samples/LookupRef/COLUMNS.php +++ b/samples/LookupRef/COLUMNS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the number of columns in an array or reference.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/INDEX.php b/samples/LookupRef/INDEX.php index b1cfa5fce..3ead163e3 100644 --- a/samples/LookupRef/INDEX.php +++ b/samples/LookupRef/INDEX.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the row index of a cell.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/INDIRECT.php b/samples/LookupRef/INDIRECT.php index 1a23a678a..b89e37fd9 100644 --- a/samples/LookupRef/INDIRECT.php +++ b/samples/LookupRef/INDIRECT.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\NamedRange; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the cell specified by a text string.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/OFFSET.php b/samples/LookupRef/OFFSET.php index a70f8d7ea..0a5245455 100644 --- a/samples/LookupRef/OFFSET.php +++ b/samples/LookupRef/OFFSET.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns a cell range that is a specified number of rows and columns from a cell or range of cells.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/ROW.php b/samples/LookupRef/ROW.php index 6fe1cea1a..ad4b2b33e 100644 --- a/samples/LookupRef/ROW.php +++ b/samples/LookupRef/ROW.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the row index of a cell.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/ROWS.php b/samples/LookupRef/ROWS.php index 45d5f463d..8b1ebe40c 100644 --- a/samples/LookupRef/ROWS.php +++ b/samples/LookupRef/ROWS.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Returns the row index of a cell.'); // Create new PhpSpreadsheet object diff --git a/samples/LookupRef/VLOOKUP.php b/samples/LookupRef/VLOOKUP.php index 28a8086ce..6fcda1a27 100644 --- a/samples/LookupRef/VLOOKUP.php +++ b/samples/LookupRef/VLOOKUP.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Searches for a value in the top row of a table or an array of values, and then returns a value in the same column from a row you specify in the table or array.'); diff --git a/samples/Pdf/21_Pdf_Domdf.php b/samples/Pdf/21_Pdf_Domdf.php index 53cae06ed..fbea071bc 100644 --- a/samples/Pdf/21_Pdf_Domdf.php +++ b/samples/Pdf/21_Pdf_Domdf.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Hide grid lines'); diff --git a/samples/Pdf/21_Pdf_TCPDF.php b/samples/Pdf/21_Pdf_TCPDF.php index 8fed65b96..f8dd8b1fb 100644 --- a/samples/Pdf/21_Pdf_TCPDF.php +++ b/samples/Pdf/21_Pdf_TCPDF.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Hide grid lines'); diff --git a/samples/Pdf/21_Pdf_mPDF.php b/samples/Pdf/21_Pdf_mPDF.php index 05e57016c..9c0dff41a 100644 --- a/samples/Pdf/21_Pdf_mPDF.php +++ b/samples/Pdf/21_Pdf_mPDF.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Hide grid lines'); diff --git a/samples/Pdf/21a_Pdf.php b/samples/Pdf/21a_Pdf.php index 377f59287..ba4879069 100644 --- a/samples/Pdf/21a_Pdf.php +++ b/samples/Pdf/21a_Pdf.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Hide grid lines'); diff --git a/samples/Pdf/21b_Pdf.php b/samples/Pdf/21b_Pdf.php index e9d340259..27b698720 100644 --- a/samples/Pdf/21b_Pdf.php +++ b/samples/Pdf/21b_Pdf.php @@ -26,6 +26,7 @@ function replaceBody(string $html): string } require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $helper->log('Hide grid lines'); diff --git a/samples/Pdf/21c_Pdf.php b/samples/Pdf/21c_Pdf.php index 66d964aed..2a274965e 100644 --- a/samples/Pdf/21c_Pdf.php +++ b/samples/Pdf/21c_Pdf.php @@ -47,6 +47,7 @@ function addHeadersFootersMpdf2000(string $html): string $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $counter = 0; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Populate spreadsheet'); for ($row = 1; $row < 1001; ++$row) { $sheet->getCell("A$row")->setValue(++$counter); diff --git a/samples/Pdf/21d_FitToHeightPdf.php b/samples/Pdf/21d_FitToHeightPdf.php index 5eaf62a36..1ea859b73 100644 --- a/samples/Pdf/21d_FitToHeightPdf.php +++ b/samples/Pdf/21d_FitToHeightPdf.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Issue 3266 - spreadsheet specified fitToHeight. $helper->log('Read spreadsheet'); diff --git a/samples/Pdf/21e_UnusualFont_mpdf.php b/samples/Pdf/21e_UnusualFont_mpdf.php index 36619d704..81d31e13c 100644 --- a/samples/Pdf/21e_UnusualFont_mpdf.php +++ b/samples/Pdf/21e_UnusualFont_mpdf.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf2; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ require_once __DIR__ . '/Mpdf2.php'; $spreadsheet = new Spreadsheet(); diff --git a/samples/Pdf/21f_Drawing_mpdf.php b/samples/Pdf/21f_Drawing_mpdf.php index 8787cbfbd..357b4690a 100644 --- a/samples/Pdf/21f_Drawing_mpdf.php +++ b/samples/Pdf/21f_Drawing_mpdf.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ require_once __DIR__ . '/Mpdf2.php'; $spreadsheet = new Spreadsheet(); diff --git a/samples/Reader/01_Simple_file_reader_using_IOFactory.php b/samples/Reader/01_Simple_file_reader_using_IOFactory.php index ec07ad74b..c66c34a1b 100644 --- a/samples/Reader/01_Simple_file_reader_using_IOFactory.php +++ b/samples/Reader/01_Simple_file_reader_using_IOFactory.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example1.xls'; $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format'); $spreadsheet = IOFactory::load($inputFileName); 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 2605f1ff3..c165e8e07 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 @@ -3,6 +3,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xls; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example1.xls'; $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using ' . Xls::class); 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 977cb8b12..c8d9bd2eb 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 @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; 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 b6ef66ad1..8dc4cb703 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 @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example1.xls'; $inputFileType = IOFactory::identify($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 668f86f3d..467a4e01f 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 @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; 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 5507c52b2..3d737a8b4 100644 --- a/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php +++ b/samples/Reader/06_Simple_file_reader_loading_all_worksheets.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; 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 142a17f81..2e8c7e1e7 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 @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; $sheetname = 'Data Sheet #2'; 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 64211dc61..5d9e9dd0a 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 @@ -14,6 +14,7 @@ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; $sheetnames = getDesiredSheetNames(); +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory with a defined reader type of ' . $inputFileType); $reader = IOFactory::createReader($inputFileType); $helper->log('Loading Sheets "' . implode('" and "', $sheetnames) . '" only'); 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 08cd648e4..1ab46213b 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 @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; $sheetname = 'Data Sheet #3'; 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 a88cbc66a..49c076861 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 @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; $sheetname = 'Data Sheet #3'; 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 ae8c75b54..517c327bf 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 @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example2.xls'; 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 d421b6ad4..614a012fa 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 @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example2.xls'; diff --git a/samples/Reader2/13_Simple_file_reader_for_multiple_CSV_files.php b/samples/Reader2/13_Simple_file_reader_for_multiple_CSV_files.php index 7078a77e8..cf38746a1 100644 --- a/samples/Reader2/13_Simple_file_reader_for_multiple_CSV_files.php +++ b/samples/Reader2/13_Simple_file_reader_for_multiple_CSV_files.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Csv; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileNames = [__DIR__ . '/sampleData/example1.csv', __DIR__ . '/sampleData/example2.csv']; $reader = new Csv(); diff --git a/samples/Reader2/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php b/samples/Reader2/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php index 4e3fe86a7..0bc3b8665 100644 --- a/samples/Reader2/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php +++ b/samples/Reader2/14_Reading_a_large_CSV_file_in_chunks_to_split_across_multiple_worksheets.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; - +/** @var \PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example2.csv'; /** Define a Read Filter class implementing IReadFilter */ diff --git a/samples/Reader2/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php b/samples/Reader2/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php index 891d95e53..3fb03be2e 100644 --- a/samples/Reader2/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php +++ b/samples/Reader2/15_Simple_file_reader_for_tab_separated_value_file_using_the_Advanced_Value_Binder.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Reader\Csv; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ Cell::setValueBinder(new AdvancedValueBinder()); $inputFileName = __DIR__ . '/sampleData/example1.tsv'; diff --git a/samples/Reader2/16_Handling_loader_exceptions_using_TryCatch.php b/samples/Reader2/16_Handling_loader_exceptions_using_TryCatch.php index 5b1029670..a5a80b8b2 100644 --- a/samples/Reader2/16_Handling_loader_exceptions_using_TryCatch.php +++ b/samples/Reader2/16_Handling_loader_exceptions_using_TryCatch.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/non-existing-file.xls'; $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using IOFactory to identify the format'); diff --git a/samples/Reader2/17_Simple_file_reader_loading_several_named_worksheets.php b/samples/Reader2/17_Simple_file_reader_loading_several_named_worksheets.php index 7701c78e7..1f90d7d03 100644 --- a/samples/Reader2/17_Simple_file_reader_loading_several_named_worksheets.php +++ b/samples/Reader2/17_Simple_file_reader_loading_several_named_worksheets.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xls; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example1.xls'; $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' using Xls reader'); diff --git a/samples/Reader2/18_Reading_list_of_worksheets_without_loading_entire_file.php b/samples/Reader2/18_Reading_list_of_worksheets_without_loading_entire_file.php index fb5b35224..9b05f9d77 100644 --- a/samples/Reader2/18_Reading_list_of_worksheets_without_loading_entire_file.php +++ b/samples/Reader2/18_Reading_list_of_worksheets_without_loading_entire_file.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xls; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/sampleData/example1.xls'; $helper->log('Loading file ' . pathinfo($inputFileName, PATHINFO_BASENAME) . ' information using Xls reader'); diff --git a/samples/Reader2/19_Reading_worksheet_information_without_loading_entire_file.php b/samples/Reader2/19_Reading_worksheet_information_without_loading_entire_file.php index 893daf49d..987cf53ab 100644 --- a/samples/Reader2/19_Reading_worksheet_information_without_loading_entire_file.php +++ b/samples/Reader2/19_Reading_worksheet_information_without_loading_entire_file.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xls; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; diff --git a/samples/Reader2/20_Reader_worksheet_hyperlink_image.php b/samples/Reader2/20_Reader_worksheet_hyperlink_image.php index 0cd09ab8d..db0c885ea 100644 --- a/samples/Reader2/20_Reader_worksheet_hyperlink_image.php +++ b/samples/Reader2/20_Reader_worksheet_hyperlink_image.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xlsx'; $helper->log('Start'); diff --git a/samples/Reader2/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php b/samples/Reader2/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php index 99f5441c3..56ba5b029 100644 --- a/samples/Reader2/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php +++ b/samples/Reader2/21_Reader_CSV_Long_Integers_with_String_Value_Binder.php @@ -5,7 +5,7 @@ use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder; use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ Cell::setValueBinder(new StringValueBinder()); $inputFileType = 'Csv'; diff --git a/samples/Reader2/22_Reader_formscomments.php b/samples/Reader2/22_Reader_formscomments.php index 3f1dd3c5c..83632268f 100644 --- a/samples/Reader2/22_Reader_formscomments.php +++ b/samples/Reader2/22_Reader_formscomments.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Start'); $inputFileType = 'Xlsx'; diff --git a/samples/Reader2/22_Reader_issue1767.php b/samples/Reader2/22_Reader_issue1767.php index 10caef121..a2c119709 100644 --- a/samples/Reader2/22_Reader_issue1767.php +++ b/samples/Reader2/22_Reader_issue1767.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Start'); $inputFileType = 'Xlsx'; diff --git a/samples/Reader2/23_iterateRowsYield.php b/samples/Reader2/23_iterateRowsYield.php index f1a7ef88f..7804c839c 100644 --- a/samples/Reader2/23_iterateRowsYield.php +++ b/samples/Reader2/23_iterateRowsYield.php @@ -5,7 +5,7 @@ */ require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileName = __DIR__ . '/../Reader/sampleData/example1.xls'; $spreadsheet = PhpOffice\PhpSpreadsheet\IOFactory::load( diff --git a/samples/Reading_workbook_data/Custom_properties.php b/samples/Reading_workbook_data/Custom_properties.php index 1a84e73f2..c04436384 100644 --- a/samples/Reading_workbook_data/Custom_properties.php +++ b/samples/Reading_workbook_data/Custom_properties.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xlsx'; $inputFileName = __DIR__ . '/sampleData/example1.xlsx'; diff --git a/samples/Reading_workbook_data/Custom_property_names.php b/samples/Reading_workbook_data/Custom_property_names.php index 0f287f04f..8c3b94cec 100644 --- a/samples/Reading_workbook_data/Custom_property_names.php +++ b/samples/Reading_workbook_data/Custom_property_names.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xlsx'; $inputFileName = __DIR__ . '/sampleData/example1.xlsx'; diff --git a/samples/Reading_workbook_data/Properties.php b/samples/Reading_workbook_data/Properties.php index 441b12c9a..6b53461bc 100644 --- a/samples/Reading_workbook_data/Properties.php +++ b/samples/Reading_workbook_data/Properties.php @@ -4,7 +4,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Shared\Date; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example1.xls'; diff --git a/samples/Reading_workbook_data/Worksheet_count_and_names.php b/samples/Reading_workbook_data/Worksheet_count_and_names.php index 630312b73..69a133c00 100644 --- a/samples/Reading_workbook_data/Worksheet_count_and_names.php +++ b/samples/Reading_workbook_data/Worksheet_count_and_names.php @@ -3,7 +3,7 @@ use PhpOffice\PhpSpreadsheet\IOFactory; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $inputFileType = 'Xls'; $inputFileName = __DIR__ . '/sampleData/example2.xls'; diff --git a/samples/Table/01_Table.php b/samples/Table/01_Table.php index 35a0bb9ac..0c250675c 100644 --- a/samples/Table/01_Table.php +++ b/samples/Table/01_Table.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Table; use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Table/02_Table_Total.php b/samples/Table/02_Table_Total.php index 4a514463c..7c5964548 100644 --- a/samples/Table/02_Table_Total.php +++ b/samples/Table/02_Table_Total.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Table; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Table/03_Column_Formula.php b/samples/Table/03_Column_Formula.php index 27fc9fd23..bb901dff2 100644 --- a/samples/Table/03_Column_Formula.php +++ b/samples/Table/03_Column_Formula.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Table; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Table/04_Column_Formula_with_Totals.php b/samples/Table/04_Column_Formula_with_Totals.php index b4c0acf51..c3e7a57f6 100644 --- a/samples/Table/04_Column_Formula_with_Totals.php +++ b/samples/Table/04_Column_Formula_with_Totals.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Table; require __DIR__ . '/../Header.php'; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ // Create new Spreadsheet object $helper->log('Create new Spreadsheet object'); diff --git a/samples/Wizards/NumberFormat/Accounting.php b/samples/Wizards/NumberFormat/Accounting.php index f4fe8ef93..e5b096e21 100644 --- a/samples/Wizards/NumberFormat/Accounting.php +++ b/samples/Wizards/NumberFormat/Accounting.php @@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Wizards/NumberFormat/Currency.php b/samples/Wizards/NumberFormat/Currency.php index bb1bdd0c4..1e1e53cc4 100644 --- a/samples/Wizards/NumberFormat/Currency.php +++ b/samples/Wizards/NumberFormat/Currency.php @@ -8,7 +8,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard\CurrencyNegative; use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter; require __DIR__ . '/../Header.php'; - +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Wizards/NumberFormat/Number.php b/samples/Wizards/NumberFormat/Number.php index 4af7fafe1..4ad9f8e32 100644 --- a/samples/Wizards/NumberFormat/Number.php +++ b/samples/Wizards/NumberFormat/Number.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard; require __DIR__ . '/../Header.php'; - +/** @var Sample $helper */ $helper = new Sample(); if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Wizards/NumberFormat/Percentage.php b/samples/Wizards/NumberFormat/Percentage.php index 26f7f082e..b3c7279b2 100644 --- a/samples/Wizards/NumberFormat/Percentage.php +++ b/samples/Wizards/NumberFormat/Percentage.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard; require __DIR__ . '/../Header.php'; - +/** @var Sample $helper */ $helper = new Sample(); if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/Wizards/NumberFormat/Scientific.php b/samples/Wizards/NumberFormat/Scientific.php index e16d6a4f7..4a895934b 100644 --- a/samples/Wizards/NumberFormat/Scientific.php +++ b/samples/Wizards/NumberFormat/Scientific.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard; require __DIR__ . '/../Header.php'; - +/** @var Sample $helper */ $helper = new Sample(); if ($helper->isCli()) { $helper->log('This example should only be run from a Web Browser' . PHP_EOL); diff --git a/samples/index.php b/samples/index.php index 956245991..5fa829305 100644 --- a/samples/index.php +++ b/samples/index.php @@ -1,6 +1,6 @@ version_compare(PHP_VERSION, '8.1', '>='), @@ -12,6 +12,7 @@ $requirements = [ 'PHP extension dom (optional)' => extension_loaded('dom'), ]; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ if (!$helper->isCli()) { ?>
diff --git a/samples/templates/chartSpreadsheet.php b/samples/templates/chartSpreadsheet.php index b43289d15..862c8f840 100644 --- a/samples/templates/chartSpreadsheet.php +++ b/samples/templates/chartSpreadsheet.php @@ -95,6 +95,7 @@ $chart->setBottomRightPosition('H20'); // Add the chart to the worksheet $worksheet->addChart($chart); +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->renderChart($chart, __FILE__); return $spreadsheet; diff --git a/samples/templates/largeSpreadsheet.php b/samples/templates/largeSpreadsheet.php index 6502211ea..7315a690e 100644 --- a/samples/templates/largeSpreadsheet.php +++ b/samples/templates/largeSpreadsheet.php @@ -3,6 +3,7 @@ // Create new Spreadsheet object use PhpOffice\PhpSpreadsheet\Spreadsheet; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); diff --git a/samples/templates/sampleSpreadsheet.php b/samples/templates/sampleSpreadsheet.php index 998f21105..2f30139ee 100644 --- a/samples/templates/sampleSpreadsheet.php +++ b/samples/templates/sampleSpreadsheet.php @@ -14,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); diff --git a/samples/templates/sampleSpreadsheet2.php b/samples/templates/sampleSpreadsheet2.php index 76b5e0454..f6a43820b 100644 --- a/samples/templates/sampleSpreadsheet2.php +++ b/samples/templates/sampleSpreadsheet2.php @@ -14,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Worksheet\Drawing; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; +/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */ $helper->log('Create new Spreadsheet object'); $spreadsheet = new Spreadsheet(); diff --git a/src/PhpSpreadsheet/Helper/Sample.php b/src/PhpSpreadsheet/Helper/Sample.php index f7e380f78..7176696dd 100644 --- a/src/PhpSpreadsheet/Helper/Sample.php +++ b/src/PhpSpreadsheet/Helper/Sample.php @@ -185,10 +185,10 @@ class Sample return $temporaryFilename . '.' . $extension; } - public function log(string $message): void + public function log(mixed $message): void { $eol = $this->isCli() ? PHP_EOL : '
'; - echo ($this->isCli() ? date('H:i:s ') : '') . $message . $eol; + echo ($this->isCli() ? date('H:i:s ') : '') . StringHelper::convertToString($message) . $eol; } /**