diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 3f9249ced..5b20e91dd 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -316,4 +316,16 @@ class Axis extends Properties { return $this->noFill; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->majorGridlines = ($this->majorGridlines === null) ? null : clone $this->majorGridlines; + $this->majorGridlines = ($this->minorGridlines === null) ? null : clone $this->minorGridlines; + $this->axisText = ($this->axisText === null) ? null : clone $this->axisText; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/AxisText.php b/src/PhpSpreadsheet/Chart/AxisText.php index e06a2c88a..09d6b2a5e 100644 --- a/src/PhpSpreadsheet/Chart/AxisText.php +++ b/src/PhpSpreadsheet/Chart/AxisText.php @@ -51,4 +51,13 @@ class AxisText extends Properties return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->font = clone $this->font; + } } diff --git a/src/PhpSpreadsheet/Chart/Chart.php b/src/PhpSpreadsheet/Chart/Chart.php index 2b8de5d51..70f6b4a5f 100644 --- a/src/PhpSpreadsheet/Chart/Chart.php +++ b/src/PhpSpreadsheet/Chart/Chart.php @@ -835,4 +835,21 @@ class Chart { return $this->renderedHeight; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->worksheet = null; + $this->title = ($this->title === null) ? null : clone $this->title; + $this->legend = ($this->legend === null) ? null : clone $this->legend; + $this->xAxisLabel = ($this->xAxisLabel === null) ? null : clone $this->xAxisLabel; + $this->yAxisLabel = ($this->yAxisLabel === null) ? null : clone $this->yAxisLabel; + $this->plotArea = ($this->plotArea === null) ? null : clone $this->plotArea; + $this->xAxis = clone $this->xAxis; + $this->yAxis = clone $this->yAxis; + $this->borderLines = clone $this->borderLines; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index 647579345..b8656ad5d 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -408,4 +408,31 @@ class DataSeries } } } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $plotLabels = $this->plotLabel; + $this->plotLabel = []; + foreach ($plotLabels as $plotLabel) { + $this->plotLabel[] = $plotLabel; + } + $plotCategories = $this->plotCategory; + $this->plotCategory = []; + foreach ($plotCategories as $plotCategory) { + $this->plotCategory[] = clone $plotCategory; + } + $plotValues = $this->plotValues; + $this->plotValues = []; + foreach ($plotValues as $plotValue) { + $this->plotValues[] = clone $plotValue; + } + $plotBubbleSizes = $this->plotBubbleSizes; + $this->plotBubbleSizes = []; + foreach ($plotBubbleSizes as $plotBubbleSize) { + $this->plotBubbleSizes[] = clone $plotBubbleSize; + } + } } diff --git a/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/src/PhpSpreadsheet/Chart/DataSeriesValues.php index 72bbf8103..b31714b72 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -587,4 +587,29 @@ class DataSeriesValues extends Properties { return $this->trendLines; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->markerFillColor = clone $this->markerFillColor; + $this->markerBorderColor = clone $this->markerBorderColor; + if (is_array($this->fillColor)) { + $fillColor = $this->fillColor; + $this->fillColor = []; + foreach ($fillColor as $color) { + $this->fillColor[] = clone $color; + } + } elseif ($this->fillColor instanceof ChartColor) { + $this->fillColor = clone $this->fillColor; + } + $this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout; + $trendLines = $this->trendLines; + $this->trendLines = []; + foreach ($trendLines as $trendLine) { + $this->trendLines[] = clone $trendLine; + } + } } diff --git a/src/PhpSpreadsheet/Chart/Layout.php b/src/PhpSpreadsheet/Chart/Layout.php index f64f53319..97bbc8fd8 100644 --- a/src/PhpSpreadsheet/Chart/Layout.php +++ b/src/PhpSpreadsheet/Chart/Layout.php @@ -528,4 +528,15 @@ class Layout return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->labelFillColor = ($this->labelFillColor === null) ? null : clone $this->labelFillColor; + $this->labelBorderColor = ($this->labelBorderColor === null) ? null : clone $this->labelBorderColor; + $this->labelFont = ($this->labelFont === null) ? null : clone $this->labelFont; + $this->labelEffects = ($this->labelEffects === null) ? null : clone $this->labelEffects; + } } diff --git a/src/PhpSpreadsheet/Chart/Legend.php b/src/PhpSpreadsheet/Chart/Legend.php index 7c5fa1833..44c098d95 100644 --- a/src/PhpSpreadsheet/Chart/Legend.php +++ b/src/PhpSpreadsheet/Chart/Legend.php @@ -175,4 +175,15 @@ class Legend return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $this->legendText = ($this->legendText === null) ? null : clone $this->legendText; + $this->borderLines = clone $this->borderLines; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/PlotArea.php b/src/PhpSpreadsheet/Chart/PlotArea.php index aa72fda16..5064d5a0d 100644 --- a/src/PhpSpreadsheet/Chart/PlotArea.php +++ b/src/PhpSpreadsheet/Chart/PlotArea.php @@ -201,4 +201,17 @@ class PlotArea return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $plotSeries = $this->plotSeries; + $this->plotSeries = []; + foreach ($plotSeries as $series) { + $this->plotSeries[] = clone $series; + } + } } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index 740702d12..8074df678 100644 --- a/src/PhpSpreadsheet/Chart/Properties.php +++ b/src/PhpSpreadsheet/Chart/Properties.php @@ -957,4 +957,14 @@ abstract class Properties { return $this->getLineStyleProperty(['arrow', $arrow, 'len']); } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->lineColor = clone $this->lineColor; + $this->glowColor = clone $this->glowColor; + $this->shadowColor = clone $this->shadowColor; + } } diff --git a/src/PhpSpreadsheet/Chart/Title.php b/src/PhpSpreadsheet/Chart/Title.php index 378987446..e9c42a4c4 100644 --- a/src/PhpSpreadsheet/Chart/Title.php +++ b/src/PhpSpreadsheet/Chart/Title.php @@ -163,4 +163,22 @@ class Title return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $this->font = ($this->font === null) ? null : clone $this->font; + if (is_array($this->caption)) { + $captions = $this->caption; + $this->caption = []; + foreach ($captions as $caption) { + $this->caption[] = is_object($caption) ? (clone $caption) : $caption; + } + } else { + $this->caption = is_object($this->caption) ? (clone $this->caption) : $this->caption; + } + } } diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index ea26b8ce6..78b3f0dc1 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -878,4 +878,14 @@ class Font extends Supervisor { return $this->cap; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->color = clone $this->color; + $this->chartColor = ($this->chartColor === null) ? null : clone $this->chartColor; + $this->underlineColor = ($this->underlineColor === null) ? null : clone $this->underlineColor; + } } diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index d572f6ccc..54b9a70e6 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -3739,10 +3739,8 @@ class Worksheet implements IComparable $currentCollection = $this->drawingCollection; $this->drawingCollection = new ArrayObject(); foreach ($currentCollection as $item) { - if (is_object($item)) { - $newDrawing = clone $item; - $newDrawing->setWorksheet($this); - } + $newDrawing = clone $item; + $newDrawing->setWorksheet($this); } } elseif ($key == 'tableCollection') { $currentCollection = $this->tableCollection; @@ -3752,6 +3750,13 @@ class Worksheet implements IComparable $newTable->setName($item->getName() . 'clone'); $this->addTable($newTable); } + } elseif ($key == 'chartCollection') { + $currentCollection = $this->chartCollection; + $this->chartCollection = new ArrayObject(); + foreach ($currentCollection as $item) { + $newChart = clone $item; + $this->addChart($newChart); + } } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) { $newAutoFilter = clone $this->autoFilter; $this->autoFilter = $newAutoFilter; diff --git a/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php new file mode 100644 index 000000000..55c35e6d9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php @@ -0,0 +1,164 @@ +setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + self::assertSame('ffffff', $chart->getFillColor()->getValue()); + self::assertSame('srgbClr', $chart->getFillColor()->getType()); + self::assertSame('d9d9d9', $chart->getBorderLines()->getLineColorProperty('value')); + self::assertSame('srgbClr', $chart->getBorderLines()->getLineColorProperty('type')); + self::assertEqualsWithDelta(9360 / Properties::POINTS_WIDTH_MULTIPLIER, $chart->getBorderLines()->getLineStyleProperty('width'), 1.0E-8); + self::assertTrue($chart->getChartAxisY()->getNoFill()); + self::assertFalse($chart->getChartAxisX()->getNoFill()); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithLegendAndTitle(): void + { + $file = self::DIRECTORY . '32readwriteChartWithImages1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + self::assertNotNull($chart->getLegend()); + self::assertNotSame($chart->getLegend(), $oldChart->getLegend()); + self::assertNotNull($chart->getTitle()); + self::assertNotSame($chart->getTitle(), $oldChart->getTitle()); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithBubbleSizes(): void + { + $file = self::DIRECTORY . '32readwriteBubbleChart2.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + $oldGroup = $oldChart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($oldGroup); + self::assertCount(1, $oldGroup); + $oldSizes = $oldGroup[0]->getPlotBubbleSizes(); + self::assertCount(2, $oldSizes); + + $plotGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($plotGroup); + self::assertCount(1, $plotGroup); + $bubbleSizes = $plotGroup[0]->getPlotBubbleSizes(); + self::assertCount(2, $bubbleSizes); + self::assertNotSame($bubbleSizes, $oldSizes); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithTrendLines(): void + { + $file = self::DIRECTORY . '32readwriteScatterChartTrendlines1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(2, $oldCharts); + $oldChart = $oldCharts[1]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(2, $charts); + $chart = $charts[1]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + $oldGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($oldGroup); + self::assertCount(1, $oldGroup); + $oldLabels = $oldGroup[0]->getPlotLabels(); + self::assertCount(1, $oldLabels); + self::assertCount(3, $oldLabels[0]->getTrendLines()); + + $plotGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($plotGroup); + self::assertCount(1, $plotGroup); + $plotLabels = $plotGroup[0]->getPlotLabels(); + self::assertCount(1, $plotLabels); + self::assertCount(3, $plotLabels[0]->getTrendLines()); + + $spreadsheet->disconnectWorksheets(); + } +}