Skip to content

Commit 0152f57

Browse files
committed
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.
1 parent f7c183b commit 0152f57

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

docs/topics/accessing-cells.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ By default, the StringValueBinder will cast any datatype passed to it into a str
551551
// Set value binder
552552
$stringValueBinder = new \PhpOffice\PhpSpreadsheet\Cell\StringValueBinder();
553553
$stringValueBinder->setNumericConversion(false)
554+
->setSetIgnoredErrors(true) // suppresses "number stored as text" indicators
554555
->setBooleanConversion(false)
555556
->setNullConversion(false)
556557
->setFormulaConversion(false);

src/PhpSpreadsheet/Cell/StringValueBinder.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class StringValueBinder extends DefaultValueBinder implements IValueBinder
1818

1919
protected bool $convertFormula = true;
2020

21+
protected bool $setIgnoredErrors = false;
22+
23+
public function setSetIgnoredErrors(bool $setIgnoredErrors = false): self
24+
{
25+
$this->setIgnoredErrors = $setIgnoredErrors;
26+
27+
return $this;
28+
}
29+
2130
public function setNullConversion(bool $suppressConversion = false): self
2231
{
2332
$this->convertNull = $suppressConversion;
@@ -90,6 +99,9 @@ public function bindValue(Cell $cell, mixed $value): bool
9099
} elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false && parent::dataTypeForValue($value) === DataType::TYPE_FORMULA) {
91100
$cell->setValueExplicit($value, DataType::TYPE_FORMULA);
92101
} else {
102+
if ($this->setIgnoredErrors && is_numeric($value)) {
103+
$cell->getIgnoredErrors()->setNumberStoredAsText(true);
104+
}
93105
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
94106
}
95107

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Cell;
6+
7+
use PhpOffice\PhpSpreadsheet\Cell\Cell;
8+
use PhpOffice\PhpSpreadsheet\Cell\DataType;
9+
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
10+
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
11+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class StringValueBinder2Test extends TestCase
15+
{
16+
private IValueBinder $valueBinder;
17+
18+
protected function setUp(): void
19+
{
20+
$this->valueBinder = Cell::getValueBinder();
21+
}
22+
23+
protected function tearDown(): void
24+
{
25+
Cell::setValueBinder($this->valueBinder);
26+
}
27+
28+
public function testStringValueBinderIgnoredErrorsDefault(): void
29+
{
30+
$valueBinder = new StringValueBinder();
31+
Cell::setValueBinder($valueBinder);
32+
$spreadsheet = new Spreadsheet();
33+
$sheet = $spreadsheet->getActiveSheet();
34+
$sheet->fromArray([
35+
[1, 'x', 3.2],
36+
['y', -5, 'z'],
37+
]);
38+
$ignoredCells = [];
39+
foreach ($sheet->getRowIterator() as $row) {
40+
foreach ($row->getCellIterator() as $cell) {
41+
$coordinate = $cell->getCoordinate();
42+
self::assertSame(DataType::TYPE_STRING, $cell->getDataType(), "not string for cell $coordinate");
43+
if ($cell->getIgnoredErrors()->getNumberStoredAsText()) {
44+
$ignoredCells[] = $coordinate;
45+
}
46+
}
47+
}
48+
self::assertSame([], $ignoredCells);
49+
$spreadsheet->disconnectWorksheets();
50+
}
51+
52+
public function testStringValueBinderIgnoredErrorsTrue(): void
53+
{
54+
$valueBinder = new StringValueBinder();
55+
$valueBinder->setSetIgnoredErrors(true);
56+
Cell::setValueBinder($valueBinder);
57+
$spreadsheet = new Spreadsheet();
58+
$sheet = $spreadsheet->getActiveSheet();
59+
$sheet->fromArray([
60+
[1, 'x', 3.2],
61+
['y', -5, 'z'],
62+
]);
63+
$ignoredCells = [];
64+
foreach ($sheet->getRowIterator() as $row) {
65+
foreach ($row->getCellIterator() as $cell) {
66+
$coordinate = $cell->getCoordinate();
67+
self::assertSame(DataType::TYPE_STRING, $cell->getDataType(), "not string for cell $coordinate");
68+
if ($cell->getIgnoredErrors()->getNumberStoredAsText()) {
69+
$ignoredCells[] = $coordinate;
70+
}
71+
}
72+
}
73+
self::assertSame(['A1', 'C1', 'B2'], $ignoredCells);
74+
$spreadsheet->disconnectWorksheets();
75+
}
76+
77+
public function testStringValueBinderPreserveNumeric(): void
78+
{
79+
$valueBinder = new StringValueBinder();
80+
$valueBinder->setNumericConversion(false);
81+
$valueBinder->setSetIgnoredErrors(true);
82+
Cell::setValueBinder($valueBinder);
83+
$spreadsheet = new Spreadsheet();
84+
$sheet = $spreadsheet->getActiveSheet();
85+
$sheet->fromArray([
86+
[1, 'x', 3.2],
87+
['y', -5, 'z'],
88+
]);
89+
$ignoredCells = [];
90+
foreach ($sheet->getRowIterator() as $row) {
91+
foreach ($row->getCellIterator() as $cell) {
92+
$coordinate = $cell->getCoordinate();
93+
$expected = is_numeric($cell->getValue()) ? DataType::TYPE_NUMERIC : DataType::TYPE_STRING;
94+
self::assertSame($expected, $cell->getDataType(), "wrong type for cell $coordinate");
95+
if ($cell->getIgnoredErrors()->getNumberStoredAsText()) {
96+
$ignoredCells[] = $coordinate;
97+
}
98+
}
99+
}
100+
self::assertSame([], $ignoredCells);
101+
$spreadsheet->disconnectWorksheets();
102+
}
103+
}

0 commit comments

Comments
 (0)