Real Errors Identified in Calculation by Scrutinizer (#2774)

* Real Errors Identified in Calculation by Scrutinizer

Before Scrutinizer broke, I took a look at the remaining 43 errors which it categorized as 'major'. Most of these were false positives, but, in the case of Calculation and Reader/Xlsx/Chart, I was able to determine that its analysis of some of the problems was correct. There is little point addressing the false positives until it starts working again, but we should fix the real errors.

This PR addresses the real errors in Calculation.
- A test for `$pCellParent` should have been a test  for `$pCellWorksheet`.
- A test for `$operand1Data['reference']` should have been a test for `$operand1Data['value']`.
- A test for `$operand2Data['reference']` should have been a test for `$operand2Data['value']`.

* Fix Attempt to Erroneously Call trim on Array

Fix #2780. Warning message is being issued when getting calculated value of cell with value `=INDIRECT(ADDRESS(ROW(),COLUMN()-2))/$C$4`. This appears to be the case for all recent (and probably not so recent) releases; it is not a result of changes to the code base. Fix added to this PR because the erring section of code was proximate to code already changed in the PR. Test added.

* Minor Code Changes

Apply some suggestions from @MarkBaker
This commit is contained in:
oleibman 2022-04-30 19:13:17 -07:00 committed by GitHub
parent 0a531cf1cd
commit 766252ccb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 45 deletions

View File

@ -20,16 +20,6 @@ parameters:
count: 6 count: 6
path: src/PhpSpreadsheet/Calculation/Calculation.php path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Cannot call method getColumn\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Cannot call method getCoordinate\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Calculation/Calculation.php
- -
message: "#^Cannot call method getHighestColumn\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" message: "#^Cannot call method getHighestColumn\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1 count: 1
@ -40,21 +30,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Cannot call method getRow\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Cannot call method getTitle\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Cannot call method has\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:_translateFormulaToEnglish\\(\\) has no return type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:_translateFormulaToEnglish\\(\\) has no return type specified\\.$#"
count: 1 count: 1
@ -140,11 +115,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Parameter \\#1 \\$parent of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\:\\:attach\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells, PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php
- -
message: "#^Parameter \\#1 \\$str of function preg_quote expects string, int\\|string given\\.$#" message: "#^Parameter \\#1 \\$str of function preg_quote expects string, int\\|string given\\.$#"
count: 1 count: 1

View File

@ -3223,10 +3223,11 @@ class Calculation
// So instead we skip replacing in any quoted strings by only replacing in every other array element // So instead we skip replacing in any quoted strings by only replacing in every other array element
// after we've exploded the formula // after we've exploded the formula
$temp = explode(self::FORMULA_STRING_QUOTE, $formula); $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
$i = false; $notWithinQuotes = false;
foreach ($temp as &$value) { foreach ($temp as &$value) {
// Only adjust in alternating array entries // Only adjust in alternating array entries
if ($i = !$i) { $notWithinQuotes = !$notWithinQuotes;
if ($notWithinQuotes === true) {
$value = self::translateFormulaBlock($from, $to, $value, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator); $value = self::translateFormulaBlock($from, $to, $value, $inFunctionBracesLevel, $inMatrixBracesLevel, $fromSeparator, $toSeparator);
} }
} }
@ -3898,10 +3899,11 @@ class Calculation
$temp = explode(self::FORMULA_STRING_QUOTE, $formula); $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
// Open and Closed counts used for trapping mismatched braces in the formula // Open and Closed counts used for trapping mismatched braces in the formula
$openCount = $closeCount = 0; $openCount = $closeCount = 0;
$i = false; $notWithinQuotes = false;
foreach ($temp as &$value) { foreach ($temp as &$value) {
// Only count/replace in alternating array entries // Only count/replace in alternating array entries
if ($i = !$i) { $notWithinQuotes = !$notWithinQuotes;
if ($notWithinQuotes === true) {
$openCount += substr_count($value, self::FORMULA_OPEN_MATRIX_BRACE); $openCount += substr_count($value, self::FORMULA_OPEN_MATRIX_BRACE);
$closeCount += substr_count($value, self::FORMULA_CLOSE_MATRIX_BRACE); $closeCount += substr_count($value, self::FORMULA_CLOSE_MATRIX_BRACE);
$value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value); $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
@ -4593,7 +4595,7 @@ class Calculation
if (strpos($operand1Data['reference'], '!') !== false) { if (strpos($operand1Data['reference'], '!') !== false) {
[$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true); [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
} else { } else {
$sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : ''; $sheet1 = ($pCellWorksheet !== null) ? $pCellWorksheet->getTitle() : '';
} }
[$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true); [$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
@ -4602,21 +4604,23 @@ class Calculation
} }
if (trim($sheet1, "'") === trim($sheet2, "'")) { if (trim($sheet1, "'") === trim($sheet2, "'")) {
if ($operand1Data['reference'] === null) { if ($operand1Data['reference'] === null && $cell !== null) {
if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) { if (is_array($operand1Data['value'])) {
$operand1Data['reference'] = $cell->getCoordinate();
} elseif ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
$operand1Data['reference'] = $cell->getColumn() . $operand1Data['value']; $operand1Data['reference'] = $cell->getColumn() . $operand1Data['value'];
// @phpstan-ignore-next-line } elseif (trim($operand1Data['value']) == '') {
} elseif (trim($operand1Data['reference']) == '') {
$operand1Data['reference'] = $cell->getCoordinate(); $operand1Data['reference'] = $cell->getCoordinate();
} else { } else {
$operand1Data['reference'] = $operand1Data['value'] . $cell->getRow(); $operand1Data['reference'] = $operand1Data['value'] . $cell->getRow();
} }
} }
if ($operand2Data['reference'] === null) { if ($operand2Data['reference'] === null && $cell !== null) {
if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) { if (is_array($operand2Data['value'])) {
$operand2Data['reference'] = $cell->getCoordinate();
} elseif ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
$operand2Data['reference'] = $cell->getColumn() . $operand2Data['value']; $operand2Data['reference'] = $cell->getColumn() . $operand2Data['value'];
// @phpstan-ignore-next-line } elseif (trim($operand2Data['value']) == '') {
} elseif (trim($operand2Data['reference']) == '') {
$operand2Data['reference'] = $cell->getCoordinate(); $operand2Data['reference'] = $cell->getCoordinate();
} else { } else {
$operand2Data['reference'] = $operand2Data['value'] . $cell->getRow(); $operand2Data['reference'] = $operand2Data['value'] . $cell->getRow();
@ -4829,7 +4833,7 @@ class Calculation
$this->debugLog->writeDebugLog('Evaluation Result for cell %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue)); $this->debugLog->writeDebugLog('Evaluation Result for cell %s in worksheet %s is %s', $cellRef, $matches[2], $this->showTypeDetails($cellValue));
} else { } else {
$this->debugLog->writeDebugLog('Evaluating Cell %s in current worksheet', $cellRef); $this->debugLog->writeDebugLog('Evaluating Cell %s in current worksheet', $cellRef);
if ($pCellParent->has($cellRef)) { if ($pCellParent !== null && $pCellParent->has($cellRef)) {
$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false); $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
$cell->attach($pCellParent); $cell->attach($pCellParent);
} else { } else {
@ -5281,7 +5285,7 @@ class Calculation
} }
// Named range? // Named range?
$namedRange = DefinedName::resolveName($range, $worksheet); $namedRange = DefinedName::resolveName($range, /** @scrutinizer ignore-type */ $worksheet);
if ($namedRange === null) { if ($namedRange === null) {
return Information\ExcelError::REF(); return Information\ExcelError::REF();
} }

View File

@ -0,0 +1,26 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class Issue2778Test extends TestCase
{
/**
* @var string
*/
private static $testbook = 'tests/data/Reader/XLSX/issue.2778.xlsx';
public function testIssue2778(): void
{
$filename = self::$testbook;
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(6, $sheet->getCell('D1')->getCalculatedValue());
self::assertSame(63, $sheet->getCell('F1')->getCalculatedValue());
self::assertSame(10, $sheet->getCell('C10')->getCalculatedValue());
$spreadsheet->disconnectWorksheets();
}
}

Binary file not shown.