diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a4313db7..5ea9f39594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com) and this project adheres to [Semantic Versioning](https://semver.org). +# TBD - 2.3.6 + +### Deprecated + +- Worksheet::getHashCode is no longer needed. + +### Fixed + +- Change hash code for worksheet. Backport of [PR #4207](https://github.com/PHPOffice/PhpSpreadsheet/pull/4207) +- Retitling cloned worksheets. Backport of [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302) + + # 2024-12-26 - 2.3.5 ### Deprecated diff --git a/src/PhpSpreadsheet/Cell/Cell.php b/src/PhpSpreadsheet/Cell/Cell.php index 26b34cd940..f10af3ab6c 100644 --- a/src/PhpSpreadsheet/Cell/Cell.php +++ b/src/PhpSpreadsheet/Cell/Cell.php @@ -318,7 +318,7 @@ public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE self::updateIfCellIsTableHeader($this->getParent()?->getParent(), $this, $oldValue, $value); $worksheet = $this->getWorksheet(); $spreadsheet = $worksheet->getParent(); - if (isset($spreadsheet)) { + if (isset($spreadsheet) && $spreadsheet->getIndex($worksheet, true) >= 0) { $originalSelected = $worksheet->getSelectedCells(); $activeSheetIndex = $spreadsheet->getActiveSheetIndex(); $style = $this->getStyle(); diff --git a/src/PhpSpreadsheet/ReferenceHelper.php b/src/PhpSpreadsheet/ReferenceHelper.php index 8b2f76cc29..45c131b6cc 100644 --- a/src/PhpSpreadsheet/ReferenceHelper.php +++ b/src/PhpSpreadsheet/ReferenceHelper.php @@ -921,7 +921,7 @@ private function updateNamedRange(DefinedName $definedName, Worksheet $worksheet { $cellAddress = $definedName->getValue(); $asFormula = ($cellAddress[0] === '='); - if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) { + if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashInt() === $worksheet->getHashInt()) { /** * If we delete the entire range that is referenced by a Named Range, MS Excel sets the value to #REF! * PhpSpreadsheet still only does a basic adjustment, so the Named Range will still reference Cells. @@ -940,7 +940,7 @@ private function updateNamedRange(DefinedName $definedName, Worksheet $worksheet private function updateNamedFormula(DefinedName $definedName, Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void { - if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) { + if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashInt() === $worksheet->getHashInt()) { /** * If we delete the entire range that is referenced by a Named Formula, MS Excel sets the value to #REF! * PhpSpreadsheet still only does a basic adjustment, so the Named Formula will still reference Cells. diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index bcea8e6a74..dc228d283c 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -511,7 +511,7 @@ public function getActiveSheet(): Worksheet public function createSheet(?int $sheetIndex = null): Worksheet { $newSheet = new Worksheet($this); - $this->addSheet($newSheet, $sheetIndex); + $this->addSheet($newSheet, $sheetIndex, true); return $newSheet; } @@ -532,8 +532,20 @@ public function sheetNameExists(string $worksheetName): bool * @param Worksheet $worksheet The worksheet to add * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last) */ - public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet + public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null, bool $retitleIfNeeded = false): Worksheet { + if ($retitleIfNeeded) { + $title = $worksheet->getTitle(); + if ($this->sheetNameExists($title)) { + $i = 1; + $newTitle = "$title $i"; + while ($this->sheetNameExists($newTitle)) { + ++$i; + $newTitle = "$title $i"; + } + $worksheet->setTitle($newTitle); + } + } if ($this->sheetNameExists($worksheet->getTitle())) { throw new Exception( "Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first." @@ -657,13 +669,17 @@ public function getSheetByNameOrThrow(string $worksheetName): Worksheet * * @return int index */ - public function getIndex(Worksheet $worksheet): int + public function getIndex(Worksheet $worksheet, bool $noThrow = false): int { + $wsHash = $worksheet->getHashInt(); foreach ($this->workSheetCollection as $key => $value) { - if ($value->getHashCode() === $worksheet->getHashCode()) { + if ($value->getHashInt() === $wsHash) { return $key; } } + if ($noThrow) { + return -1; + } throw new Exception('Sheet does not exist.'); } diff --git a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php index 0cd4448672..81bcf73c83 100644 --- a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php +++ b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php @@ -416,7 +416,7 @@ public function getHashCode(): string return md5( $this->name . $this->description - . (($this->worksheet === null) ? '' : $this->worksheet->getHashCode()) + . (($this->worksheet === null) ? '' : (string) $this->worksheet->getHashInt()) . $this->coordinates . $this->offsetX . $this->offsetY diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index f8e9415435..a602fa8097 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -20,7 +20,6 @@ use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Exception; -use PhpOffice\PhpSpreadsheet\IComparable; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Shared; @@ -32,7 +31,7 @@ use PhpOffice\PhpSpreadsheet\Style\Protection as StyleProtection; use PhpOffice\PhpSpreadsheet\Style\Style; -class Worksheet implements IComparable +class Worksheet { // Break types public const BREAK_NONE = 0; @@ -305,15 +304,10 @@ class Worksheet implements IComparable */ private ?Color $tabColor = null; - /** - * Dirty flag. - */ - private bool $dirty = true; - /** * Hash. */ - private string $hash; + private int $hash; /** * CodeName. @@ -327,6 +321,7 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh { // Set parent and title $this->parent = $parent; + $this->hash = spl_object_id($this); $this->setTitle($title, false); // setTitle can change $pTitle $this->setCodeName($this->getTitle()); @@ -383,6 +378,12 @@ public function __destruct() unset($this->rowDimensions, $this->columnDimensions, $this->tableCollection, $this->drawingCollection, $this->chartCollection, $this->autoFilter); } + public function __wakeup(): void + { + $this->hash = spl_object_id($this); + $this->parent = null; + } + /** * Return the cell collection. */ @@ -862,7 +863,7 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true // Syntax check self::checkSheetTitle($title); - if ($this->parent) { + if ($this->parent && $this->parent->getIndex($this, true) >= 0) { // Is there already such sheet name? if ($this->parent->sheetNameExists($title)) { // Use name, but append with lowest possible integer @@ -891,9 +892,8 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true // Set title $this->title = $title; - $this->dirty = true; - if ($this->parent && $this->parent->getCalculationEngine()) { + if ($this->parent && $this->parent->getIndex($this, true) >= 0 && $this->parent->getCalculationEngine()) { // New title $newTitle = $this->getTitle(); $this->parent->getCalculationEngine() @@ -1026,7 +1026,6 @@ public function getProtection(): Protection public function setProtection(Protection $protection): static { $this->protection = $protection; - $this->dirty = true; return $this; } @@ -2994,7 +2993,7 @@ private function validateNamedRange(string $definedName, bool $returnNullIfInval if ($namedRange->getLocalOnly()) { $worksheet = $namedRange->getWorksheet(); - if ($worksheet === null || $this->getHashCode() !== $worksheet->getHashCode()) { + if ($worksheet === null || $this->hash !== $worksheet->getHashInt()) { if ($returnNullIfInvalid) { return null; } @@ -3131,17 +3130,15 @@ public function garbageCollect(): static } /** - * Get hash code. - * - * @return string Hash code + * @deprecated 3.5.0 use getHashInt instead. */ public function getHashCode(): string { - if ($this->dirty) { - $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__); - $this->dirty = false; - } + return (string) $this->hash; + } + public function getHashInt(): int + { return $this->hash; } @@ -3470,6 +3467,7 @@ public function __clone() } } } + $this->hash = spl_object_id($this); } /** diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/src/PhpSpreadsheet/Writer/Xlsx/Style.php index daac5d020c..a31b8e2498 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Style.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Style.php @@ -142,6 +142,7 @@ public function writeStyles(Spreadsheet $spreadsheet): string // dxf for ($i = 0; $i < $this->getParentWriter()->getStylesConditionalHashTable()->count(); ++$i) { + /** @var ?Conditional */ $thisstyle = $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i); if ($thisstyle !== null) { $this->writeCellStyleDxf($objWriter, $thisstyle->getStyle()); diff --git a/tests/PhpSpreadsheetTests/Shared/DateTest.php b/tests/PhpSpreadsheetTests/Shared/DateTest.php index 8da86e98ee..d747785917 100644 --- a/tests/PhpSpreadsheetTests/Shared/DateTest.php +++ b/tests/PhpSpreadsheetTests/Shared/DateTest.php @@ -8,6 +8,7 @@ use DateTimeZone; use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PHPUnit\Framework\TestCase; @@ -206,7 +207,7 @@ public function testVarious(): void $date = Date::PHPToExcel('2020-01-01'); self::assertEquals(43831.0, $date); - $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); + $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('B1', 'x'); /** @var float|int|string */ @@ -248,5 +249,6 @@ public function testVarious(): void ->getNumberFormat() ->setFormatCode('yyyy-mm-dd'); self::assertFalse(Date::isDateTime($cella4)); + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Shared/StringHelperInvalidCharTest.php b/tests/PhpSpreadsheetTests/Shared/StringHelperInvalidCharTest.php index eb46beaf39..13a58dfe19 100644 --- a/tests/PhpSpreadsheetTests/Shared/StringHelperInvalidCharTest.php +++ b/tests/PhpSpreadsheetTests/Shared/StringHelperInvalidCharTest.php @@ -43,5 +43,6 @@ public function testInvalidChar(): void $sheet->getCell("A$row")->getValue() ); } + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Style/BorderRangeTest.php b/tests/PhpSpreadsheetTests/Style/BorderRangeTest.php index 844fa3fb50..8c50ddc17d 100644 --- a/tests/PhpSpreadsheetTests/Style/BorderRangeTest.php +++ b/tests/PhpSpreadsheetTests/Style/BorderRangeTest.php @@ -62,6 +62,7 @@ public function testBorderRangeInAction(): void } } } + $spreadsheet->disconnectWorksheets(); } public function testBorderRangeDirectly(): void @@ -71,5 +72,6 @@ public function testBorderRangeDirectly(): void $sheet = $spreadsheet->getActiveSheet(); $style = $sheet->getStyle('A1:C1')->getBorders()->getTop()->setBorderStyle(Border::BORDER_THIN); self::assertSame('A1:C1', $style->getSelectedCells(), 'getSelectedCells should not change after a style operation on a border range'); + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Style/BorderTest.php b/tests/PhpSpreadsheetTests/Style/BorderTest.php index 9f18f07668..e6684b1a93 100644 --- a/tests/PhpSpreadsheetTests/Style/BorderTest.php +++ b/tests/PhpSpreadsheetTests/Style/BorderTest.php @@ -31,6 +31,7 @@ public function testAllBorders(): void self::assertSame(Border::BORDER_THIN, $borders->getRight()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $borders->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $borders->getDiagonal()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testAllBordersArray(): void @@ -45,6 +46,7 @@ public function testAllBordersArray(): void self::assertSame(Border::BORDER_THIN, $borders->getRight()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $borders->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $borders->getDiagonal()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testAllBordersArrayNotSupervisor(): void @@ -86,6 +88,7 @@ public function testOutline(): void self::assertSame(Border::BORDER_THIN, $sheet->getCell('B2')->getStyle()->getBorders()->getBottom()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getTop()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testInside(): void @@ -115,6 +118,7 @@ public function testInside(): void self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getBottom()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $sheet->getCell('B2')->getStyle()->getBorders()->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $sheet->getCell('B2')->getStyle()->getBorders()->getTop()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testHorizontal(): void @@ -144,6 +148,7 @@ public function testHorizontal(): void self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getBottom()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $sheet->getCell('B2')->getStyle()->getBorders()->getTop()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testVertical(): void @@ -173,6 +178,7 @@ public function testVertical(): void self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getBottom()->getBorderStyle()); self::assertSame(Border::BORDER_THIN, $sheet->getCell('B2')->getStyle()->getBorders()->getLeft()->getBorderStyle()); self::assertSame(Border::BORDER_NONE, $sheet->getCell('B2')->getStyle()->getBorders()->getTop()->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testNoSupervisorAllBorders(): void @@ -233,6 +239,7 @@ public function testBorderStyle(): void $border->setBorderStyle(Border::BORDER_THIN)->setColor(new Color('FFFF0000')); self::assertEquals('FFFF0000', $border->getColor()->getARGB()); self::assertEquals(Border::BORDER_THIN, $border->getBorderStyle()); + $spreadsheet->disconnectWorksheets(); } public function testDiagonalDirection(): void @@ -245,5 +252,6 @@ public function testDiagonalDirection(): void self::assertSame(Border::BORDER_MEDIUM, $borders->getDiagonal()->getBorderStyle()); self::assertSame(Borders::DIAGONAL_BOTH, $borders->getDiagonalDirection()); + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Style/ConditionalFormatting/Wizard/WizardFactoryTest.php b/tests/PhpSpreadsheetTests/Style/ConditionalFormatting/Wizard/WizardFactoryTest.php index 04681b39a0..5002bcaa1a 100644 --- a/tests/PhpSpreadsheetTests/Style/ConditionalFormatting/Wizard/WizardFactoryTest.php +++ b/tests/PhpSpreadsheetTests/Style/ConditionalFormatting/Wizard/WizardFactoryTest.php @@ -66,6 +66,7 @@ public function testWizardFromConditional(string $sheetName, string $cellAddress $wizard = Wizard::fromConditional($conditional); self::assertEquals($expectedWizads[$index], $wizard::class); } + $spreadsheet->disconnectWorksheets(); } public static function conditionalProvider(): array diff --git a/tests/PhpSpreadsheetTests/Style/StyleTest.php b/tests/PhpSpreadsheetTests/Style/StyleTest.php index be67a5e792..6446bcf991 100644 --- a/tests/PhpSpreadsheetTests/Style/StyleTest.php +++ b/tests/PhpSpreadsheetTests/Style/StyleTest.php @@ -24,6 +24,7 @@ public function testStyleOddMethods(): void $styleArray = ['alignment' => ['textRotation' => 45]]; $outArray = $cell1style->getStyleArray($styleArray); self::assertEquals($styleArray, $outArray['quotePrefix']); + $spreadsheet->disconnectWorksheets(); } public function testStyleColumn(): void @@ -58,6 +59,7 @@ public function testStyleColumn(): void self::assertTrue($sheet->getStyle('A1')->getFont()->getItalic()); self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic()); self::assertFalse($sheet->getStyle('C3')->getFont()->getItalic()); + $spreadsheet->disconnectWorksheets(); } public function testStyleIsReused(): void @@ -81,6 +83,7 @@ public function testStyleIsReused(): void $spreadsheet->garbageCollect(); self::assertCount(3, $spreadsheet->getCellXfCollection()); + $spreadsheet->disconnectWorksheets(); } public function testStyleRow(): void @@ -115,6 +118,7 @@ public function testStyleRow(): void self::assertFalse($sheet->getStyle('A1')->getFont()->getItalic()); self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic()); self::assertTrue($sheet->getStyle('C3')->getFont()->getItalic()); + $spreadsheet->disconnectWorksheets(); } public function testIssue1712A(): void @@ -137,6 +141,7 @@ public function testIssue1712A(): void ->setRGB($rgb); self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB()); self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB()); + $spreadsheet->disconnectWorksheets(); } public function testIssue1712B(): void @@ -159,6 +164,7 @@ public function testIssue1712B(): void $sheet->fromArray(['OK', 'KO']); self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB()); self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB()); + $spreadsheet->disconnectWorksheets(); } public function testStyleLoopUpwards(): void @@ -184,6 +190,7 @@ public function testStyleLoopUpwards(): void self::assertFalse($sheet->getStyle('A1')->getFont()->getBold()); self::assertFalse($sheet->getStyle('B2')->getFont()->getBold()); self::assertTrue($sheet->getStyle('C3')->getFont()->getBold()); + $spreadsheet->disconnectWorksheets(); } public function testStyleCellAddressObject(): void @@ -195,6 +202,7 @@ public function testStyleCellAddressObject(): void $style->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDDSLASH); self::assertSame(NumberFormat::FORMAT_DATE_YYYYMMDDSLASH, $style->getNumberFormat()->getFormatCode()); + $spreadsheet->disconnectWorksheets(); } public function testStyleCellRangeObject(): void @@ -208,5 +216,6 @@ public function testStyleCellRangeObject(): void $style->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDDSLASH); self::assertSame(NumberFormat::FORMAT_DATE_YYYYMMDDSLASH, $style->getNumberFormat()->getFormatCode()); + $spreadsheet->disconnectWorksheets(); } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoSizeTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoSizeTest.php index 4a4f488bb3..558590bfb3 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoSizeTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoSizeTest.php @@ -13,16 +13,14 @@ class AutoSizeTest extends TestCase { - protected Spreadsheet $spreadsheet; + private Spreadsheet $spreadsheet; - protected Worksheet $worksheet; + private Worksheet $worksheet; protected function setUp(): void { - parent::setUp(); - - $spreadsheet = new Spreadsheet(); - $this->worksheet = $spreadsheet->getActiveSheet(); + $this->spreadsheet = new Spreadsheet(); + $this->worksheet = $this->spreadsheet->getActiveSheet(); $this->worksheet->setCellValue('A1', 'YEAR') ->setCellValue('B1', 'QUARTER') @@ -44,7 +42,13 @@ protected function setUp(): void } } - protected function setTable(): Table + protected function tearDown(): void + { + $this->spreadsheet->disconnectWorksheets(); + unset($this->spreadsheet, $this->worksheet); + } + + private function setTable(): Table { $table = new Table('A1:D5', 'Sales_Data'); $tableStyle = new TableStyle(); @@ -55,7 +59,7 @@ protected function setTable(): Table return $table; } - protected function readColumnSizes(): array + private function readColumnSizes(): array { $columnSizes = []; $toColumn = $this->worksheet->getHighestColumn(); diff --git a/tests/PhpSpreadsheetTests/Worksheet/CloneTest.php b/tests/PhpSpreadsheetTests/Worksheet/CloneTest.php new file mode 100644 index 0000000000..221ec2fb3b --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/CloneTest.php @@ -0,0 +1,75 @@ +spreadsheet = new Spreadsheet(); + } + + protected function tearDown(): void + { + $this->spreadsheet->disconnectWorksheets(); + unset($this->spreadsheet); + } + + public function testUnattachedIndex(): void + { + $sheet1 = $this->spreadsheet->getActiveSheet(); + $sheet1->getCell('A1')->setValue(10); + $sheet2 = clone $sheet1; + $sheet2->getCell('A1')->setValue(20); + self::assertSame(0, $this->spreadsheet->getIndex($sheet1)); + $idx = $this->spreadsheet->getIndex($sheet2, true); + self::assertSame(-1, $idx); + $sheet2->setTitle('clone'); + $this->spreadsheet->addSheet($sheet2); + $idx = $this->spreadsheet->getIndex($sheet2, true); + self::assertSame(1, $idx); + self::assertSame(10, $this->spreadsheet->getSheet(0)->getCell('A1')->getValue()); + self::assertSame(20, $this->spreadsheet->getSheet(1)->getCell('A1')->getValue()); + } + + public function testGetCloneIndex(): void + { + $this->expectException(SpreadsheetException::class); + $this->expectExceptionMessage('Sheet does not exist'); + $sheet1 = $this->spreadsheet->getActiveSheet(); + $sheet1->getCell('A1')->setValue(10); + $sheet2 = clone $sheet1; + $this->spreadsheet + ->getSheet($this->spreadsheet->getIndex($sheet2)) + ->setCellValue('A1', 100); + } + + public function testSerialize1(): void + { + // If worksheet attached to spreadsheet, can't serialize it. + $this->expectException(SpreadsheetException::class); + $this->expectExceptionMessage('cannot be serialized'); + $sheet1 = $this->spreadsheet->getActiveSheet(); + serialize($sheet1); + } + + public function testSerialize2(): void + { + $sheet1 = new Worksheet(); + $sheet1->getCell('A1')->setValue(10); + $serialized = serialize($sheet1); + /** @var Worksheet */ + $newSheet = unserialize($serialized); + self::assertSame(10, $newSheet->getCell('A1')->getValue()); + self::assertNotEquals($newSheet->getHashInt(), $sheet1->getHashInt()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php b/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php new file mode 100644 index 0000000000..bbb6c8bf00 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/Issue641Test.php @@ -0,0 +1,82 @@ +removeSheetByIndex(0); + $availableWs = []; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Condensed A'); + $worksheet->getCell('A1')->setValue("=SUM('Detailed A'!A1:A10)"); + $worksheet->getCell('A2')->setValue(mt_rand(1, 30)); + $availableWs[] = 'Condensed A'; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Condensed B'); + $worksheet->getCell('A1')->setValue("=SUM('Detailed B'!A1:A10)"); + $worksheet->getCell('A2')->setValue(mt_rand(1, 30)); + $availableWs[] = 'Condensed B'; + + // at this point the value in worksheet 'Condensed B' cell A1 is + // =SUM('Detailed B'!A1:A10) + + // worksheet in question is cloned and totals are attached + $totalWs1 = clone $xlsx->getSheet($xlsx->getSheetCount() - 1); + $totalWs1->setTitle('Condensed Total'); + $xlsx->addSheet($totalWs1); + $formula = '='; + foreach ($availableWs as $ws) { + $formula .= sprintf("+'%s'!A2", $ws); + } + $totalWs1->getCell('A1')->setValue("=SUM('Detailed Total'!A1:A10)"); + $totalWs1->getCell('A2')->setValue($formula); + + $availableWs = []; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Detailed A'); + for ($step = 1; $step <= 10; ++$step) { + $worksheet->getCell("A{$step}")->setValue(mt_rand(1, 30)); + } + $availableWs[] = 'Detailed A'; + + $worksheet = $xlsx->createSheet(); + $worksheet->setTitle('Detailed B'); + for ($step = 1; $step <= 10; ++$step) { + $worksheet->getCell("A{$step}")->setValue(mt_rand(1, 30)); + } + $availableWs[] = 'Detailed B'; + + $totalWs2 = clone $xlsx->getSheet($xlsx->getSheetCount() - 1); + $totalWs2->setTitle('Detailed Total'); + $xlsx->addSheet($totalWs2); + + for ($step = 1; $step <= 10; ++$step) { + $formula = '='; + foreach ($availableWs as $ws) { + $formula .= sprintf("+'%s'!A%s", $ws, $step); + } + $totalWs2->getCell("A{$step}")->setValue($formula); + } + + self::assertSame("=SUM('Detailed A'!A1:A10)", $xlsx->getSheetByName('Condensed A')?->getCell('A1')?->getValue()); + self::assertSame("=SUM('Detailed B'!A1:A10)", $xlsx->getSheetByName('Condensed B')?->getCell('A1')?->getValue()); + self::assertSame("=SUM('Detailed Total'!A1:A10)", $xlsx->getSheetByName('Condensed Total')?->getCell('A1')?->getValue()); + self::assertSame("=+'Detailed A'!A1+'Detailed B'!A1", $xlsx->getSheetByName('Detailed Total')?->getCell('A1')?->getValue()); + + $xlsx->disconnectWorksheets(); + } +}