Update Some Doc Block Annotations (#2351)

* Update Some Doc Block Annotations

See PR #2010. That PR was never completed, and has gone stale. However, it was correct in identifying a situation where the doc block was not entirely accurate. It did not go far enough - several closely-related methods have similar problems. This PR attempts to fix the original problem and its close relations. Aside from the doc block changes, there are very minor changes to executable code. It also changes some of the unit tests targeted at the methods in question to eliminate mocking in favor of 'real' tests.

* Change Method to Static

Otherwise Scrutinizer will complain, even though Phpstan doesn't.

* Scrutinizer

Various clean-up activities.

* Scrutinizer

@#&$(*#&$ Got complexity down from 53 to (I think) 50. Don't really know what the target is.

* Code Changes Suggested By Review

Some improvements suggested in review by @PowerKiKi.

* Update Cells.php

* Merge Conflict

A change to a parameter name caused several problems when trying to fix it on Github. Fixing it locally should do the trick.

* Merge Conflicts in Phpstan Baseline

PR #2382 made a large number of changes to Phpstan Baseline, some of which conflicted with the Phpstan Baseline changes in this PR. This should resolve them all.
This commit is contained in:
oleibman 2021-11-11 23:38:05 -08:00 committed by GitHub
parent 5a704158e1
commit ffdae8efac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 257 additions and 377 deletions

View File

@ -220,11 +220,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Parameter \\#1 \\$row of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getHighestDataColumn\\(\\) expects string\\|null, mixed 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
@ -2950,46 +2945,11 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Collection/Cells.php path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Cannot call method detach\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:get\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null but returns mixed\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:get\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null but returns mixed\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Collection/Cells.php path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:getCurrentCoordinate\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:getParent\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet but returns PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Parameter \\#1 \\$columnIndex of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:stringFromColumnIndex\\(\\) expects int, int\\|false given\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Parameter \\#1 \\$str of function sscanf expects string, string\\|null given\\.$#"
count: 2
path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
-
message: "#^Possibly invalid array key type \\(array\\|string\\)\\.$#"
count: 1
path: src/PhpSpreadsheet/Collection/Cells.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:\\$currentCell \\(PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\) does not accept mixed\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:\\$currentCell \\(PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\) does not accept mixed\\.$#"
count: 1 count: 1
@ -8680,11 +8640,6 @@ parameters:
count: 1 count: 1
path: tests/PhpSpreadsheetTests/Collection/CellsTest.php path: tests/PhpSpreadsheetTests/Collection/CellsTest.php
-
message: "#^Parameter \\#1 \\$row of method PhpOffice\\\\PhpSpreadsheet\\\\Collection\\\\Cells\\:\\:getHighestColumn\\(\\) expects string\\|null, int given\\.$#"
count: 3
path: tests/PhpSpreadsheetTests/Collection/CellsTest.php
- -
message: "#^Parameter \\#1 \\$propertyName of method PhpOffice\\\\PhpSpreadsheet\\\\Document\\\\Properties\\:\\:getCustomPropertyType\\(\\) expects string, mixed given\\.$#" message: "#^Parameter \\#1 \\$propertyName of method PhpOffice\\\\PhpSpreadsheet\\\\Document\\\\Properties\\:\\:getCustomPropertyType\\(\\) expects string, mixed given\\.$#"
count: 1 count: 1

View File

@ -4230,7 +4230,9 @@ class Calculation
if (ctype_digit($val) && $val <= 1048576) { if (ctype_digit($val) && $val <= 1048576) {
// Row range // Row range
$stackItemType = 'Row Reference'; $stackItemType = 'Row Reference';
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($val) : 'XFD'; // Max 16,384 columns for Excel2007 /** @var string */
$valx = $val;
$endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : 'XFD'; // Max 16,384 columns for Excel2007
$val = "{$rangeWS2}{$endRowColRef}{$val}"; $val = "{$rangeWS2}{$endRowColRef}{$val}";
} elseif (ctype_alpha($val) && strlen($val) <= 3) { } elseif (ctype_alpha($val) && strlen($val) <= 3) {
// Column range // Column range

View File

@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Throwable;
class Cell class Cell
{ {
@ -139,7 +140,16 @@ class Cell
*/ */
public function getCoordinate() public function getCoordinate()
{ {
return $this->parent->getCurrentCoordinate(); try {
$coordinate = $this->parent->getCurrentCoordinate();
} catch (Throwable $e) {
$coordinate = null;
}
if ($coordinate === null) {
throw new Exception('Coordinate no longer exists');
}
return $coordinate;
} }
/** /**
@ -478,7 +488,17 @@ class Cell
*/ */
public function getWorksheet() public function getWorksheet()
{ {
return $this->parent->getParent(); try {
$worksheet = $this->parent->getParent();
} catch (Throwable $e) {
$worksheet = null;
}
if ($worksheet === null) {
throw new Exception('Worksheet no longer exists');
}
return $worksheet;
} }
/** /**

View File

@ -76,7 +76,7 @@ class Cells
/** /**
* Return the parent worksheet for this cell collection. * Return the parent worksheet for this cell collection.
* *
* @return Worksheet * @return null|Worksheet
*/ */
public function getParent() public function getParent()
{ {
@ -181,7 +181,7 @@ class Cells
// Determine highest column and row // Determine highest column and row
$highestRow = max($row); $highestRow = max($row);
$highestColumn = substr(max($col), 1); $highestColumn = substr((string) @max($col), 1);
return [ return [
'row' => $highestRow, 'row' => $highestRow,
@ -192,7 +192,7 @@ class Cells
/** /**
* Return the cell coordinate of the currently active cell object. * Return the cell coordinate of the currently active cell object.
* *
* @return string * @return null|string
*/ */
public function getCurrentCoordinate() public function getCurrentCoordinate()
{ {
@ -209,7 +209,7 @@ class Cells
$column = ''; $column = '';
$row = 0; $row = 0;
sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row); sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
return $column; return $column;
} }
@ -224,7 +224,7 @@ class Cells
$column = ''; $column = '';
$row = 0; $row = 0;
sscanf($this->currentCoordinate, '%[A-Z]%d', $column, $row); sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
return (int) $row; return (int) $row;
} }
@ -232,7 +232,7 @@ class Cells
/** /**
* Get highest worksheet column. * Get highest worksheet column.
* *
* @param string $row Return the highest column for the specified row, * @param null|int|string $row Return the highest column for the specified row,
* or the highest column of any row if no row number is passed * or the highest column of any row if no row number is passed
* *
* @return string Highest column name * @return string Highest column name
@ -257,13 +257,13 @@ class Cells
$columnList[] = Coordinate::columnIndexFromString($c); $columnList[] = Coordinate::columnIndexFromString($c);
} }
return Coordinate::stringFromColumnIndex(max($columnList)); return Coordinate::stringFromColumnIndex((int) @max($columnList));
} }
/** /**
* Get highest worksheet row. * Get highest worksheet row.
* *
* @param string $column Return the highest row for the specified column, * @param null|string $column Return the highest row for the specified column,
* or the highest row of any column if no column letter is passed * or the highest row of any column if no column letter is passed
* *
* @return int Highest row number * @return int Highest row number
@ -304,8 +304,6 @@ class Cells
/** /**
* Clone the cell collection. * Clone the cell collection.
* *
* @param Worksheet $worksheet The new worksheet that we're copying to
*
* @return self * @return self
*/ */
public function cloneCellCollection(Worksheet $worksheet) public function cloneCellCollection(Worksheet $worksheet)
@ -314,7 +312,7 @@ class Cells
$newCollection = clone $this; $newCollection = clone $this;
$newCollection->parent = $worksheet; $newCollection->parent = $worksheet;
if (($newCollection->currentCell !== null) && (is_object($newCollection->currentCell))) { if (is_object($newCollection->currentCell)) {
$newCollection->currentCell->attach($this); $newCollection->currentCell->attach($this);
} }
@ -327,16 +325,14 @@ class Cells
// Change prefix // Change prefix
$newCollection->cachePrefix = $newCollection->getUniqueID(); $newCollection->cachePrefix = $newCollection->getUniqueID();
foreach ($oldValues as $oldKey => $value) { foreach ($oldValues as $oldKey => $value) {
$newValues[str_replace($oldCachePrefix, $newCollection->cachePrefix, $oldKey)] = clone $value; /** @var string */
$newKey = str_replace($oldCachePrefix, $newCollection->cachePrefix, $oldKey);
$newValues[$newKey] = clone $value;
} }
// Store new values // Store new values
$stored = $newCollection->cache->setMultiple($newValues); $stored = $newCollection->cache->setMultiple($newValues);
if (!$stored) { $this->destructIfNeeded($stored, $newCollection, 'Failed to copy cells in cache');
$newCollection->__destruct();
throw new PhpSpreadsheetException('Failed to copy cells in cache');
}
return $newCollection; return $newCollection;
} }
@ -383,15 +379,11 @@ class Cells
*/ */
private function storeCurrentCell(): void private function storeCurrentCell(): void
{ {
if ($this->currentCellIsDirty && !empty($this->currentCoordinate)) { if ($this->currentCellIsDirty && isset($this->currentCoordinate, $this->currentCell)) {
$this->currentCell->detach(); $this->currentCell->detach();
$stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell); $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
if (!$stored) { $this->destructIfNeeded($stored, $this, "Failed to store cell {$this->currentCoordinate} in cache");
$this->__destruct();
throw new PhpSpreadsheetException("Failed to store cell {$this->currentCoordinate} in cache");
}
$this->currentCellIsDirty = false; $this->currentCellIsDirty = false;
} }
@ -399,6 +391,15 @@ class Cells
$this->currentCell = null; $this->currentCell = null;
} }
private function destructIfNeeded(bool $stored, self $cells, string $message): void
{
if (!$stored) {
$cells->__destruct();
throw new PhpSpreadsheetException($message);
}
}
/** /**
* Add or update a cell identified by its coordinate into the collection. * Add or update a cell identified by its coordinate into the collection.
* *

View File

@ -1036,14 +1036,14 @@ class Worksheet implements IComparable
/** /**
* Get highest worksheet column. * Get highest worksheet column.
* *
* @param string $row Return the data highest column for the specified row, * @param null|int|string $row Return the data highest column for the specified row,
* or the highest column of any row if no row number is passed * or the highest column of any row if no row number is passed
* *
* @return string Highest column name * @return string Highest column name
*/ */
public function getHighestColumn($row = null) public function getHighestColumn($row = null)
{ {
if ($row == null) { if (empty($row)) {
return Coordinate::stringFromColumnIndex($this->cachedHighestColumn); return Coordinate::stringFromColumnIndex($this->cachedHighestColumn);
} }
@ -1053,7 +1053,7 @@ class Worksheet implements IComparable
/** /**
* Get highest worksheet column that contains data. * Get highest worksheet column that contains data.
* *
* @param string $row Return the highest data column for the specified row, * @param null|int|string $row Return the highest data column for the specified row,
* or the highest data column of any row if no row number is passed * or the highest data column of any row if no row number is passed
* *
* @return string Highest column name that contains data * @return string Highest column name that contains data
@ -1066,7 +1066,7 @@ class Worksheet implements IComparable
/** /**
* Get highest worksheet row. * Get highest worksheet row.
* *
* @param string $column Return the highest data row for the specified column, * @param null|string $column Return the highest data row for the specified column,
* or the highest row of any column if no column letter is passed * or the highest row of any column if no column letter is passed
* *
* @return int Highest row number * @return int Highest row number
@ -1083,7 +1083,7 @@ class Worksheet implements IComparable
/** /**
* Get highest worksheet row that contains data. * Get highest worksheet row that contains data.
* *
* @param string $column Return the highest data row for the specified column, * @param null|string $column Return the highest data row for the specified column,
* or the highest data row of any column if no column letter is passed * or the highest data row of any column if no column letter is passed
* *
* @return int Highest row number that contains data * @return int Highest row number that contains data

View File

@ -4,12 +4,9 @@ namespace PhpOffice\PhpSpreadsheetTests\Cell;
use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder; use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class AdvancedValueBinderTest extends TestCase class AdvancedValueBinderTest extends TestCase
@ -29,11 +26,18 @@ class AdvancedValueBinderTest extends TestCase
*/ */
private $thousandsSeparator; private $thousandsSeparator;
/**
* @var IValueBinder
*/
private $valueBinder;
protected function setUp(): void protected function setUp(): void
{ {
$this->currencyCode = StringHelper::getCurrencyCode(); $this->currencyCode = StringHelper::getCurrencyCode();
$this->decimalSeparator = StringHelper::getDecimalSeparator(); $this->decimalSeparator = StringHelper::getDecimalSeparator();
$this->thousandsSeparator = StringHelper::getThousandsSeparator(); $this->thousandsSeparator = StringHelper::getThousandsSeparator();
$this->valueBinder = Cell::getValueBinder();
Cell::setValueBinder(new AdvancedValueBinder());
} }
protected function tearDown(): void protected function tearDown(): void
@ -41,23 +45,16 @@ class AdvancedValueBinderTest extends TestCase
StringHelper::setCurrencyCode($this->currencyCode); StringHelper::setCurrencyCode($this->currencyCode);
StringHelper::setDecimalSeparator($this->decimalSeparator); StringHelper::setDecimalSeparator($this->decimalSeparator);
StringHelper::setThousandsSeparator($this->thousandsSeparator); StringHelper::setThousandsSeparator($this->thousandsSeparator);
Cell::setValueBinder($this->valueBinder);
} }
public function testNullValue(): void public function testNullValue(): void
{ {
/** @var Cell&MockObject $cellStub */ $spreadsheet = new Spreadsheet();
$cellStub = $this->getMockBuilder(Cell::class) $sheet = $spreadsheet->getActiveSheet();
->disableOriginalConstructor() $sheet->getCell('A1')->setValue(null);
->getMock(); self::assertNull($sheet->getCell('A1')->getValue());
$spreadsheet->disconnectWorksheets();
// Configure the stub.
$cellStub->expects(self::once())
->method('setValueExplicit')
->with(null, DataType::TYPE_NULL)
->willReturn(true);
$binder = new AdvancedValueBinder();
$binder->bindValue($cellStub, null);
} }
/** /**
@ -65,65 +62,34 @@ class AdvancedValueBinderTest extends TestCase
* *
* @param mixed $value * @param mixed $value
* @param mixed $valueBinded * @param mixed $valueBinded
* @param mixed $format
* @param mixed $thousandsSeparator * @param mixed $thousandsSeparator
* @param mixed $decimalSeparator * @param mixed $decimalSeparator
* @param mixed $currencyCode * @param mixed $currencyCode
*/ */
public function testCurrency($value, $valueBinded, $format, $thousandsSeparator, $decimalSeparator, $currencyCode): void public function testCurrency($value, $valueBinded, $thousandsSeparator, $decimalSeparator, $currencyCode): void
{ {
$sheet = $this->getMockBuilder(Worksheet::class)
->onlyMethods(['getStyle', 'getCellCollection'])
->addMethods(['getNumberFormat', 'setFormatCode'])
->getMock();
$cellCollection = $this->getMockBuilder(Cells::class)
->disableOriginalConstructor()
->getMock();
$cellCollection->expects(self::any())
->method('getParent')
->willReturn($sheet);
$sheet->expects(self::once())
->method('getStyle')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('getNumberFormat')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('setFormatCode')
->with($format)
->willReturnSelf();
$sheet->expects(self::any())
->method('getCellCollection')
->willReturn($cellCollection);
StringHelper::setCurrencyCode($currencyCode); StringHelper::setCurrencyCode($currencyCode);
StringHelper::setDecimalSeparator($decimalSeparator); StringHelper::setDecimalSeparator($decimalSeparator);
StringHelper::setThousandsSeparator($thousandsSeparator); StringHelper::setThousandsSeparator($thousandsSeparator);
$cell = new Cell(null, DataType::TYPE_STRING, $sheet); $spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$binder = new AdvancedValueBinder(); $sheet->getCell('A1')->setValue($value);
$binder->bindValue($cell, $value); self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
self::assertEquals($valueBinded, $cell->getValue()); $spreadsheet->disconnectWorksheets();
} }
public function currencyProvider(): array public function currencyProvider(): array
{ {
$currencyUSD = NumberFormat::FORMAT_CURRENCY_USD_SIMPLE;
$currencyEURO = str_replace('$', '€', NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
return [ return [
['$10.11', 10.11, $currencyUSD, ',', '.', '$'], ['$10.11', 10.11, ',', '.', '$'],
['$1,010.12', 1010.12, $currencyUSD, ',', '.', '$'], ['$1,010.12', 1010.12, ',', '.', '$'],
['$20,20', 20.2, $currencyUSD, '.', ',', '$'], ['$20,20', 20.2, '.', ',', '$'],
['$2.020,20', 2020.2, $currencyUSD, '.', ',', '$'], ['$2.020,20', 2020.2, '.', ',', '$'],
['€2.020,20', 2020.2, $currencyEURO, '.', ',', '€'], ['€2.020,20', 2020.2, '.', ',', '€'],
['€ 2.020,20', 2020.2, $currencyEURO, '.', ',', '€'], ['€ 2.020,20', 2020.2, '.', ',', '€'],
['€2,020.22', 2020.22, $currencyEURO, ',', '.', '€'], ['€2,020.22', 2020.22, ',', '.', '€'],
['$10.11', 10.11, $currencyUSD, ',', '.', '€'], ['$10.11', 10.11, ',', '.', '€'],
]; ];
} }
@ -132,59 +98,30 @@ class AdvancedValueBinderTest extends TestCase
* *
* @param mixed $value * @param mixed $value
* @param mixed $valueBinded * @param mixed $valueBinded
* @param mixed $format
*/ */
public function testFractions($value, $valueBinded, $format): void public function testFractions($value, $valueBinded): void
{ {
$sheet = $this->getMockBuilder(Worksheet::class) $spreadsheet = new Spreadsheet();
->onlyMethods(['getStyle', 'getCellCollection']) $sheet = $spreadsheet->getActiveSheet();
->addMethods(['getNumberFormat', 'setFormatCode']) $sheet->getCell('A1')->setValue($value);
->getMock(); self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
$spreadsheet->disconnectWorksheets();
$cellCollection = $this->getMockBuilder(Cells::class)
->disableOriginalConstructor()
->getMock();
$cellCollection->expects(self::any())
->method('getParent')
->willReturn($sheet);
$sheet->expects(self::once())
->method('getStyle')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('getNumberFormat')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('setFormatCode')
->with($format)
->willReturnSelf();
$sheet->expects(self::any())
->method('getCellCollection')
->willReturn($cellCollection);
$cell = new Cell(null, DataType::TYPE_STRING, $sheet);
$binder = new AdvancedValueBinder();
$binder->bindValue($cell, $value);
self::assertEquals($valueBinded, $cell->getValue());
} }
public function fractionProvider(): array public function fractionProvider(): array
{ {
return [ return [
['1/5', 0.2, '?/?'], ['1/5', 0.2],
['-1/5', -0.2, '?/?'], ['-1/5', -0.2],
['12/5', 2.4, '??/?'], ['12/5', 2.4],
['2/100', 0.02, '?/???'], ['2/100', 0.02],
['15/12', 1.25, '??/??'], ['15/12', 1.25],
['20/100', 0.2, '??/???'], ['20/100', 0.2],
['1 3/5', 1.6, '# ?/?'], ['1 3/5', 1.6],
['-1 3/5', -1.6, '# ?/?'], ['-1 3/5', -1.6],
['1 4/20', 1.2, '# ?/??'], ['1 4/20', 1.2],
['1 16/20', 1.8, '# ??/??'], ['1 16/20', 1.8],
['12 20/100', 12.2, '# ??/???'], ['12 20/100', 12.2],
]; ];
} }
@ -193,51 +130,23 @@ class AdvancedValueBinderTest extends TestCase
* *
* @param mixed $value * @param mixed $value
* @param mixed $valueBinded * @param mixed $valueBinded
* @param mixed $format
*/ */
public function testPercentages($value, $valueBinded, $format): void public function testPercentages($value, $valueBinded): void
{ {
$sheet = $this->getMockBuilder(Worksheet::class) $spreadsheet = new Spreadsheet();
->onlyMethods(['getStyle', 'getCellCollection']) $sheet = $spreadsheet->getActiveSheet();
->addMethods(['getNumberFormat', 'setFormatCode']) $sheet->getCell('A1')->setValue($value);
->getMock(); self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
$cellCollection = $this->getMockBuilder(Cells::class) $spreadsheet->disconnectWorksheets();
->disableOriginalConstructor()
->getMock();
$cellCollection->expects(self::any())
->method('getParent')
->willReturn($sheet);
$sheet->expects(self::once())
->method('getStyle')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('getNumberFormat')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('setFormatCode')
->with($format)
->willReturnSelf();
$sheet->expects(self::any())
->method('getCellCollection')
->willReturn($cellCollection);
$cell = new Cell(null, DataType::TYPE_STRING, $sheet);
$binder = new AdvancedValueBinder();
$binder->bindValue($cell, $value);
self::assertEquals($valueBinded, $cell->getValue());
} }
public function percentageProvider(): array public function percentageProvider(): array
{ {
return [ return [
['10%', 0.1, NumberFormat::FORMAT_PERCENTAGE_00], ['10%', 0.1],
['-12%', -0.12, NumberFormat::FORMAT_PERCENTAGE_00], ['-12%', -0.12],
['120%', 1.2, NumberFormat::FORMAT_PERCENTAGE_00], ['120%', 1.2],
['12.5%', 0.125, NumberFormat::FORMAT_PERCENTAGE_00], ['12.5%', 0.125],
]; ];
} }
@ -246,92 +155,37 @@ class AdvancedValueBinderTest extends TestCase
* *
* @param mixed $value * @param mixed $value
* @param mixed $valueBinded * @param mixed $valueBinded
* @param mixed $format
*/ */
public function testTimes($value, $valueBinded, $format): void public function testTimes($value, $valueBinded): void
{ {
$sheet = $this->getMockBuilder(Worksheet::class) $spreadsheet = new Spreadsheet();
->onlyMethods(['getStyle', 'getCellCollection']) $sheet = $spreadsheet->getActiveSheet();
->addMethods(['getNumberFormat', 'setFormatCode']) $sheet->getCell('A1')->setValue($value);
->getMock(); self::assertEquals($valueBinded, $sheet->getCell('A1')->getValue());
$spreadsheet->disconnectWorksheets();
$cellCollection = $this->getMockBuilder(Cells::class)
->disableOriginalConstructor()
->getMock();
$cellCollection->expects(self::any())
->method('getParent')
->willReturn($sheet);
$sheet->expects(self::once())
->method('getStyle')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('getNumberFormat')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects(self::once())
->method('setFormatCode')
->with($format)
->willReturnSelf();
$sheet->expects(self::any())
->method('getCellCollection')
->willReturn($cellCollection);
$cell = new Cell(null, DataType::TYPE_STRING, $sheet);
$binder = new AdvancedValueBinder();
$binder->bindValue($cell, $value);
self::assertEquals($valueBinded, $cell->getValue());
} }
public function timeProvider(): array public function timeProvider(): array
{ {
return [ return [
['1:20', 0.05555555556, NumberFormat::FORMAT_DATE_TIME3], ['1:20', 0.05555555556],
['09:17', 0.386805555556, NumberFormat::FORMAT_DATE_TIME3], ['09:17', 0.386805555556],
['15:00', 0.625, NumberFormat::FORMAT_DATE_TIME3], ['15:00', 0.625],
['17:12:35', 0.71707175926, NumberFormat::FORMAT_DATE_TIME4], ['17:12:35', 0.71707175926],
['23:58:20', 0.99884259259, NumberFormat::FORMAT_DATE_TIME4], ['23:58:20', 0.99884259259],
]; ];
} }
/** /**
* @dataProvider stringProvider * @dataProvider stringProvider
*/ */
public function testStringWrapping(string $value, bool $wrapped): void public function testStringWrapping(string $value): void
{ {
$sheet = $this->getMockBuilder(Worksheet::class) $spreadsheet = new Spreadsheet();
->onlyMethods(['getStyle', 'getCellCollection']) $sheet = $spreadsheet->getActiveSheet();
->addMethods(['getAlignment', 'setWrapText']) $sheet->getCell('A1')->setValue($value);
->getMock(); self::assertEquals($value, $sheet->getCell('A1')->getValue());
$cellCollection = $this->getMockBuilder(Cells::class) $spreadsheet->disconnectWorksheets();
->disableOriginalConstructor()
->getMock();
$cellCollection->expects(self::any())
->method('getParent')
->willReturn($sheet);
$sheet->expects($wrapped ? self::once() : self::never())
->method('getStyle')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects($wrapped ? self::once() : self::never())
->method('getAlignment')
->willReturnSelf();
// @phpstan-ignore-next-line
$sheet->expects($wrapped ? self::once() : self::never())
->method('setWrapText')
->with($wrapped)
->willReturnSelf();
$sheet->expects(self::any())
->method('getCellCollection')
->willReturn($cellCollection);
$cell = new Cell(null, DataType::TYPE_STRING, $sheet);
$binder = new AdvancedValueBinder();
$binder->bindValue($cell, $value);
} }
public function stringProvider(): array public function stringProvider(): array

View File

@ -21,6 +21,7 @@ class CellTest extends TestCase
$cell->setValueExplicit($value, $dataType); $cell->setValueExplicit($value, $dataType);
self::assertSame($expected, $cell->getValue()); self::assertSame($expected, $cell->getValue());
$spreadsheet->disconnectWorksheets();
} }
public function providerSetValueExplicit(): array public function providerSetValueExplicit(): array
@ -64,5 +65,42 @@ class CellTest extends TestCase
$value = $spreadsheet->getActiveSheet()->getCell($cell)->getCalculatedValue(); $value = $spreadsheet->getActiveSheet()->getCell($cell)->getCalculatedValue();
self::assertEquals(0, $spreadsheet->getActiveSheetIndex()); self::assertEquals(0, $spreadsheet->getActiveSheetIndex());
self::assertEquals(247, $value); self::assertEquals(247, $value);
$spreadsheet->disconnectWorksheets();
}
public function testDestroyWorksheet(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
self::assertSame($sheet, $cell->getWorksheet());
$this->expectException(Exception::class);
$this->expectExceptionMessage('Worksheet no longer exists');
$spreadsheet->disconnectWorksheets();
$cell->getWorksheet();
}
public function testDestroyCell1(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
self::assertSame('A1', $cell->getCoordinate());
$this->expectException(Exception::class);
$this->expectExceptionMessage('Coordinate no longer exists');
$spreadsheet->disconnectWorksheets();
$cell->getCoordinate();
}
public function testDestroyCell2(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
self::assertSame('A1', $cell->getCoordinate());
$this->expectException(Exception::class);
$this->expectExceptionMessage('Coordinate no longer exists');
$cell->getParent()->delete('A1');
$cell->getCoordinate();
} }
} }

View File

@ -4,31 +4,14 @@ namespace PhpOffice\PhpSpreadsheetTests\Cell;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder; use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PHPUnit\Framework\MockObject\MockObject; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class DefaultValueBinderTest extends TestCase class DefaultValueBinderTest extends TestCase
{ {
private function createCellStub(): Cell
{
// Create a stub for the Cell class.
/** @var Cell&MockObject $cellStub */
$cellStub = $this->getMockBuilder(Cell::class)
->disableOriginalConstructor()
->getMock();
// Configure the stub.
$cellStub->expects(self::any())
->method('setValueExplicit')
->willReturn(true);
return $cellStub;
}
/** /**
* @dataProvider binderProvider * @dataProvider binderProvider
* *
@ -36,10 +19,13 @@ class DefaultValueBinderTest extends TestCase
*/ */
public function testBindValue($value): void public function testBindValue($value): void
{ {
$cellStub = $this->createCellStub(); $spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$binder = new DefaultValueBinder(); $binder = new DefaultValueBinder();
$result = $binder->bindValue($cellStub, $value); $result = $binder->bindValue($cell, $value);
self::assertTrue($result); self::assertTrue($result);
$spreadsheet->disconnectWorksheets();
} }
public function binderProvider(): array public function binderProvider(): array
@ -91,11 +77,14 @@ class DefaultValueBinderTest extends TestCase
public function testCanOverrideStaticMethodWithoutOverridingBindValue(): void public function testCanOverrideStaticMethodWithoutOverridingBindValue(): void
{ {
$cellStub = $this->createCellStub(); $spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$binder = new ValueBinderWithOverriddenDataTypeForValue(); $binder = new ValueBinderWithOverriddenDataTypeForValue();
self::assertFalse($binder::$called); self::assertFalse($binder::$called);
$binder->bindValue($cellStub, 123); $binder->bindValue($cell, 123);
self::assertTrue($binder::$called); self::assertTrue($binder::$called);
$spreadsheet->disconnectWorksheets();
} }
} }

View File

@ -6,41 +6,27 @@ use DateTime;
use DateTimeZone; use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder; use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class StringValueBinderTest extends TestCase class StringValueBinderTest extends TestCase
{ {
/** /**
* @param mixed $expectedValue * @var IValueBinder
*
* @return Cell&MockObject
*/ */
protected function createCellStub($expectedValue, string $expectedDataType, bool $quotePrefix = false): MockObject private $valueBinder;
protected function setUp(): void
{ {
/** @var Style&MockObject $styleStub */ $this->valueBinder = Cell::getValueBinder();
$styleStub = $this->getMockBuilder(Style::class) }
->disableOriginalConstructor()
->getMock();
/** @var Cell&MockObject $cellStub */ protected function tearDown(): void
$cellStub = $this->getMockBuilder(Cell::class) {
->disableOriginalConstructor() Cell::setValueBinder($this->valueBinder);
->getMock();
// Configure the stub.
$cellStub->expects(self::once())
->method('setValueExplicit')
->with($expectedValue, $expectedDataType)
->willReturn(true);
$cellStub->expects($quotePrefix ? self::once() : self::never())
->method('getStyle')
->willReturn($styleStub);
return $cellStub;
} }
/** /**
@ -52,13 +38,16 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderDefaultBehaviour( public function testStringValueBinderDefaultBehaviour(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix); Cell::setValueBinder(new StringValueBinder());
$spreadsheet = new Spreadsheet();
$binder = new StringValueBinder(); $sheet = $spreadsheet->getActiveSheet();
$binder->bindValue($cellStub, $value); $cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesDefault(): array public function providerDataValuesDefault(): array
@ -76,7 +65,7 @@ class StringValueBinderTest extends TestCase
['-.123', '-.123', DataType::TYPE_STRING], ['-.123', '-.123', DataType::TYPE_STRING],
['1.23e-4', '1.23e-4', DataType::TYPE_STRING], ['1.23e-4', '1.23e-4', DataType::TYPE_STRING],
['ABC', 'ABC', DataType::TYPE_STRING], ['ABC', 'ABC', DataType::TYPE_STRING],
['=SUM(A1:C3)', '=SUM(A1:C3)', DataType::TYPE_STRING, true], ['=SUM(A1:C3)', '=SUM(A1:C3)', DataType::TYPE_STRING],
[123, '123', DataType::TYPE_STRING], [123, '123', DataType::TYPE_STRING],
[123.456, '123.456', DataType::TYPE_STRING], [123.456, '123.456', DataType::TYPE_STRING],
[0.123, '0.123', DataType::TYPE_STRING], [0.123, '0.123', DataType::TYPE_STRING],
@ -98,20 +87,26 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderSuppressNullConversion( public function testStringValueBinderSuppressNullConversion(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setNullConversion(false); $binder->setNullConversion(false);
$binder->bindValue($cellStub, $value); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesSuppressNullConversion(): array public function providerDataValuesSuppressNullConversion(): array
{ {
return [ return [
[null, null, DataType::TYPE_NULL], [null, null, DataType::TYPE_NULL],
[true, '1', DataType::TYPE_STRING],
[123, '123', DataType::TYPE_STRING],
]; ];
} }
@ -124,14 +119,18 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderSuppressBooleanConversion( public function testStringValueBinderSuppressBooleanConversion(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setBooleanConversion(false); $binder->setBooleanConversion(false);
$binder->bindValue($cellStub, $value); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesSuppressBooleanConversion(): array public function providerDataValuesSuppressBooleanConversion(): array
@ -139,6 +138,8 @@ class StringValueBinderTest extends TestCase
return [ return [
[true, true, DataType::TYPE_BOOL], [true, true, DataType::TYPE_BOOL],
[false, false, DataType::TYPE_BOOL], [false, false, DataType::TYPE_BOOL],
[null, '', DataType::TYPE_STRING],
[123, '123', DataType::TYPE_STRING],
]; ];
} }
@ -151,14 +152,18 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderSuppressNumericConversion( public function testStringValueBinderSuppressNumericConversion(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setNumericConversion(false); $binder->setNumericConversion(false);
$binder->bindValue($cellStub, $value); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesSuppressNumericConversion(): array public function providerDataValuesSuppressNumericConversion(): array
@ -172,6 +177,9 @@ class StringValueBinderTest extends TestCase
[-.123, -0.123, DataType::TYPE_NUMERIC], [-.123, -0.123, DataType::TYPE_NUMERIC],
[1.23e-4, 0.000123, DataType::TYPE_NUMERIC], [1.23e-4, 0.000123, DataType::TYPE_NUMERIC],
[1.23e-24, 1.23E-24, DataType::TYPE_NUMERIC], [1.23e-24, 1.23E-24, DataType::TYPE_NUMERIC],
[true, '1', DataType::TYPE_STRING],
[false, '', DataType::TYPE_STRING],
[null, '', DataType::TYPE_STRING],
]; ];
} }
@ -184,14 +192,18 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderSuppressFormulaConversion( public function testStringValueBinderSuppressFormulaConversion(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setFormulaConversion(false); $binder->setFormulaConversion(false);
$binder->bindValue($cellStub, $value); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesSuppressFormulaConversion(): array public function providerDataValuesSuppressFormulaConversion(): array
@ -210,14 +222,18 @@ class StringValueBinderTest extends TestCase
public function testStringValueBinderSuppressAllConversion( public function testStringValueBinderSuppressAllConversion(
$value, $value,
$expectedValue, $expectedValue,
string $expectedDataType, string $expectedDataType
bool $quotePrefix = false
): void { ): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setConversionForAllValueTypes(false); $binder->setConversionForAllValueTypes(false);
$binder->bindValue($cellStub, $value); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($value);
self::assertSame($expectedValue, $cell->getValue());
self::assertSame($expectedDataType, $cell->getDataType());
$spreadsheet->disconnectWorksheets();
} }
public function providerDataValuesSuppressAllConversion(): array public function providerDataValuesSuppressAllConversion(): array
@ -252,10 +268,15 @@ class StringValueBinderTest extends TestCase
$objRichText = new RichText(); $objRichText = new RichText();
$objRichText->createText('Hello World'); $objRichText->createText('Hello World');
$cellStub = $this->createCellStub($objRichText, DataType::TYPE_INLINE);
$binder = new StringValueBinder(); $binder = new StringValueBinder();
$binder->setConversionForAllValueTypes(false); $binder->setConversionForAllValueTypes(false);
$binder->bindValue($cellStub, $objRichText); Cell::setValueBinder($binder);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cell = $sheet->getCell('A1');
$cell->setValue($objRichText);
self::assertSame('inlineStr', $cell->getDataType());
self::assertSame('Hello World', $cell->getCalculatedValue());
$spreadsheet->disconnectWorksheets();
} }
} }