Specify data type in html tags using attributes (#3445)

* Specify data type in html tags using attributes #3444

* Set data types using attributes in flushCell function and add some unit tests

* Check TYPE_INLINE in flushCell and some changes in test cases
This commit is contained in:
Pouria Seyfi
2023-03-18 19:07:39 +03:30
committed by GitHub
parent 452ec07e42
commit 603d093df7
2 changed files with 71 additions and 11 deletions
+32 -11
View File
@@ -7,6 +7,7 @@ use DOMElement;
use DOMNode;
use DOMText;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension;
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -283,15 +284,35 @@ class Html extends BaseReader
* @param int|string $row
* @param mixed $cellContent
*/
protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void
protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent, array $attributeArray): void
{
if (is_string($cellContent)) {
// Simple String content
if (trim($cellContent) > '') {
// Only actually write it if there's content in the string
// Write to worksheet to be done here...
// ... we return the cell so we can mess about with styles more easily
$sheet->setCellValue($column . $row, $cellContent);
// ... we return the cell, so we can mess about with styles more easily
// Set cell value explicitly if there is data-type attribute
if (isset($attributeArray['data-type'])) {
$datatype = $attributeArray['data-type'];
if (in_array($datatype, [DataType::TYPE_STRING, DataType::TYPE_STRING2, DataType::TYPE_INLINE])) {
//Prevent to Excel treat string with beginning equal sign or convert big numbers to scientific number
if (substr($cellContent, 0, 1) === '=') {
$sheet->getCell($column . $row)
->getStyle()
->setQuotePrefix(true);
}
}
//catching the Exception and ignoring the invalid data types
try {
$sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
} catch (\PhpOffice\PhpSpreadsheet\Exception $exception) {
$sheet->setCellValue($column . $row, $cellContent);
}
} else {
$sheet->setCellValue($column . $row, $cellContent);
}
$this->dataArray[$row][$column] = $cellContent;
}
} else {
@@ -355,7 +376,7 @@ class Html extends BaseReader
private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'hr') {
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
++$row;
if (isset($this->formats[$child->nodeName])) {
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
@@ -375,7 +396,7 @@ class Html extends BaseReader
$sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
} else {
// Otherwise flush our existing content and move the row cursor on
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
++$row;
}
} else {
@@ -421,11 +442,11 @@ class Html extends BaseReader
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
if ($cellContent > '') {
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
++$row;
}
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
if (isset($this->formats[$child->nodeName])) {
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
@@ -448,11 +469,11 @@ class Html extends BaseReader
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
if ($cellContent > '') {
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
}
++$row;
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$column = 'A';
}
} else {
@@ -472,7 +493,7 @@ class Html extends BaseReader
private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'table') {
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$column = $this->setTableStartColumn($column);
if ($this->tableLevel > 1 && $row > 1) {
--$row;
@@ -574,7 +595,7 @@ class Html extends BaseReader
// apply inline style
$this->applyInlineStyle($sheet, $row, $column, $attributeArray);
$this->flushCell($sheet, $column, $row, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$this->processDomElementBgcolor($sheet, $row, $column, $attributeArray);
$this->processDomElementWidth($sheet, $column, $attributeArray);
@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Reader\Html;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Html;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
@@ -380,4 +381,42 @@ class HtmlTest extends TestCase
}
$spreadsheet->disconnectWorksheets();
}
public function testDataType(): void
{
$html = '<table>
<tr>
<td data-type="b">1</td>
<td data-type="s">12345678987654</td>
<!-- in some cases, you may want to treat the string with beginning equal sign as a string rather than a formula -->
<td data-type="s">=B1</td>
<td data-type="d">2022-02-21 10:20:30</td>
<td data-type="null">null</td>
<td data-type="invalid-datatype">text with invalid datatype</td>
</tr>
</table>';
$reader = new Html();
$spreadsheet = $reader->loadFromString($html);
$firstSheet = $spreadsheet->getSheet(0);
// check boolean data type
self::assertEquals(DataType::TYPE_BOOL, $firstSheet->getCell('A1')->getDataType());
self::assertIsBool($firstSheet->getCell('A1')->getValue());
// check string data type
self::assertEquals(DataType::TYPE_STRING, $firstSheet->getCell('B1')->getDataType());
self::assertIsString($firstSheet->getCell('B1')->getValue());
// check string with beginning equal sign (=B1) and string datatype,is not formula
self::assertEquals(DataType::TYPE_STRING, $firstSheet->getCell('C1')->getDataType());
self::assertEquals('=B1', $firstSheet->getCell('C1')->getValue());
self::assertTrue($firstSheet->getCell('C1')->getStyle()->getQuotePrefix());
//check iso date
self::assertEqualsWithDelta($firstSheet->getCell('D1')->getValue(), 44613.43090277778, 1.0e-12);
//null
self::assertEquals($firstSheet->getCell('E1')->getValue(), null);
}
}