From 3ddd12a49b501c9fddd251982f1f48de22e01b03 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Tue, 29 Jun 2021 19:28:56 +0200 Subject: [PATCH 1/6] Update complex numbe library to version 3 (#2198) --- composer.json | 2 +- composer.lock | 62 ++++++++------------------------------------------- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/composer.json b/composer.json index 57ec895e..720039f1 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,7 @@ "ext-zlib": "*", "ezyang/htmlpurifier": "^4.13", "maennchen/zipstream-php": "^2.1", - "markbaker/complex": "^2.0", + "markbaker/complex": "^3.0", "markbaker/matrix": "^2.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", diff --git a/composer.lock b/composer.lock index fa6bed6f..9bfe87d0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9158fcde13425499acaf0da201637737", + "content-hash": "475a2da5744e2426d6201ca5d9c56283", "packages": [ { "name": "ezyang/htmlpurifier", @@ -133,16 +133,16 @@ }, { "name": "markbaker/complex", - "version": "2.0.2", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/MarkBaker/PHPComplex.git", - "reference": "d18272926d58065140314c01e18ec3dd7ae854ea" + "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/d18272926d58065140314c01e18ec3dd7ae854ea", - "reference": "d18272926d58065140314c01e18ec3dd7ae854ea", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/ab8bc271e404909db09ff2d5ffa1e538085c0f22", + "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22", "shasum": "" }, "require": { @@ -158,51 +158,7 @@ "autoload": { "psr-4": { "Complex\\": "classes/src/" - }, - "files": [ - "classes/src/functions/abs.php", - "classes/src/functions/acos.php", - "classes/src/functions/acosh.php", - "classes/src/functions/acot.php", - "classes/src/functions/acoth.php", - "classes/src/functions/acsc.php", - "classes/src/functions/acsch.php", - "classes/src/functions/argument.php", - "classes/src/functions/asec.php", - "classes/src/functions/asech.php", - "classes/src/functions/asin.php", - "classes/src/functions/asinh.php", - "classes/src/functions/atan.php", - "classes/src/functions/atanh.php", - "classes/src/functions/conjugate.php", - "classes/src/functions/cos.php", - "classes/src/functions/cosh.php", - "classes/src/functions/cot.php", - "classes/src/functions/coth.php", - "classes/src/functions/csc.php", - "classes/src/functions/csch.php", - "classes/src/functions/exp.php", - "classes/src/functions/inverse.php", - "classes/src/functions/ln.php", - "classes/src/functions/log2.php", - "classes/src/functions/log10.php", - "classes/src/functions/negative.php", - "classes/src/functions/pow.php", - "classes/src/functions/rho.php", - "classes/src/functions/sec.php", - "classes/src/functions/sech.php", - "classes/src/functions/sin.php", - "classes/src/functions/sinh.php", - "classes/src/functions/sqrt.php", - "classes/src/functions/tan.php", - "classes/src/functions/tanh.php", - "classes/src/functions/theta.php", - "classes/src/operations/add.php", - "classes/src/operations/subtract.php", - "classes/src/operations/multiply.php", - "classes/src/operations/divideby.php", - "classes/src/operations/divideinto.php" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -222,9 +178,9 @@ ], "support": { "issues": "https://github.com/MarkBaker/PHPComplex/issues", - "source": "https://github.com/MarkBaker/PHPComplex/tree/2.0.2" + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.1" }, - "time": "2021-05-24T10:53:30+00:00" + "time": "2021-06-29T15:32:53+00:00" }, { "name": "markbaker/matrix", @@ -5210,5 +5166,5 @@ "ext-zlib": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } From 49e97f0914e7f0d5d3ed7ef7d228566c1af7a0a1 Mon Sep 17 00:00:00 2001 From: oleibman Date: Tue, 29 Jun 2021 10:54:08 -0700 Subject: [PATCH 2/6] Correct Some Problems Which Will Show Up for PHP8.1 (#2191) * Reader/Gnumeric vs. Scrutinizer Just reviewing Scrutinizer's list of "bugs". There are 19 ascribed to me. For some, I will definitely take no action (e.g. use of bitwise operators in AND, OR, and XOR functions). However, where I can clean things up so that Scrutinizer is satisfied and the resulting code is not too contorted, I will make an attempt. I believe this is the only one with which will involve more than 2 or 3 changes. It fixes 5 items ascribed to me, and 4 to others. * Use Strict Checking for in_array * Correct Some Problems Which Will Show Up for PHP8.1 PHP8.1 wants to issue a message when you use a float where it thinks you ought to be using an int (it wants its implicit casts made explicit). This is causing unit tests to fail. The following corrections are made in this PR: - Calculation.php tests `isset(self::binaryOperators[$token])`, where token can be a float. No numeric values are members of that array, so we can test for numeric before isset. - SharedOle.php packs a float, intending it as an int, in 2 places. I simplified the logic here, and added explicit casts to avoid the problem. This is used by Xls Reader and Writer; as added confirmation, I added some timestamps from before 1970 (i.e. negative values) to Document/EpochTest. Because of this, the test suite has been verified for 32-bit PHP as well as PHP 8.1. - Writer/Xlsx/StringTable tests `isset($aFlippedStringTable[$cellValue])`. This is the same problem as in Calculation, but requires a different solution. The same if statement here also tests that the datatype is string, but it does so after the isset test. Changing the order of these tests avoids the problem. * Update OLE.php --- .../Calculation/Calculation.php | 2 +- src/PhpSpreadsheet/Shared/OLE.php | 22 +++++-------------- .../Writer/Xlsx/StringTable.php | 4 ++-- .../Document/EpochTest.php | 3 +++ 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index eb111437..0d771ff7 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -4477,7 +4477,7 @@ class Calculation } // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack - if (isset(self::$binaryOperators[$token])) { + if (!is_numeric($token) && isset(self::$binaryOperators[$token])) { // We must have two operands, error if we don't if (($operand2Data = $stack->pop()) === null) { return $this->raiseFormulaError('Internal error - Operand value missing from stack'); diff --git a/src/PhpSpreadsheet/Shared/OLE.php b/src/PhpSpreadsheet/Shared/OLE.php index 8ecfc6be..6278553f 100644 --- a/src/PhpSpreadsheet/Shared/OLE.php +++ b/src/PhpSpreadsheet/Shared/OLE.php @@ -502,9 +502,6 @@ class OLE } $dateTime = Date::dateTimeFromTimestamp("$date"); - // factor used for separating numbers into 4 bytes parts - $factor = 2 ** 32; - // days from 1-1-1601 until the beggining of UNIX era $days = 134774; // calculate seconds @@ -512,22 +509,15 @@ class OLE // multiply just to make MS happy $big_date *= 10000000; - $high_part = floor($big_date / $factor); - // lower 4 bytes - $low_part = floor((($big_date / $factor) - $high_part) * $factor); - // Make HEX string $res = ''; - for ($i = 0; $i < 4; ++$i) { - $hex = $low_part % 0x100; - $res .= pack('c', $hex); - $low_part /= 0x100; - } - for ($i = 0; $i < 4; ++$i) { - $hex = $high_part % 0x100; - $res .= pack('c', $hex); - $high_part /= 0x100; + $factor = 2 ** 56; + while ($factor >= 1) { + $hex = (int) floor($big_date / $factor); + $res = pack('c', $hex) . $res; + $big_date = fmod($big_date, $factor); + $factor /= 256; } return $res; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index b0f7d6d4..b73d53cc 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -42,8 +42,8 @@ class StringTable extends WriterPart !is_object($cellValue) && ($cellValue !== null) && $cellValue !== '' && - !isset($aFlippedStringTable[$cellValue]) && - ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) + ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) && + !isset($aFlippedStringTable[$cellValue]) ) { $aStringTable[] = $cellValue; $aFlippedStringTable[$cellValue] = true; diff --git a/tests/PhpSpreadsheetTests/Document/EpochTest.php b/tests/PhpSpreadsheetTests/Document/EpochTest.php index 63c51a59..5ea5c5a8 100644 --- a/tests/PhpSpreadsheetTests/Document/EpochTest.php +++ b/tests/PhpSpreadsheetTests/Document/EpochTest.php @@ -11,10 +11,13 @@ class EpochTest extends AbstractFunctional public function providerFormats(): array { return [ + ['Ods', '1921-03-17 11:30:00Z'], ['Ods', '2021-03-17 11:30:00Z'], ['Ods', '2041-03-17 11:30:00Z'], + ['Xls', '1921-03-17 11:30:00Z'], ['Xls', '2021-03-17 11:30:00Z'], ['Xls', '2041-03-17 11:30:00Z'], + ['Xlsx', '1921-03-17 11:30:00Z'], ['Xlsx', '2021-03-17 11:30:00Z'], ['Xlsx', '2041-03-17 11:30:00Z'], ]; From 2ae948a31915c19e2748d26845eee8f4bb012302 Mon Sep 17 00:00:00 2001 From: oleibman Date: Tue, 29 Jun 2021 11:48:31 -0700 Subject: [PATCH 3/6] Reader/Slk vs. Scrutinizer/Phpstan (#2192) Just reviewing Scrutinizer's list of "bugs". There are 19 ascribed to me. For some, I will definitely take no action (e.g. use of bitwise operators in AND, OR, and XOR functions). However, where I can clean things up so that Scrutinizer is satisfied and the resulting code is not too contorted, I will make an attempt. This PR corrects 3 problems (2 mine) according to Scrutinizer, and 7 per Phpstan. It also moves the Reader Slk tests under their own directory, as is the case for all the other Reader types. --- phpstan-baseline.neon | 35 ------------------- src/PhpSpreadsheet/Reader/Slk.php | 33 ++++++++--------- .../Reader/{ => Slk}/SlkTest.php | 9 +++-- 3 files changed, 23 insertions(+), 54 deletions(-) rename tests/PhpSpreadsheetTests/Reader/{ => Slk}/SlkTest.php (96%) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3538d091..54c05805 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2565,41 +2565,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php - - - message: "#^Parameter \\#1 \\$haystack of function substr_count expects string, string\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, string\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Slk\\:\\:\\$colorArray has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Slk\\:\\:\\$fontStyleMappings has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Slk\\:\\:\\$styleSettingsFont has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Slk\\:\\:\\$styleSettingsBorder has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Slk.php - - - - message: "#^Parameter \\#1 \\$columnIndex of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:stringFromColumnIndex\\(\\) expects int, string given\\.$#" - count: 3 - path: src/PhpSpreadsheet/Reader/Slk.php - - message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\\\SpContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:getDgContainer\\(\\)\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Reader/Slk.php b/src/PhpSpreadsheet/Reader/Slk.php index b58fdcba..4e76a161 100644 --- a/src/PhpSpreadsheet/Reader/Slk.php +++ b/src/PhpSpreadsheet/Reader/Slk.php @@ -79,7 +79,7 @@ class Slk extends BaseReader } // Read sample data (first 2 KB will do) - $data = fread($this->fileHandle, 2048); + $data = (string) fread($this->fileHandle, 2048); // Count delimiters in file $delimiterCount = substr_count($data, ';'); @@ -210,7 +210,7 @@ class Slk extends BaseReader return $this->loadIntoExisting($pFilename, $spreadsheet); } - private $colorArray = [ + private const COLOR_ARRAY = [ 'FF00FFFF', // 0 - cyan 'FF000000', // 1 - black 'FFFFFFFF', // 2 - white @@ -221,7 +221,7 @@ class Slk extends BaseReader 'FFFF00FF', // 7 - magenta ]; - private $fontStyleMappings = [ + private const FONT_STYLE_MAPPINGS = [ 'B' => 'bold', 'I' => 'italic', 'U' => 'underline', @@ -235,7 +235,8 @@ class Slk extends BaseReader $key = false; foreach ($temp as &$value) { // Only count/replace in alternate array entries - if ($key = !$key) { + $key = !$key; + if ($key) { preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); // Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way // through the formula from left to right. Reversing means that we work right to left.through @@ -357,9 +358,9 @@ class Slk extends BaseReader $this->addWidth($spreadsheet, $columnWidth, $startCol, $endCol); } - private $styleSettingsFont = ['D' => 'bold', 'I' => 'italic']; + private const STYLE_SETTINGS_FONT = ['D' => 'bold', 'I' => 'italic']; - private $styleSettingsBorder = [ + private const STYLE_SETTINGS_BORDER = [ 'B' => 'bottom', 'L' => 'left', 'R' => 'right', @@ -372,10 +373,10 @@ class Slk extends BaseReader $iMax = strlen($styleSettings); for ($i = 0; $i < $iMax; ++$i) { $char = $styleSettings[$i]; - if (array_key_exists($char, $this->styleSettingsFont)) { - $styleData['font'][$this->styleSettingsFont[$char]] = true; - } elseif (array_key_exists($char, $this->styleSettingsBorder)) { - $styleData['borders'][$this->styleSettingsBorder[$char]]['borderStyle'] = Border::BORDER_THIN; + if (array_key_exists($char, self::STYLE_SETTINGS_FONT)) { + $styleData['font'][self::STYLE_SETTINGS_FONT[$char]] = true; + } elseif (array_key_exists($char, self::STYLE_SETTINGS_BORDER)) { + $styleData['borders'][self::STYLE_SETTINGS_BORDER[$char]]['borderStyle'] = Border::BORDER_THIN; } elseif ($char == 'S') { $styleData['fill']['fillType'] = \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_PATTERN_GRAY125; } elseif ($char == 'M') { @@ -409,7 +410,7 @@ class Slk extends BaseReader private function addStyle(Spreadsheet &$spreadsheet, array $styleData, string $row, string $column): void { if ((!empty($styleData)) && $column > '' && $row > '') { - $columnLetter = Coordinate::stringFromColumnIndex($column); + $columnLetter = Coordinate::stringFromColumnIndex((int) $column); $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData); } } @@ -421,8 +422,8 @@ class Slk extends BaseReader $startCol = Coordinate::stringFromColumnIndex((int) $startCol); $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth); } else { - $startCol = Coordinate::stringFromColumnIndex($startCol); - $endCol = Coordinate::stringFromColumnIndex($endCol); + $startCol = Coordinate::stringFromColumnIndex((int) $startCol); + $endCol = Coordinate::stringFromColumnIndex((int) $endCol); $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth); do { $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth((float) $columnWidth); @@ -469,7 +470,7 @@ class Slk extends BaseReader { if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) { $fontColor = $matches[1] % 8; - $formatArray['font']['color']['argb'] = $this->colorArray[$fontColor]; + $formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor]; } } @@ -478,8 +479,8 @@ class Slk extends BaseReader $styleSettings = substr($rowDatum, 1); $iMax = strlen($styleSettings); for ($i = 0; $i < $iMax; ++$i) { - if (array_key_exists($styleSettings[$i], $this->fontStyleMappings)) { - $formatArray['font'][$this->fontStyleMappings[$styleSettings[$i]]] = true; + if (array_key_exists($styleSettings[$i], self::FONT_STYLE_MAPPINGS)) { + $formatArray['font'][self::FONT_STYLE_MAPPINGS[$styleSettings[$i]]] = true; } } } diff --git a/tests/PhpSpreadsheetTests/Reader/SlkTest.php b/tests/PhpSpreadsheetTests/Reader/Slk/SlkTest.php similarity index 96% rename from tests/PhpSpreadsheetTests/Reader/SlkTest.php rename to tests/PhpSpreadsheetTests/Reader/Slk/SlkTest.php index 6881648d..3a7dff6c 100644 --- a/tests/PhpSpreadsheetTests/Reader/SlkTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Slk/SlkTest.php @@ -1,6 +1,6 @@ filename) { + if ($this->filename !== '') { unlink($this->filename); $this->filename = ''; } @@ -134,6 +134,7 @@ class SlkTest extends \PHPUnit\Framework\TestCase self::assertEquals(Border::BORDER_THIN, $sheet->getCell('C18')->getStyle()->getBorders()->getBottom()->getBorderStyle()); self::assertEquals(Border::BORDER_THIN, $sheet->getCell('C18')->getStyle()->getBorders()->getLeft()->getBorderStyle()); // Have not yet figured out how C6/C7 are centred + $spreadsheet->disconnectWorksheets(); } public function testSheetIndex(): void @@ -147,6 +148,7 @@ class SlkTest extends \PHPUnit\Framework\TestCase self::assertEquals('SylkTest', $sheet->getTitle()); self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB()); + $spreadsheet->disconnectWorksheets(); } public function testLongName(): void @@ -160,5 +162,6 @@ class SlkTest extends \PHPUnit\Framework\TestCase $sheet = $spreadsheet->getActiveSheet(); self::assertEquals('123456789a123456789b123456789c1', $sheet->getTitle()); self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB()); + $spreadsheet->disconnectWorksheets(); } } From 435ac30b471e151121821745062a9efdc09487a7 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Wed, 30 Jun 2021 19:19:30 -0700 Subject: [PATCH 4/6] Reader/Html vs. Scrutinizer/Phpstan Just reviewing Scrutinizer's list of "bugs". There are 19 ascribed to me. For some, I will definitely take no action (e.g. use of bitwise operators in AND, OR, and XOR functions). However, where I can clean things up so that Scrutinizer is satisfied and the resulting code is not too contorted, I will make an attempt. This PR corrects 2 problems according to Scrutinizer, and about 30 per Phpstan. --- phpstan-baseline.neon | 145 ----------------------------- src/PhpSpreadsheet/Reader/Html.php | 94 ++++++++++++------- 2 files changed, 61 insertions(+), 178 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 54c05805..274764b1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2260,151 +2260,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/BaseReader.php - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$rowspan has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:readBeginning\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:readEnding\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Parameter \\#2 \\$length of function fread expects int, int\\\\|int\\<1, 2048\\>\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:startsWithTag\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:startsWithTag\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:endsWithTag\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:endsWithTag\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:containsTags\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:containsTags\\(\\) has parameter \\$data with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$dataArray has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$tableLevel has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$nestedColumn has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:setTableStartColumn\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:setTableStartColumn\\(\\) has parameter \\$column with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:getTableStartColumn\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:releaseTableStartColumn\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:flushCell\\(\\) has parameter \\$cellContent with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:flushCell\\(\\) has parameter \\$column with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:flushCell\\(\\) has parameter \\$row with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Argument of an invalid type DOMNamedNodeMap\\|null supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$spanEtc has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$h1Etc has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Parameter \\#2 \\$styleValue of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:setBorderStyle\\(\\) expects string, string\\|null given\\.$#" - count: 5 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Alignment\\:\\:setHorizontal\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Alignment\\:\\:setVertical\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:getStyleColor\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$borderMappings has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Html.php - - message: "#^Cannot call method getNamespaces\\(\\) on SimpleXMLElement\\|false\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index fe2ea018..342ce560 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -123,6 +123,7 @@ class Html extends BaseReader ], // Italic ]; + /** @var array */ protected $rowspan = []; /** @@ -160,19 +161,19 @@ class Html extends BaseReader return $startWithTag && $containsTags && $endsWithTag; } - private function readBeginning() + private function readBeginning(): string { fseek($this->fileHandle, 0); - return fread($this->fileHandle, self::TEST_SAMPLE_SIZE); + return (string) fread($this->fileHandle, self::TEST_SAMPLE_SIZE); } - private function readEnding() + private function readEnding(): string { $meta = stream_get_meta_data($this->fileHandle); $filename = $meta['uri']; - $size = filesize($filename); + $size = (int) filesize($filename); if ($size === 0) { return ''; } @@ -184,20 +185,20 @@ class Html extends BaseReader fseek($this->fileHandle, $size - $blockSize); - return fread($this->fileHandle, $blockSize); + return (string) fread($this->fileHandle, $blockSize); } - private static function startsWithTag($data) + private static function startsWithTag(string $data): bool { return '<' === substr(trim($data), 0, 1); } - private static function endsWithTag($data) + private static function endsWithTag(string $data): bool { return '>' === substr(trim($data), -1, 1); } - private static function containsTags($data) + private static function containsTags(string $data): bool { return strlen($data) !== strlen(strip_tags($data)); } @@ -251,13 +252,17 @@ class Html extends BaseReader } // Data Array used for testing only, should write to Spreadsheet object on completion of tests + + /** @var array */ protected $dataArray = []; + /** @var int */ protected $tableLevel = 0; + /** @var array */ protected $nestedColumn = ['A']; - protected function setTableStartColumn($column) + protected function setTableStartColumn(string $column): string { if ($this->tableLevel == 0) { $column = 'A'; @@ -268,18 +273,25 @@ class Html extends BaseReader return $this->nestedColumn[$this->tableLevel]; } - protected function getTableStartColumn() + protected function getTableStartColumn(): string { return $this->nestedColumn[$this->tableLevel]; } - protected function releaseTableStartColumn() + protected function releaseTableStartColumn(): string { --$this->tableLevel; return array_pop($this->nestedColumn); } + /** + * Flush cell. + * + * @param string $column + * @param int|string $row + * @param mixed $cellContent + */ protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void { if (is_string($cellContent)) { @@ -302,7 +314,7 @@ class Html extends BaseReader private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void { $attributeArray = []; - foreach ($child->attributes as $attribute) { + foreach (($child->attributes ?? []) as $attribute) { $attributeArray[$attribute->name] = $attribute->value; } @@ -328,11 +340,11 @@ class Html extends BaseReader } } - private static $spanEtc = ['span', 'div', 'font', 'i', 'em', 'strong', 'b']; + private const SPAN_ETC = ['span', 'div', 'font', 'i', 'em', 'strong', 'b']; private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void { - if (in_array($child->nodeName, self::$spanEtc)) { + if (in_array((string) $child->nodeName, self::SPAN_ETC, true)) { if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') { $sheet->getComment($column . $row) ->getText() @@ -405,11 +417,11 @@ class Html extends BaseReader } } - private static $h1Etc = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p']; + private const H1_ETC = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p']; private function processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void { - if (in_array($child->nodeName, self::$h1Etc)) { + if (in_array((string) $child->nodeName, self::H1_ETC, true)) { if ($this->tableLevel > 0) { // If we're inside a table, replace with a \n $cellContent .= $cellContent ? "\n" : ''; @@ -628,6 +640,16 @@ class Html extends BaseReader } } + /** + * Make sure mb_convert_encoding returns string. + * + * @param mixed $result + */ + private static function ensureString($result): string + { + return is_string($result) ? $result : ''; + } + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. * @@ -646,7 +668,8 @@ class Html extends BaseReader $dom = new DOMDocument(); // Reload the HTML file into the DOM object try { - $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8')); + $convert = mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'); + $loaded = $dom->loadHTML(self::ensureString($convert)); } catch (Throwable $e) { $loaded = false; } @@ -668,7 +691,8 @@ class Html extends BaseReader $dom = new DOMDocument(); // Reload the HTML file into the DOM object try { - $loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8')); + $convert = mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'); + $loaded = $dom->loadHTML(self::ensureString($convert)); } catch (Throwable $e) { $loaded = false; } @@ -774,6 +798,7 @@ class Html extends BaseReader $value = explode(':', $st); $styleName = isset($value[0]) ? trim($value[0]) : null; $styleValue = isset($value[1]) ? trim($value[1]) : null; + $styleValueString = (string) $styleValue; if (!$styleName) { continue; @@ -782,7 +807,7 @@ class Html extends BaseReader switch ($styleName) { case 'background': case 'background-color': - $styleColor = $this->getStyleColor($styleValue); + $styleColor = $this->getStyleColor($styleValueString); if (!$styleColor) { continue 2; @@ -792,7 +817,7 @@ class Html extends BaseReader break; case 'color': - $styleColor = $this->getStyleColor($styleValue); + $styleColor = $this->getStyleColor($styleValueString); if (!$styleColor) { continue 2; @@ -803,27 +828,27 @@ class Html extends BaseReader break; case 'border': - $this->setBorderStyle($cellStyle, $styleValue, 'allBorders'); + $this->setBorderStyle($cellStyle, $styleValueString, 'allBorders'); break; case 'border-top': - $this->setBorderStyle($cellStyle, $styleValue, 'top'); + $this->setBorderStyle($cellStyle, $styleValueString, 'top'); break; case 'border-bottom': - $this->setBorderStyle($cellStyle, $styleValue, 'bottom'); + $this->setBorderStyle($cellStyle, $styleValueString, 'bottom'); break; case 'border-left': - $this->setBorderStyle($cellStyle, $styleValue, 'left'); + $this->setBorderStyle($cellStyle, $styleValueString, 'left'); break; case 'border-right': - $this->setBorderStyle($cellStyle, $styleValue, 'right'); + $this->setBorderStyle($cellStyle, $styleValueString, 'right'); break; @@ -849,7 +874,7 @@ class Html extends BaseReader break; case 'font-family': - $cellStyle->getFont()->setName(str_replace('\'', '', $styleValue)); + $cellStyle->getFont()->setName(str_replace('\'', '', $styleValueString)); break; @@ -868,12 +893,12 @@ class Html extends BaseReader break; case 'text-align': - $cellStyle->getAlignment()->setHorizontal($styleValue); + $cellStyle->getAlignment()->setHorizontal($styleValueString); break; case 'vertical-align': - $cellStyle->getAlignment()->setVertical($styleValue); + $cellStyle->getAlignment()->setVertical($styleValueString); break; @@ -900,7 +925,7 @@ class Html extends BaseReader case 'text-indent': $cellStyle->getAlignment()->setIndent( - (int) str_replace(['px'], '', $styleValue) + (int) str_replace(['px'], '', $styleValueString) ); break; @@ -911,15 +936,18 @@ class Html extends BaseReader /** * Check if has #, so we can get clean hex. * + * @param mixed $value + * * @return null|string */ public function getStyleColor($value) { + $value = (string) $value; if (strpos($value ?? '', '#') === 0) { return substr($value, 1); } - return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup((string) $value); + return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup($value); } /** @@ -966,7 +994,7 @@ class Html extends BaseReader ); } - private static $borderMappings = [ + private const BORDER_MAPPINGS = [ 'dash-dot' => Border::BORDER_DASHDOT, 'dash-dot-dot' => Border::BORDER_DASHDOTDOT, 'dashed' => Border::BORDER_DASHED, @@ -985,7 +1013,7 @@ class Html extends BaseReader public static function getBorderMappings(): array { - return self::$borderMappings; + return self::BORDER_MAPPINGS; } /** @@ -997,7 +1025,7 @@ class Html extends BaseReader */ public function getBorderStyle($style) { - return (array_key_exists($style, self::$borderMappings)) ? self::$borderMappings[$style] : null; + return self::BORDER_MAPPINGS[$style] ?? null; } /** From b03544469bebe9111f36b442fd6b22146aaa5378 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Wed, 30 Jun 2021 18:56:25 -0700 Subject: [PATCH 5/6] 2 Tests vs. Scrutinizer/Phpstan Just reviewing Scrutinizer's list of "bugs". There are 19 ascribed to me. For some, I will definitely take no action (e.g. use of bitwise operators in AND, OR, and XOR functions). However, where I can clean things up so that Scrutinizer is satisfied and the resulting code is not too contorted, I will make an attempt. This PR corrects 2 problems according to Scrutinizer, and 1 per Phpstan. Only test members are involved. --- phpstan-baseline.neon | 5 ----- .../Calculation/Functions/MathTrig/SubTotalTest.php | 2 +- .../Writer/Html/ImagesRootTest.php | 12 +++++++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 274764b1..78979ae9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6800,11 +6800,6 @@ parameters: count: 3 path: tests/PhpSpreadsheetTests/Writer/Html/HtmlCommentsTest.php - - - message: "#^Parameter \\#1 \\$directory of function chdir expects string, string\\|false given\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php - - message: "#^Parameter \\#1 \\$options of static method PhpOffice\\\\PhpSpreadsheet\\\\Settings\\:\\:setLibXmlLoaderOptions\\(\\) expects int, null given\\.$#" count: 1 diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php index 2a54e459..cf79ac0b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php @@ -92,7 +92,7 @@ class SubTotalTest extends AllSetupTeardown '12' => false, ]; foreach ($visibleRows as $row => $visible) { - $rowDimension = $sheet->getRowDimension($row); + $rowDimension = $sheet->getRowDimension((int) $row); $rowDimension->setVisible($visible); } $sheet->getCell('D2')->setValue("=SUBTOTAL($type, A1:$maxCol$maxRow)"); diff --git a/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php b/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php index 40099177..b36a87c0 100644 --- a/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php @@ -10,13 +10,18 @@ use PhpOffice\PhpSpreadsheetTests\Functional; class ImagesRootTest extends Functional\AbstractFunctional { /** - * @var false|string + * @var string */ - private $curdir; + private $curdir = ''; protected function setUp(): void { - $this->curdir = getcwd(); + $curdir = getcwd(); + if ($curdir === false) { + self::fail('Unable to obtain current directory'); + } else { + $this->curdir = $curdir; + } } protected function tearDown(): void @@ -64,5 +69,6 @@ class ImagesRootTest extends Functional\AbstractFunctional self::assertCount(1, $img); self::assertEquals("$root/$stub", $img[0]->getAttribute('src')); self::assertEquals($desc, $img[0]->getAttribute('alt')); + $spreadsheet->disconnectWorksheets(); } } From 560e9a885c5c353f626ac6cf722ab0f7a997eec8 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Wed, 30 Jun 2021 19:47:12 -0700 Subject: [PATCH 6/6] CashFlow/Variable/NonPeriodic vs. Scrutinizer/Phpstan Just reviewing Scrutinizer's list of "bugs". There are 19 ascribed to me. For some, I will definitely take no action (e.g. use of bitwise operators in AND, OR, and XOR functions). However, where I can clean things up so that Scrutinizer is satisfied and the resulting code is not too contorted, I will make an attempt. This is the last of this set of changes. It corrects 2 problems according to Scrutinizer, and about 20 per Phpstan. --- phpstan-baseline.neon | 105 ------------------ .../CashFlow/Variable/NonPeriodic.php | 37 ++++-- 2 files changed, 28 insertions(+), 114 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 78979ae9..c7a45cf5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -525,111 +525,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:bothNegAndPos\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:bothNegAndPos\\(\\) has parameter \\$neg with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:bothNegAndPos\\(\\) has parameter \\$pos with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart1\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart1\\(\\) has parameter \\$dates with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart1\\(\\) has parameter \\$values with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart2\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart2\\(\\) has parameter \\$values with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart3\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart3\\(\\) has parameter \\$dates with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart3\\(\\) has parameter \\$values with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart3\\(\\) has parameter \\$x1 with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xirrPart3\\(\\) has parameter \\$x2 with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) has parameter \\$dates with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) has parameter \\$ordered with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) has parameter \\$rate with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) has parameter \\$values with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:validateXnpv\\(\\) has parameter \\$dates with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:validateXnpv\\(\\) has parameter \\$rate with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:validateXnpv\\(\\) has parameter \\$values with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\Periodic\\:\\:presentValue\\(\\) has parameter \\$args with no typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php index 6a32bdfc..8986146c 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php @@ -32,7 +32,7 @@ class NonPeriodic public static function rate($values, $dates, $guess = 0.1) { $rslt = self::xirrPart1($values, $dates); - if ($rslt) { + if ($rslt !== '') { return $rslt; } @@ -47,6 +47,8 @@ class NonPeriodic if (!is_numeric($f1) || !is_numeric($f2)) { break; } + $f1 = (float) $f1; + $f2 = (float) $f2; if (($f1 * $f2) < 0.0) { $found = true; @@ -91,14 +93,18 @@ class NonPeriodic return self::xnpvOrdered($rate, $values, $dates, true); } - private static function bothNegAndPos($neg, $pos) + private static function bothNegAndPos(bool $neg, bool $pos): bool { return $neg && $pos; } - private static function xirrPart1(&$values, &$dates) + /** + * @param mixed $values + * @param mixed $dates + */ + private static function xirrPart1(&$values, &$dates): string { - if ((!is_array($values)) && (!is_array($dates))) { + if (!is_array($values) && !is_array($dates)) { return Functions::NA(); } $values = Functions::flattenArray($values); @@ -119,7 +125,7 @@ class NonPeriodic return self::xirrPart2($values); } - private static function xirrPart2(&$values) + private static function xirrPart2(array &$values): string { $valCount = count($values); $foundpos = false; @@ -141,7 +147,10 @@ class NonPeriodic return ''; } - private static function xirrPart3($values, $dates, $x1, $x2) + /** + * @return float|string + */ + private static function xirrPart3(array $values, array $dates, float $x1, float $x2) { $f = self::xnpvOrdered($x1, $values, $dates, false); if ($f < 0.0) { @@ -156,7 +165,7 @@ class NonPeriodic for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) { $dx *= 0.5; $x_mid = $rtb + $dx; - $f_mid = self::xnpvOrdered($x_mid, $values, $dates, false); + $f_mid = (float) self::xnpvOrdered($x_mid, $values, $dates, false); if ($f_mid <= 0.0) { $rtb = $x_mid; } @@ -170,7 +179,14 @@ class NonPeriodic return $rslt; } - private static function xnpvOrdered($rate, $values, $dates, $ordered = true) + /** + * @param mixed $rate + * @param mixed $values + * @param mixed $dates + * + * @return float|string + */ + private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true) { $rate = Functions::flattenSingleValue($rate); $values = Functions::flattenArray($values); @@ -209,7 +225,10 @@ class NonPeriodic return is_finite($xnpv) ? $xnpv : Functions::VALUE(); } - private static function validateXnpv($rate, $values, $dates): void + /** + * @param mixed $rate + */ + private static function validateXnpv($rate, array $values, array $dates): void { if (!is_numeric($rate)) { throw new Exception(Functions::VALUE());