Allow more control over what non-string datatypes are converted to strings in the StringValueBinder

This commit is contained in:
MarkBaker 2021-06-03 19:11:31 +02:00 committed by Mark Baker
parent 642fc7dee7
commit 8e41445fbd
2 changed files with 301 additions and 2 deletions

View File

@ -2,10 +2,58 @@
namespace PhpOffice\PhpSpreadsheet\Cell;
use DateTimeInterface;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class StringValueBinder implements IValueBinder
{
protected $convertNull = true;
protected $convertBoolean = true;
protected $convertNumeric = true;
protected $convertFormula = true;
public function setNullConversion(bool $suppressConversion = false): self
{
$this->convertNull = $suppressConversion;
return $this;
}
public function setBooleanConversion(bool $suppressConversion = false): self
{
$this->convertBoolean = $suppressConversion;
return $this;
}
public function setNumericConversion(bool $suppressConversion = false): self
{
$this->convertNumeric = $suppressConversion;
return $this;
}
public function setFormulaConversion(bool $suppressConversion = false): self
{
$this->convertFormula = $suppressConversion;
return $this;
}
public function setConversionForAllValueTypes(bool $suppressConversion = false): self
{
$this->convertNull = $suppressConversion;
$this->convertBoolean = $suppressConversion;
$this->convertNumeric = $suppressConversion;
$this->convertFormula = $suppressConversion;
return $this;
}
/**
* Bind value to a cell.
*
@ -21,9 +69,35 @@ class StringValueBinder implements IValueBinder
$value = StringHelper::sanitizeUTF8($value);
}
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
if (is_object($value)) {
// Handle any objects that might be injected
if ($value instanceof DateTimeInterface) {
$value = $value->format('Y-m-d H:i:s');
} elseif ($value instanceof RichText) {
$cell->setValueExplicit((string) $value, DataType::TYPE_INLINE);
return true;
} else {
// Attempt to cast any unexpected objects to string
$value = (string) $value;
}
}
if ($value === null && $this->convertNull === false) {
$cell->setValueExplicit($value, DataType::TYPE_NULL);
} elseif (is_bool($value) && $this->convertBoolean === false) {
$cell->setValueExplicit($value, DataType::TYPE_BOOL);
} elseif ((is_int($value) || is_float($value)) && $this->convertNumeric === false) {
$cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
} elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false) {
$cell->setValueExplicit($value, DataType::TYPE_FORMULA);
} else {
if (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
$cell->getStyle()->setQuotePrefix(true);
}
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
}
// Done!
return true;
}
}

View File

@ -0,0 +1,225 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Cell;
use DateTime;
use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class StringValueBinderTest extends TestCase
{
protected function createCellStub($expectedValue, string $expectedDataType, bool $quotePrefix = false): Cell
{
/** @var Style&MockObject $styleStub */
$styleStub = $this->getMockBuilder(Style::class)
->disableOriginalConstructor()
->getMock();
/** @var Cell&MockObject $cellStub */
$cellStub = $this->getMockBuilder(Cell::class)
->disableOriginalConstructor()
->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;
}
/**
* @dataProvider providerDataValuesDefault
*/
public function testStringValueBinderDefaultBehaviour(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesDefault(): array
{
return [
[null, '', DataType::TYPE_STRING],
[true, '1', DataType::TYPE_STRING],
[false, '', DataType::TYPE_STRING],
['', '', DataType::TYPE_STRING],
['123', '123', DataType::TYPE_STRING],
['123.456', '123.456', DataType::TYPE_STRING],
['0.123', '0.123', DataType::TYPE_STRING],
['.123', '.123', DataType::TYPE_STRING],
['-0.123', '-0.123', DataType::TYPE_STRING],
['-.123', '-.123', DataType::TYPE_STRING],
['1.23e-4', '1.23e-4', DataType::TYPE_STRING],
['ABC', 'ABC', DataType::TYPE_STRING],
['=SUM(A1:C3)', '=SUM(A1:C3)', DataType::TYPE_STRING, true],
[123, '123', DataType::TYPE_STRING],
[123.456, '123.456', DataType::TYPE_STRING],
[0.123, '0.123', DataType::TYPE_STRING],
[.123, '0.123', DataType::TYPE_STRING],
[-0.123, '-0.123', DataType::TYPE_STRING],
[-.123, '-0.123', DataType::TYPE_STRING],
[1.23e-4, '0.000123', DataType::TYPE_STRING],
[1.23e-24, '1.23E-24', DataType::TYPE_STRING],
[new DateTime('2021-06-01 00:00:00', new DateTimeZone('UTC')), '2021-06-01 00:00:00', DataType::TYPE_STRING],
];
}
/**
* @dataProvider providerDataValuesSuppressNullConversion
*/
public function testStringValueBinderSuppressNullConversion(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->setNullConversion(false);
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesSuppressNullConversion(): array
{
return [
[null, null, DataType::TYPE_NULL],
];
}
/**
* @dataProvider providerDataValuesSuppressBooleanConversion
*/
public function testStringValueBinderSuppressBooleanConversion(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->setBooleanConversion(false);
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesSuppressBooleanConversion(): array
{
return [
[true, true, DataType::TYPE_BOOL],
[false, false, DataType::TYPE_BOOL],
];
}
/**
* @dataProvider providerDataValuesSuppressNumericConversion
*/
public function testStringValueBinderSuppressNumericConversion(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->setNumericConversion(false);
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesSuppressNumericConversion(): array
{
return [
[123, 123, DataType::TYPE_NUMERIC],
[123.456, 123.456, DataType::TYPE_NUMERIC],
[0.123, 0.123, DataType::TYPE_NUMERIC],
[.123, 0.123, DataType::TYPE_NUMERIC],
[-0.123, -0.123, DataType::TYPE_NUMERIC],
[-.123, -0.123, DataType::TYPE_NUMERIC],
[1.23e-4, 0.000123, DataType::TYPE_NUMERIC],
[1.23e-24, 1.23E-24, DataType::TYPE_NUMERIC],
];
}
/**
* @dataProvider providerDataValuesSuppressFormulaConversion
*/
public function testStringValueBinderSuppressFormulaConversion(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->setFormulaConversion(false);
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesSuppressFormulaConversion(): array
{
return [
['=SUM(A1:C3)', '=SUM(A1:C3)', DataType::TYPE_FORMULA, false],
];
}
/**
* @dataProvider providerDataValuesSuppressAllConversion
*/
public function testStringValueBinderSuppressAllConversion(
$value,
$expectedValue,
string $expectedDataType,
bool $quotePrefix = false
): void {
$cellStub = $this->createCellStub($expectedValue, $expectedDataType, $quotePrefix);
$binder = new StringValueBinder();
$binder->setConversionForAllValueTypes(false);
$binder->bindValue($cellStub, $value);
}
public function providerDataValuesSuppressAllConversion(): array
{
return [
[null, null, DataType::TYPE_NULL],
[true, true, DataType::TYPE_BOOL],
[false, false, DataType::TYPE_BOOL],
['', '', DataType::TYPE_STRING],
['123', '123', DataType::TYPE_STRING],
['123.456', '123.456', DataType::TYPE_STRING],
['0.123', '0.123', DataType::TYPE_STRING],
['.123', '.123', DataType::TYPE_STRING],
['-0.123', '-0.123', DataType::TYPE_STRING],
['-.123', '-.123', DataType::TYPE_STRING],
['1.23e-4', '1.23e-4', DataType::TYPE_STRING],
['ABC', 'ABC', DataType::TYPE_STRING],
['=SUM(A1:C3)', '=SUM(A1:C3)', DataType::TYPE_FORMULA, false],
[123, 123, DataType::TYPE_NUMERIC],
[123.456, 123.456, DataType::TYPE_NUMERIC],
[0.123, 0.123, DataType::TYPE_NUMERIC],
[.123, 0.123, DataType::TYPE_NUMERIC],
[-0.123, -0.123, DataType::TYPE_NUMERIC],
[-.123, -0.123, DataType::TYPE_NUMERIC],
[1.23e-4, 0.000123, DataType::TYPE_NUMERIC],
[1.23e-24, 1.23E-24, DataType::TYPE_NUMERIC],
];
}
}