Option to return formatted dates instead of PHP objects (#226)
When reading spreadsheets, Spout should be able to return formatted dates, as shown when opened with Excel for instance. It currently only returns DateTime/DateInterval objects, making it impossible to read + write, as the Writer does not accept objects.
This commit is contained in:
1 parent
a6a6b158de
commit
104cd9b811
21 files changed
+389
-51
No files matched your search
@@ -34,14 +34,19 @@ class CellValueFormatter
|
||||
const XML_ATTRIBUTE_CURRENCY = 'office:currency';
|
||||
const XML_ATTRIBUTE_C = 'text:c';
|
||||
|
||||
/** @var bool Whether date/time values should be returned as PHP objects or be formatted as strings */
|
||||
protected $shouldFormatDates;
|
||||
|
||||
/** @var \Box\Spout\Common\Escaper\ODS Used to unescape XML data */
|
||||
protected $escaper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct($shouldFormatDates)
|
||||
{
|
||||
$this->shouldFormatDates = $shouldFormatDates;
|
||||
|
||||
/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
|
||||
$this->escaper = new \Box\Spout\Common\Escaper\ODS();
|
||||
}
|
||||
@@ -122,6 +127,7 @@ class CellValueFormatter
|
||||
{
|
||||
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
|
||||
$nodeIntValue = intval($nodeValue);
|
||||
// The "==" is intentionally not a "===" because only the value matters, not the type
|
||||
$cellValue = ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
|
||||
return $cellValue;
|
||||
}
|
||||
@@ -144,15 +150,27 @@ class CellValueFormatter
|
||||
* Returns the cell Date value from the given node.
|
||||
*
|
||||
* @param \DOMNode $node
|
||||
* @return \DateTime|null The value associated with the cell or NULL if invalid date value
|
||||
* @return \DateTime|string|null The value associated with the cell or NULL if invalid date value
|
||||
*/
|
||||
protected function formatDateCellValue($node)
|
||||
{
|
||||
try {
|
||||
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE);
|
||||
return new \DateTime($nodeValue);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
// The XML node looks like this:
|
||||
// <table:table-cell calcext:value-type="date" office:date-value="2016-05-19T16:39:00" office:value-type="date">
|
||||
// <text:p>05/19/16 04:39 PM</text:p>
|
||||
// </table:table-cell>
|
||||
|
||||
if ($this->shouldFormatDates) {
|
||||
// The date is already formatted in the "p" tag
|
||||
$nodeWithValueAlreadyFormatted = $node->getElementsByTagName(self::XML_NODE_P)->item(0);
|
||||
return $nodeWithValueAlreadyFormatted->nodeValue;
|
||||
} else {
|
||||
// otherwise, get it from the "date-value" attribute
|
||||
try {
|
||||
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE);
|
||||
return new \DateTime($nodeValue);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,15 +178,27 @@ class CellValueFormatter
|
||||
* Returns the cell Time value from the given node.
|
||||
*
|
||||
* @param \DOMNode $node
|
||||
* @return \DateInterval|null The value associated with the cell or NULL if invalid time value
|
||||
* @return \DateInterval|string|null The value associated with the cell or NULL if invalid time value
|
||||
*/
|
||||
protected function formatTimeCellValue($node)
|
||||
{
|
||||
try {
|
||||
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE);
|
||||
return new \DateInterval($nodeValue);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
// The XML node looks like this:
|
||||
// <table:table-cell calcext:value-type="time" office:time-value="PT13H24M00S" office:value-type="time">
|
||||
// <text:p>01:24:00 PM</text:p>
|
||||
// </table:table-cell>
|
||||
|
||||
if ($this->shouldFormatDates) {
|
||||
// The date is already formatted in the "p" tag
|
||||
$nodeWithValueAlreadyFormatted = $node->getElementsByTagName(self::XML_NODE_P)->item(0);
|
||||
return $nodeWithValueAlreadyFormatted->nodeValue;
|
||||
} else {
|
||||
// otherwise, get it from the "time-value" attribute
|
||||
try {
|
||||
$nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE);
|
||||
return new \DateInterval($nodeValue);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class Reader extends AbstractReader
|
||||
$this->zip = new \ZipArchive();
|
||||
|
||||
if ($this->zip->open($filePath) === true) {
|
||||
$this->sheetIterator = new SheetIterator($filePath);
|
||||
$this->sheetIterator = new SheetIterator($filePath, $this->shouldFormatDates);
|
||||
} else {
|
||||
throw new IOException("Could not open $filePath for reading.");
|
||||
}
|
||||
|
||||
@@ -45,11 +45,12 @@ class RowIterator implements IteratorInterface
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader XML Reader, positioned on the "<table:table>" element
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
*/
|
||||
public function __construct($xmlReader)
|
||||
public function __construct($xmlReader, $shouldFormatDates)
|
||||
{
|
||||
$this->xmlReader = $xmlReader;
|
||||
$this->cellValueFormatter = new CellValueFormatter();
|
||||
$this->cellValueFormatter = new CellValueFormatter($shouldFormatDates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +187,7 @@ class RowIterator implements IteratorInterface
|
||||
/**
|
||||
* empty() replacement that honours 0 as a valid value
|
||||
*
|
||||
* @param $value The cell value
|
||||
* @param string|int|float|bool|\DateTime|\DateInterval|null $value The cell value
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEmptyCellValue($value)
|
||||
|
||||
@@ -27,12 +27,13 @@ class Sheet implements SheetInterface
|
||||
|
||||
/**
|
||||
* @param XMLReader $xmlReader XML Reader, positioned on the "<table:table>" element
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
* @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based)
|
||||
* @param string $sheetName Name of the sheet
|
||||
*/
|
||||
public function __construct($xmlReader, $sheetIndex, $sheetName)
|
||||
public function __construct($xmlReader, $shouldFormatDates, $sheetIndex, $sheetName)
|
||||
{
|
||||
$this->rowIterator = new RowIterator($xmlReader);
|
||||
$this->rowIterator = new RowIterator($xmlReader, $shouldFormatDates);
|
||||
$this->index = $sheetIndex;
|
||||
$this->name = $sheetName;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ class SheetIterator implements IteratorInterface
|
||||
/** @var string $filePath Path of the file to be read */
|
||||
protected $filePath;
|
||||
|
||||
/** @var bool Whether date/time values should be returned as PHP objects or be formatted as strings */
|
||||
protected $shouldFormatDates;
|
||||
|
||||
/** @var XMLReader The XMLReader object that will help read sheet's XML data */
|
||||
protected $xmlReader;
|
||||
|
||||
@@ -36,11 +39,13 @@ class SheetIterator implements IteratorInterface
|
||||
|
||||
/**
|
||||
* @param string $filePath Path of the file to be read
|
||||
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
|
||||
* @throws \Box\Spout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file
|
||||
*/
|
||||
public function __construct($filePath)
|
||||
public function __construct($filePath, $shouldFormatDates)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
$this->shouldFormatDates = $shouldFormatDates;
|
||||
$this->xmlReader = new XMLReader();
|
||||
|
||||
/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
|
||||
@@ -109,7 +114,7 @@ class SheetIterator implements IteratorInterface
|
||||
$escapedSheetName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_NAME);
|
||||
$sheetName = $this->escaper->unescape($escapedSheetName);
|
||||
|
||||
return new Sheet($this->xmlReader, $sheetName, $this->currentSheetIndex);
|
||||
return new Sheet($this->xmlReader, $this->shouldFormatDates, $sheetName, $this->currentSheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user