diff --git a/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php b/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php index 0ab70870..b276707d 100644 --- a/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php +++ b/src/PhpSpreadsheet/Chart/Renderer/JpGraph.php @@ -21,6 +21,14 @@ use ScatterPlot; use Spline; use StockPlot; +/** + * Jpgraph is not maintained in Composer, and the version there + * is extremely out of date. For that reason, all unit test + * requiring Jpgraph are skipped. So, do not measure + * code coverage for this class till that is fixed. + * + * @codeCoverageIgnore + */ class JpGraph implements IRenderer { private static $width = 640; diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php b/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php index 5b501e0f..204c7302 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php @@ -22,11 +22,8 @@ class PageSetup public function printInformation(SimpleXMLElement $sheet): self { - if (isset($sheet->PrintInformation)) { + if (isset($sheet->PrintInformation, $sheet->PrintInformation[0])) { $printInformation = $sheet->PrintInformation[0]; - if (!$printInformation) { - return $this; - } $scale = (string) $printInformation->Scale->attributes()['percentage']; $pageOrder = (string) $printInformation->order; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Theme.php b/src/PhpSpreadsheet/Reader/Xlsx/Theme.php index 1f2b863c..706c4d19 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Theme.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Theme.php @@ -41,9 +41,11 @@ class Theme } /** - * Get Theme Name. + * Not called by Reader, never accessible any other time. * * @return string + * + * @codeCoverageIgnore */ public function getThemeName() { @@ -51,9 +53,11 @@ class Theme } /** - * Get colour Scheme Name. + * Not called by Reader, never accessible any other time. * * @return string + * + * @codeCoverageIgnore */ public function getColourSchemeName() { @@ -69,25 +73,6 @@ class Theme */ public function getColourByIndex($index) { - if (isset($this->colourMap[$index])) { - return $this->colourMap[$index]; - } - - return null; - } - - /** - * Implement PHP __clone to create a deep clone, not just a shallow copy. - */ - public function __clone() - { - $vars = get_object_vars($this); - foreach ($vars as $key => $value) { - if ((is_object($value)) && ($key != '_parent')) { - $this->$key = clone $value; - } else { - $this->$key = $value; - } - } + return $this->colourMap[$index] ?? null; } } diff --git a/src/PhpSpreadsheet/RichText/RichText.php b/src/PhpSpreadsheet/RichText/RichText.php index 3a6e1f8e..88e7c792 100644 --- a/src/PhpSpreadsheet/RichText/RichText.php +++ b/src/PhpSpreadsheet/RichText/RichText.php @@ -158,11 +158,14 @@ class RichText implements IComparable { $vars = get_object_vars($this); foreach ($vars as $key => $value) { - if (is_object($value)) { - $this->$key = clone $value; - } else { - $this->$key = $value; + $newValue = is_object($value) ? (clone $value) : $value; + if (is_array($value)) { + $newValue = []; + foreach ($value as $key2 => $value2) { + $newValue[$key2] = is_object($value2) ? (clone $value2) : $value2; + } } + $this->$key = $newValue; } } } diff --git a/src/PhpSpreadsheet/RichText/TextElement.php b/src/PhpSpreadsheet/RichText/TextElement.php index 6bec005b..23733436 100644 --- a/src/PhpSpreadsheet/RichText/TextElement.php +++ b/src/PhpSpreadsheet/RichText/TextElement.php @@ -68,19 +68,4 @@ class TextElement implements ITextElement __CLASS__ ); } - - /** - * Implement PHP __clone to create a deep clone, not just a shallow copy. - */ - public function __clone() - { - $vars = get_object_vars($this); - foreach ($vars as $key => $value) { - if (is_object($value)) { - $this->$key = clone $value; - } else { - $this->$key = $value; - } - } - } } diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index e5b056c9..616b2d4f 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -299,11 +299,14 @@ class Font extends Supervisor if ($fontname == '') { $fontname = 'Calibri'; } - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->latin = $fontname; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['latin' => $fontname]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->latin = $fontname; + // @codeCoverageIgnoreEnd } return $this; @@ -314,11 +317,14 @@ class Font extends Supervisor if ($fontname == '') { $fontname = 'Calibri'; } - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->eastAsian = $fontname; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['eastAsian' => $fontname]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->eastAsian = $fontname; + // @codeCoverageIgnoreEnd } return $this; @@ -329,11 +335,14 @@ class Font extends Supervisor if ($fontname == '') { $fontname = 'Calibri'; } - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->complexScript = $fontname; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['complexScript' => $fontname]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->complexScript = $fontname; + // @codeCoverageIgnoreEnd } return $this; @@ -533,11 +542,14 @@ class Font extends Supervisor public function setBaseLine(int $baseLine): self { - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->baseLine = $baseLine; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['baseLine' => $baseLine]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->baseLine = $baseLine; + // @codeCoverageIgnoreEnd } return $this; @@ -554,11 +566,14 @@ class Font extends Supervisor public function setStrikeType(string $strikeType): self { - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->strikeType = $strikeType; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['strikeType' => $strikeType]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->strikeType = $strikeType; + // @codeCoverageIgnoreEnd } return $this; @@ -575,11 +590,14 @@ class Font extends Supervisor public function setUSchemeClr(string $uSchemeClr): self { - if ($this->isSupervisor) { + if (!$this->isSupervisor) { + $this->uSchemeClr = $uSchemeClr; + } else { + // should never be true + // @codeCoverageIgnoreStart $styleArray = $this->getStyleArray(['uSchemeClr' => $uSchemeClr]); $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); - } else { - $this->uSchemeClr = $uSchemeClr; + // @codeCoverageIgnoreEnd } return $this; @@ -743,6 +761,7 @@ class Font extends Supervisor $this->exportArray2($exportedArray, 'subscript', $this->getSubscript()); $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript()); $this->exportArray2($exportedArray, 'underline', $this->getUnderline()); + $this->exportArray2($exportedArray, 'uSchemeClr', $this->getUSchemeClr()); return $exportedArray; } diff --git a/src/PhpSpreadsheet/Style/Style.php b/src/PhpSpreadsheet/Style/Style.php index 78e5ebbf..5ea70597 100644 --- a/src/PhpSpreadsheet/Style/Style.php +++ b/src/PhpSpreadsheet/Style/Style.php @@ -421,8 +421,10 @@ class Style extends Supervisor // Handle bug in PHPStan, see https://github.com/phpstan/phpstan/issues/5805 // $newStyle should always be defined. // This block might not be needed in the future + // @codeCoverageIgnoreStart $newStyle = clone $style; $newStyle->applyFromArray($styleArray); + // @codeCoverageIgnoreEnd } // we don't have such a cell Xf, need to add diff --git a/src/PhpSpreadsheet/Writer/BaseWriter.php b/src/PhpSpreadsheet/Writer/BaseWriter.php index 7a3fa369..2ea0ea8b 100644 --- a/src/PhpSpreadsheet/Writer/BaseWriter.php +++ b/src/PhpSpreadsheet/Writer/BaseWriter.php @@ -118,7 +118,9 @@ abstract class BaseWriter implements IWriter $mode = 'wb'; $scheme = parse_url($filename, PHP_URL_SCHEME); if ($scheme === 's3') { + // @codeCoverageIgnoreStart $mode = 'w'; + // @codeCoverageIgnoreEnd } $fileHandle = $filename ? fopen($filename, $mode) : false; if ($fileHandle === false) { diff --git a/tests/PhpSpreadsheetTests/Reader/BaseNoLoad.php b/tests/PhpSpreadsheetTests/Reader/BaseNoLoad.php new file mode 100644 index 00000000..02298e8d --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/BaseNoLoad.php @@ -0,0 +1,18 @@ +loadSpreadsheetFromFile($filename); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/BaseNoLoadTest.php b/tests/PhpSpreadsheetTests/Reader/BaseNoLoadTest.php new file mode 100644 index 00000000..c79bb9e9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/BaseNoLoadTest.php @@ -0,0 +1,17 @@ +expectException(SpreadsheetException::class); + $this->expectExceptionMessage('Reader classes must implement their own loadSpreadsheetFromFile() method'); + $reader = new BaseNoLoad(); + $reader->loadxxx('unknown.file'); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Html/HtmlBorderTest.php b/tests/PhpSpreadsheetTests/Reader/Html/HtmlBorderTest.php index 58a0b5d7..a7b95bcf 100644 --- a/tests/PhpSpreadsheetTests/Reader/Html/HtmlBorderTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Html/HtmlBorderTest.php @@ -18,6 +18,8 @@ class HtmlBorderTest extends TestCase Border left Border right + + '; $filename = HtmlHelper::createHtml($html); @@ -61,6 +63,17 @@ class HtmlBorderTest extends TestCase foreach ([$borders->getTop(), $borders->getBottom(), $borders->getLeft(), $borders->getRight()] as $border) { self::assertEquals(Border::BORDER_NONE, $border->getBorderStyle()); } + + $style = $firstSheet->getCell('G1')->getStyle(); + $borders = $style->getBorders(); + $border = $borders->getRight(); + self::assertEquals(Border::BORDER_DASHED, $border->getBorderStyle()); + + $style = $firstSheet->getCell('H1')->getStyle(); + $borders = $style->getBorders(); + $border = $borders->getRight(); + self::assertEquals(Border::BORDER_DOTTED, $border->getBorderStyle()); + self::assertEquals('333333', $border->getColor()->getRGB()); } /** diff --git a/tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php b/tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php index 38a9bc9e..cabac403 100644 --- a/tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php @@ -327,4 +327,35 @@ class HtmlTest extends TestCase self::assertEquals(Border::BORDER_THIN, $border->getBorderStyle()); } } + + public function testBorderWithColspan(): void + { + $html = ' + + + + + + + +
NOT SPANNEDSPANNED
NOT SPANNED
'; + + $reader = new Html(); + $spreadsheet = $reader->loadFromString($html); + $firstSheet = $spreadsheet->getSheet(0); + $style = $firstSheet->getStyle('B1:B2'); + + $borders = $style->getBorders(); + + $totalBorders = [ + $borders->getTop(), + $borders->getLeft(), + $borders->getBottom(), + $borders->getRight(), + ]; + + foreach ($totalBorders as $border) { + self::assertEquals(Border::BORDER_THIN, $border->getBorderStyle()); + } + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php new file mode 100644 index 00000000..6d6949d8 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php @@ -0,0 +1,106 @@ +getRowDimension($row)->getVisible()) { + $actualVisible[] = $row; + } + } + + return $actualVisible; + } + + public function testReadDateRange(): void + { + $spreadsheet = IOFactory::load(self::TESTBOOK); + $sheet = $spreadsheet->getSheetByName('daterange'); + self::assertNotNull($sheet); + $filter = $sheet->getAutoFilter(); + $maxRow = 30; + self::assertSame("A1:A$maxRow", $filter->getRange()); + $columns = $filter->getColumns(); + self::assertCount(1, $columns); + $column = $columns['A'] ?? null; + self::assertNotNull($column); + $ruleset = $column->getRules(); + self::assertCount(1, $ruleset); + $rule = $ruleset[0]; + self::assertSame(Rule::AUTOFILTER_RULETYPE_DATEGROUP, $rule->getRuleType()); + $value = $rule->getValue(); + self::assertIsArray($value); + self::assertCount(6, $value); + self::assertSame('2002', $value['year']); + self::assertSame('', $value['month']); + self::assertSame('', $value['day']); + self::assertSame('', $value['hour']); + self::assertSame('', $value['minute']); + self::assertSame('', $value['second']); + self::assertSame( + [25, 26, 27, 28, 29, 30], + $this->getVisibleSheet($sheet, $maxRow) + ); + $spreadsheet->disconnectWorksheets(); + } + + public function testReadTopTen(): void + { + $spreadsheet = IOFactory::load(self::TESTBOOK); + $sheet = $spreadsheet->getSheetByName('top10'); + self::assertNotNull($sheet); + $filter = $sheet->getAutoFilter(); + $maxRow = 65; + self::assertSame("A1:A$maxRow", $filter->getRange()); + $columns = $filter->getColumns(); + self::assertCount(1, $columns); + $column = $columns['A'] ?? null; + self::assertNotNull($column); + $ruleset = $column->getRules(); + self::assertCount(1, $ruleset); + $rule = $ruleset[0]; + self::assertSame(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER, $rule->getRuleType()); + $value = $rule->getValue(); + self::assertSame('10', $value); + self::assertSame( + [56, 57, 58, 59, 60, 61, 62, 63, 64, 65], + $this->getVisibleSheet($sheet, $maxRow) + ); + $spreadsheet->disconnectWorksheets(); + } + + public function testReadDynamic(): void + { + $spreadsheet = IOFactory::load(self::TESTBOOK); + $sheet = $spreadsheet->getSheetByName('dynamic'); + self::assertNotNull($sheet); + $filter = $sheet->getAutoFilter(); + $maxRow = 30; + self::assertSame("A1:A$maxRow", $filter->getRange()); + $columns = $filter->getColumns(); + self::assertCount(1, $columns); + $column = $columns['A'] ?? null; + self::assertNotNull($column); + $ruleset = $column->getRules(); + self::assertCount(1, $ruleset); + $rule = $ruleset[0]; + self::assertSame(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER, $rule->getRuleType()); + self::assertSame('M4', $rule->getGrouping()); + self::assertSame( + [5, 17, 28], + $this->getVisibleSheet($sheet, $maxRow) + ); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetup2Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetup2Test.php new file mode 100644 index 00000000..1bbc88ac --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetup2Test.php @@ -0,0 +1,41 @@ +getAllSheets() as $worksheet) { + ++$sheets; + $hf = $worksheet->getHeaderFooter(); + self::assertTrue($hf->getDifferentOddEven()); + self::assertTrue($hf->getDifferentFirst()); + } + self::assertSame(4, $sheets); + + $spreadsheet->disconnectWorksheets(); + } + + public function testColumnBreak(): void + { + $spreadsheet = IOFactory::load(self::TESTBOOK); + $sheet = $spreadsheet->getSheetByName('colbreak'); + self::assertNotNull($sheet); + $breaks = $sheet->getBreaks(); + self::assertCount(1, $breaks); + $break = $breaks['D1'] ?? null; + self::assertNotNull($break); + self::assertSame($break, Worksheet::BREAK_COLUMN); + + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/RichTextTest.php b/tests/PhpSpreadsheetTests/RichTextTest.php new file mode 100644 index 00000000..e2dfcce7 --- /dev/null +++ b/tests/PhpSpreadsheetTests/RichTextTest.php @@ -0,0 +1,41 @@ +getActiveSheet(); + $cell = $sheet->getCell('A1'); + $cell->setValue(2); + self::assertSame(2, $cell->getCalculatedValue()); + $cell->getStyle()->getFont()->setName('whatever'); + $richText = new RichText($cell); + self::assertSame('whatever', $sheet->getCell('A1')->getStyle()->getFont()->getName()); + self::assertEquals($richText, $cell->getValue()); + self::assertSame('2', $cell->getCalculatedValue()); + + $spreadsheet->disconnectWorksheets(); + } + + public function testTextElements(): void + { + $element1 = new TextElement('A'); + self::assertNull($element1->getFont()); + $element2 = new TextElement('B'); + $element3 = new TextElement('C'); + $richText = new RichText(); + $richText->setRichTextElements([$element1, $element2, $element3]); + self::assertSame('ABC', $richText->getPlainText()); + $cloneText = clone $richText; + self::assertEquals($richText, $cloneText); + self::assertNotSame($richText, $cloneText); + } +} diff --git a/tests/PhpSpreadsheetTests/Style/FontTest.php b/tests/PhpSpreadsheetTests/Style/FontTest.php index a6843c10..02814afa 100644 --- a/tests/PhpSpreadsheetTests/Style/FontTest.php +++ b/tests/PhpSpreadsheetTests/Style/FontTest.php @@ -38,6 +38,7 @@ class FontTest extends TestCase $font->setSuperscript(true); self::assertTrue($font->getSuperscript()); self::assertFalse($font->getSubscript(), 'False remains unchanged'); + $spreadsheet->disconnectWorksheets(); } public function testSize(): void @@ -70,5 +71,21 @@ class FontTest extends TestCase $font->setSize($invalidFontSizeValue); self::assertEquals(10, $font->getSize(), 'Set to 10 after trying to set an invalid value.'); } + $spreadsheet->disconnectWorksheets(); + } + + public function testName(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $cell = $sheet->getCell('A1'); + $cell->setValue('Cell A1'); + $font = $cell->getStyle()->getFont(); + self::assertEquals('Calibri', $font->getName(), 'The default is Calibri'); + $font->setName('whatever'); + self::assertEquals('whatever', $font->getName(), 'The default is Calibri'); + $font->setName(''); + self::assertEquals('Calibri', $font->getName(), 'Null string changed to default'); + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/data/Reader/XLSX/autofilter2.xlsx b/tests/data/Reader/XLSX/autofilter2.xlsx new file mode 100644 index 00000000..70c9839c Binary files /dev/null and b/tests/data/Reader/XLSX/autofilter2.xlsx differ diff --git a/tests/data/Style/Color/ColorGetBlue.php b/tests/data/Style/Color/ColorGetBlue.php index d12a98f0..ff4d9fb7 100644 --- a/tests/data/Style/Color/ColorGetBlue.php +++ b/tests/data/Style/Color/ColorGetBlue.php @@ -32,4 +32,5 @@ return [ 'FFFF00', false, ], + 'invalid hex' => ['00', 'AABBDX'], ];