Xlsx Reader External Data Validations Flag Missing (#3078)

* Xlsx Reader External Data Validations Flag Missing

Fix #2677. This PR supersedes #2679, written by @technghiath, which lacks tests, and probably doesn't solve the problem entirely. The code causing the problem appears to be the last remnant in Xlsx Reader which calls `children` using a namespace prefix rather than a namespace. That is changed, and tests are added where the tag is unexpectedly missing, and also where it uses a non-standard namespace prefix.

* Scrutinizer

Reports 1 "new" error. It isn't, but fix it anyhow.

* Fix One Existing Scrutinizer Problem

Only remaining problem in Reader/Xlsx.
This commit is contained in:
oleibman 2022-09-28 00:14:37 -07:00 committed by GitHub
parent a193f36f31
commit 050a42db8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 43 deletions

View File

@ -469,6 +469,7 @@ class Xlsx extends BaseReader
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS); $rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
$propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties()); $propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
$chartDetails = [];
foreach ($rels->Relationship as $relx) { foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx); $rel = self::getAttributes($relx);
$relTarget = (string) $rel['Target']; $relTarget = (string) $rel['Target'];
@ -929,13 +930,16 @@ class Xlsx extends BaseReader
$xmlSheet->addChild('dataValidations'); $xmlSheet->addChild('dataValidations');
} }
foreach ($xmlSheet->extLst->ext->children('x14', true)->dataValidations->dataValidation as $item) { foreach ($xmlSheet->extLst->ext->children(Namespaces::DATA_VALIDATIONS1)->dataValidations->dataValidation as $item) {
$item = self::testSimpleXml($item);
$node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation'); $node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
foreach ($item->attributes() ?? [] as $attr) { foreach ($item->attributes() ?? [] as $attr) {
$node->addAttribute($attr->getName(), $attr); $node->addAttribute($attr->getName(), $attr);
} }
$node->addAttribute('sqref', $item->children('xm', true)->sqref); $node->addAttribute('sqref', $item->children(Namespaces::DATA_VALIDATIONS2)->sqref);
$node->addChild('formula1', $item->formula1->children('xm', true)->f); if (isset($item->formula1)) {
$node->addChild('formula1', $item->formula1->children(Namespaces::DATA_VALIDATIONS2)->f);
}
} }
} }
@ -1278,6 +1282,7 @@ class Xlsx extends BaseReader
if ($xmlDrawingChildren->oneCellAnchor) { if ($xmlDrawingChildren->oneCellAnchor) {
foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) { foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
$oneCellAnchor = self::testSimpleXml($oneCellAnchor);
if ($oneCellAnchor->pic->blipFill) { if ($oneCellAnchor->pic->blipFill) {
/** @var SimpleXMLElement $blip */ /** @var SimpleXMLElement $blip */
$blip = $oneCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip; $blip = $oneCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
@ -1285,8 +1290,6 @@ class Xlsx extends BaseReader
$xfrm = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm; $xfrm = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
/** @var SimpleXMLElement $outerShdw */ /** @var SimpleXMLElement $outerShdw */
$outerShdw = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw; $outerShdw = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
/** @var SimpleXMLElement $hlinkClick */
$hlinkClick = $oneCellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'name')); $objDrawing->setName((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'name'));
@ -1363,11 +1366,11 @@ class Xlsx extends BaseReader
} }
if ($xmlDrawingChildren->twoCellAnchor) { if ($xmlDrawingChildren->twoCellAnchor) {
foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) { foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
$twoCellAnchor = self::testSimpleXml($twoCellAnchor);
if ($twoCellAnchor->pic->blipFill) { if ($twoCellAnchor->pic->blipFill) {
$blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip; $blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
$xfrm = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm; $xfrm = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
$outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw; $outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
$hlinkClick = $twoCellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
/** @scrutinizer ignore-call */ /** @scrutinizer ignore-call */
$editAs = $twoCellAnchor->attributes(); $editAs = $twoCellAnchor->attributes();

View File

@ -58,6 +58,10 @@ class Namespaces
const VBA = 'http://schemas.microsoft.com/office/2006/relationships/vbaProject'; const VBA = 'http://schemas.microsoft.com/office/2006/relationships/vbaProject';
const DATA_VALIDATIONS1 = 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main';
const DATA_VALIDATIONS2 = 'http://schemas.microsoft.com/office/excel/2006/main';
const DC_ELEMENTS = 'http://purl.org/dc/elements/1.1/'; const DC_ELEMENTS = 'http://purl.org/dc/elements/1.1/';
const DC_TERMS = 'http://purl.org/dc/terms'; const DC_TERMS = 'http://purl.org/dc/terms';

View File

@ -0,0 +1,58 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class DataValidationTest extends TestCase
{
public function testLoadXlsxDataValidation(): void
{
$filename = 'tests/data/Reader/XLSX/dataValidationTest.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$worksheet = $spreadsheet->getActiveSheet();
self::assertTrue($worksheet->getCell('B3')->hasDataValidation());
$spreadsheet->disconnectWorksheets();
}
/**
* Test for load drop down lists of another sheet.
* Pull #2150, issue #2149. Also issue #2677.
*
* @dataProvider providerExternalSheet
*/
public function testDataValidationOfAnotherSheet(string $expectedB14, string $filename): void
{
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$worksheet = $spreadsheet->getActiveSheet();
// same sheet
$validationCell = $worksheet->getCell('B5');
self::assertTrue($validationCell->hasDataValidation());
self::assertSame(DataValidation::TYPE_LIST, $validationCell->getDataValidation()->getType());
self::assertSame('$A$5:$A$7', $validationCell->getDataValidation()->getFormula1());
// another sheet
$validationCell = $worksheet->getCell('B14');
self::assertTrue($validationCell->hasDataValidation());
self::assertSame(DataValidation::TYPE_LIST, $validationCell->getDataValidation()->getType());
self::assertSame($expectedB14, $validationCell->getDataValidation()->getFormula1());
$spreadsheet->disconnectWorksheets();
}
public function providerExternalSheet(): array
{
return [
'standard spreadsheet' => ['Feuil2!$A$3:$A$5', 'tests/data/Reader/XLSX/dataValidation2Test.xlsx'],
'alternate namespace prefix' => ['Feuil2!$A$3:$A$5', 'tests/data/Reader/XLSX/issue.2677.namespace.xlsx'],
'missing formula' => ['', 'tests/data/Reader/XLSX/issue.2677.removeformula1.xlsx'],
];
}
}

View File

@ -3,7 +3,6 @@
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx; namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\File;
@ -163,42 +162,6 @@ class XlsxTest extends TestCase
self::assertInstanceOf(Style::class, $conditionalRule->getStyle()); self::assertInstanceOf(Style::class, $conditionalRule->getStyle());
} }
public function testLoadXlsxDataValidation(): void
{
$filename = 'tests/data/Reader/XLSX/dataValidationTest.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$worksheet = $spreadsheet->getActiveSheet();
self::assertTrue($worksheet->getCell('B3')->hasDataValidation());
}
/*
* Test for load drop down lists of another sheet.
* Pull #2150, issue #2149
*/
public function testLoadXlsxDataValidationOfAnotherSheet(): void
{
$filename = 'tests/data/Reader/XLSX/dataValidation2Test.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$worksheet = $spreadsheet->getActiveSheet();
// same sheet
$validationCell = $worksheet->getCell('B5');
self::assertTrue($validationCell->hasDataValidation());
self::assertSame(DataValidation::TYPE_LIST, $validationCell->getDataValidation()->getType());
self::assertSame('$A$5:$A$7', $validationCell->getDataValidation()->getFormula1());
// another sheet
$validationCell = $worksheet->getCell('B14');
self::assertTrue($validationCell->hasDataValidation());
self::assertSame(DataValidation::TYPE_LIST, $validationCell->getDataValidation()->getType());
self::assertSame('Feuil2!$A$3:$A$5', $validationCell->getDataValidation()->getFormula1());
}
/** /**
* Test load Xlsx file without cell reference. * Test load Xlsx file without cell reference.
* *

Binary file not shown.

Binary file not shown.