From 0152f57e4487d2555163a52ff043c398e136ca47 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:36:45 -0700 Subject: [PATCH] String Value Binder Allow Setting "Ignore Number Stored As Text" When String Value Binder converts a numeric value to text, the resulting spreadsheet will be full of little green triangles to indicate to the end user that something might be wrong. It is unlikely that a spreadsheet created in this manner needs that visual clutter. This PR adds a property and setter (I can't really think of a good use case for a getter) to suppress it. Suppression should arguably be the default, but, for now, I will avoid any BC problems by leaving non-suppression as the default. --- docs/topics/accessing-cells.md | 1 + src/PhpSpreadsheet/Cell/StringValueBinder.php | 12 ++ .../Cell/StringValueBinder2Test.php | 103 ++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Cell/StringValueBinder2Test.php diff --git a/docs/topics/accessing-cells.md b/docs/topics/accessing-cells.md index 67e177536..d377da899 100644 --- a/docs/topics/accessing-cells.md +++ b/docs/topics/accessing-cells.md @@ -551,6 +551,7 @@ By default, the StringValueBinder will cast any datatype passed to it into a str // Set value binder $stringValueBinder = new \PhpOffice\PhpSpreadsheet\Cell\StringValueBinder(); $stringValueBinder->setNumericConversion(false) + ->setSetIgnoredErrors(true) // suppresses "number stored as text" indicators ->setBooleanConversion(false) ->setNullConversion(false) ->setFormulaConversion(false); diff --git a/src/PhpSpreadsheet/Cell/StringValueBinder.php b/src/PhpSpreadsheet/Cell/StringValueBinder.php index d86cdabd3..00a15cd4c 100644 --- a/src/PhpSpreadsheet/Cell/StringValueBinder.php +++ b/src/PhpSpreadsheet/Cell/StringValueBinder.php @@ -18,6 +18,15 @@ class StringValueBinder extends DefaultValueBinder implements IValueBinder protected bool $convertFormula = true; + protected bool $setIgnoredErrors = false; + + public function setSetIgnoredErrors(bool $setIgnoredErrors = false): self + { + $this->setIgnoredErrors = $setIgnoredErrors; + + return $this; + } + public function setNullConversion(bool $suppressConversion = false): self { $this->convertNull = $suppressConversion; @@ -90,6 +99,9 @@ class StringValueBinder extends DefaultValueBinder implements IValueBinder } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false && parent::dataTypeForValue($value) === DataType::TYPE_FORMULA) { $cell->setValueExplicit($value, DataType::TYPE_FORMULA); } else { + if ($this->setIgnoredErrors && is_numeric($value)) { + $cell->getIgnoredErrors()->setNumberStoredAsText(true); + } $cell->setValueExplicit((string) $value, DataType::TYPE_STRING); } diff --git a/tests/PhpSpreadsheetTests/Cell/StringValueBinder2Test.php b/tests/PhpSpreadsheetTests/Cell/StringValueBinder2Test.php new file mode 100644 index 000000000..735d38801 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Cell/StringValueBinder2Test.php @@ -0,0 +1,103 @@ +valueBinder = Cell::getValueBinder(); + } + + protected function tearDown(): void + { + Cell::setValueBinder($this->valueBinder); + } + + public function testStringValueBinderIgnoredErrorsDefault(): void + { + $valueBinder = new StringValueBinder(); + Cell::setValueBinder($valueBinder); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + [1, 'x', 3.2], + ['y', -5, 'z'], + ]); + $ignoredCells = []; + foreach ($sheet->getRowIterator() as $row) { + foreach ($row->getCellIterator() as $cell) { + $coordinate = $cell->getCoordinate(); + self::assertSame(DataType::TYPE_STRING, $cell->getDataType(), "not string for cell $coordinate"); + if ($cell->getIgnoredErrors()->getNumberStoredAsText()) { + $ignoredCells[] = $coordinate; + } + } + } + self::assertSame([], $ignoredCells); + $spreadsheet->disconnectWorksheets(); + } + + public function testStringValueBinderIgnoredErrorsTrue(): void + { + $valueBinder = new StringValueBinder(); + $valueBinder->setSetIgnoredErrors(true); + Cell::setValueBinder($valueBinder); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + [1, 'x', 3.2], + ['y', -5, 'z'], + ]); + $ignoredCells = []; + foreach ($sheet->getRowIterator() as $row) { + foreach ($row->getCellIterator() as $cell) { + $coordinate = $cell->getCoordinate(); + self::assertSame(DataType::TYPE_STRING, $cell->getDataType(), "not string for cell $coordinate"); + if ($cell->getIgnoredErrors()->getNumberStoredAsText()) { + $ignoredCells[] = $coordinate; + } + } + } + self::assertSame(['A1', 'C1', 'B2'], $ignoredCells); + $spreadsheet->disconnectWorksheets(); + } + + public function testStringValueBinderPreserveNumeric(): void + { + $valueBinder = new StringValueBinder(); + $valueBinder->setNumericConversion(false); + $valueBinder->setSetIgnoredErrors(true); + Cell::setValueBinder($valueBinder); + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + [1, 'x', 3.2], + ['y', -5, 'z'], + ]); + $ignoredCells = []; + foreach ($sheet->getRowIterator() as $row) { + foreach ($row->getCellIterator() as $cell) { + $coordinate = $cell->getCoordinate(); + $expected = is_numeric($cell->getValue()) ? DataType::TYPE_NUMERIC : DataType::TYPE_STRING; + self::assertSame($expected, $cell->getDataType(), "wrong type for cell $coordinate"); + if ($cell->getIgnoredErrors()->getNumberStoredAsText()) { + $ignoredCells[] = $coordinate; + } + } + } + self::assertSame([], $ignoredCells); + $spreadsheet->disconnectWorksheets(); + } +}