Skip to content

Commit 048947e

Browse files
wnasichPowerKiKi
authored andcommitted
Avoid memory exhaustion when cloning worksheet with a drawing
When cloning `BaseDrawing`, its worksheet will be set as null and thus be orphaned. But when cloning the worksheet, it will re-assign itself as the new worksheet for the BaseDrawing. That way we avoid recursive cloning of a Worksheet that would clone a BaseDrawing that would clone a Worksheet etc. Fixes #437 Fixes #613
1 parent 1b96c95 commit 048947e

File tree

5 files changed

+50
-6
lines changed

5 files changed

+50
-6
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
6565
- Could not open CSV file containing HTML fragment - [#564](https://github.com/PHPOffice/PhpSpreadsheet/issues/564)
6666
- Exclude the vendor folder in migration - [#481](https://github.com/PHPOffice/PhpSpreadsheet/issues/481)
6767
- Chained operations on cell ranges involving borders operated on last cell only [#428](https://github.com/PHPOffice/PhpSpreadsheet/issues/428)
68+
- Avoid memory exhaustion when cloning worksheet with a drawing [#437](https://github.com/PHPOffice/PhpSpreadsheet/issues/437)
6869

6970
## [1.3.1] - 2018-06-12
7071

src/PhpSpreadsheet/Worksheet/AutoFilter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ public function __clone()
841841
$vars = get_object_vars($this);
842842
foreach ($vars as $key => $value) {
843843
if (is_object($value)) {
844-
if ($key == 'workSheet') {
844+
if ($key === 'workSheet') {
845845
// Detach from worksheet
846846
$this->{$key} = null;
847847
} else {

src/PhpSpreadsheet/Worksheet/BaseDrawing.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,9 @@ public function __clone()
509509
{
510510
$vars = get_object_vars($this);
511511
foreach ($vars as $key => $value) {
512-
if (is_object($value)) {
512+
if ($key == 'worksheet') {
513+
$this->worksheet = null;
514+
} elseif (is_object($value)) {
513515
$this->$key = clone $value;
514516
} else {
515517
$this->$key = $value;

src/PhpSpreadsheet/Worksheet/Worksheet.php

+5-4
Original file line numberDiff line numberDiff line change
@@ -2963,13 +2963,14 @@ public function __clone()
29632963
$newCollection = $this->cellCollection->cloneCellCollection($this);
29642964
$this->cellCollection = $newCollection;
29652965
} elseif ($key == 'drawingCollection') {
2966-
$newCollection = new ArrayObject();
2967-
foreach ($this->drawingCollection as $id => $item) {
2966+
$currentCollection = $this->drawingCollection;
2967+
$this->drawingCollection = new ArrayObject();
2968+
foreach ($currentCollection as $item) {
29682969
if (is_object($item)) {
2969-
$newCollection[$id] = clone $this->drawingCollection[$id];
2970+
$newDrawing = clone $item;
2971+
$newDrawing->setWorksheet($this);
29702972
}
29712973
}
2972-
$this->drawingCollection = $newCollection;
29732974
} elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) {
29742975
$newAutoFilter = clone $this->autoFilter;
29752976
$this->autoFilter = $newAutoFilter;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
4+
5+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
6+
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class DrawingTest extends TestCase
10+
{
11+
public function testCloningWorksheetWithImages()
12+
{
13+
$spreadsheet = new Spreadsheet();
14+
$aSheet = $spreadsheet->getActiveSheet();
15+
16+
$gdImage = @imagecreatetruecolor(120, 20);
17+
$textColor = imagecolorallocate($gdImage, 255, 255, 255);
18+
imagestring($gdImage, 1, 5, 5, 'Created with PhpSpreadsheet', $textColor);
19+
20+
$drawing = new MemoryDrawing();
21+
$drawing->setName('In-Memory image 1');
22+
$drawing->setDescription('In-Memory image 1');
23+
$drawing->setCoordinates('A1');
24+
$drawing->setImageResource($gdImage);
25+
$drawing->setRenderingFunction(
26+
MemoryDrawing::RENDERING_JPEG
27+
);
28+
$drawing->setMimeType(MemoryDrawing::MIMETYPE_DEFAULT);
29+
$drawing->setHeight(36);
30+
$drawing->setWorksheet($aSheet);
31+
32+
$originDrawingCount = count($aSheet->getDrawingCollection());
33+
$clonedWorksheet = clone $aSheet;
34+
$clonedDrawingCount = count($clonedWorksheet->getDrawingCollection());
35+
36+
self::assertEquals($originDrawingCount, $clonedDrawingCount);
37+
self::assertNotSame($aSheet, $clonedWorksheet);
38+
self::assertNotSame($aSheet->getDrawingCollection(), $clonedWorksheet->getDrawingCollection());
39+
}
40+
}

0 commit comments

Comments
 (0)