-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #4324. Serialization was explicity forbidden by PR #3199. This was in response to several issues, and concern that the Spreadsheet object contained non-serializable properties. This PR restores the ability to serialize a spreadsheet. Json serialization remains unsupported. Fix #1757, closed in Nov. 2023 but just reopened. At the time, Cell property `formulaAttributes` was stored as a SimpleXmlElement. Dynamic arrays PR #3962 defined that property as `null|array<string, string>` in the doc block. However, it left the formal Php type for the property as `mixed`. This PR changes the formal type to `?array`. Fix #1741, closed in Dec. 2020 but just reopened. Calculation property `referenceHelper` was defined as static, and, since static properties don't take part in serialization, this caused a problem after unserialization. There are at least 3 trivial ways to deal with this - make it an instance property, reinitialize it when unserialized using a wakeup method, or remove the property altogether. This PR uses the last of those 3. Calculation does have other static properties. Almost all of these deal with locale. So serialize/unserialize might wind up using a default locale when non-default is desired (but not necessarily required). If that is a problem for end-users, it will be a new one, and I will work on a solution if and when the time comes. Static property `returnArrayAsType` is potentially problematic. However, instance property `instanceArrayReturnType` is the preferred method of handling this, and using that will avoid any problems. Issue #932 also dealt with serialization. I do not have the wherewithal to investigate that issue. If it is not solved by this and the earlier PR's, I will have to leave it to others to re-raise it. Spreadsheet `copy` is now simplified to use serialize followed by unserialize. Formal tests are added. In addition, I have made a number of informal tests on very complicated spreadsheets, and it has performed correctly for all of them.
- Loading branch information
Showing
7 changed files
with
117 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpOffice\PhpSpreadsheetTests; | ||
|
||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException; | ||
use PhpOffice\PhpSpreadsheet\Helper\Sample; | ||
use PhpOffice\PhpSpreadsheet\NamedRange; | ||
use PhpOffice\PhpSpreadsheet\Spreadsheet; | ||
use PHPUnit\Framework\Attributes; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class SpreadsheetSerializeTest extends TestCase | ||
{ | ||
private ?Spreadsheet $spreadsheet = null; | ||
|
||
protected function tearDown(): void | ||
{ | ||
if ($this->spreadsheet !== null) { | ||
$this->spreadsheet->disconnectWorksheets(); | ||
$this->spreadsheet = null; | ||
} | ||
} | ||
|
||
public function testSerialize(): void | ||
{ | ||
$this->spreadsheet = new Spreadsheet(); | ||
$sheet = $this->spreadsheet->getActiveSheet(); | ||
$sheet->getCell('A1')->setValue(10); | ||
|
||
$serialized = serialize($this->spreadsheet); | ||
$newSpreadsheet = unserialize($serialized); | ||
self::assertInstanceOf(Spreadsheet::class, $newSpreadsheet); | ||
self::assertNotSame($this->spreadsheet, $newSpreadsheet); | ||
$newSheet = $newSpreadsheet->getActiveSheet(); | ||
self::assertSame(10, $newSheet->getCell('A1')->getValue()); | ||
$newSpreadsheet->disconnectWorksheets(); | ||
} | ||
|
||
public function testNotJsonEncodable(): void | ||
{ | ||
$this->spreadsheet = new Spreadsheet(); | ||
|
||
$this->expectException(SpreadsheetException::class); | ||
$this->expectExceptionMessage('Spreadsheet objects cannot be json encoded'); | ||
json_encode($this->spreadsheet); | ||
} | ||
|
||
/** | ||
* These tests are a bit weird. | ||
* If prepareSerialize and readSerialize are run in the same | ||
* process, the latter's assertions will always succeed. | ||
* So to demonstrate that the | ||
* problem is solved, they need to run in separate processes. | ||
* But then they can't share the file name. So we need to send | ||
* the file to a semi-hard-coded destination. | ||
*/ | ||
private static function getTempFileName(): string | ||
{ | ||
$helper = new Sample(); | ||
|
||
return $helper->getTemporaryFolder() . '/spreadsheet.serialize.test.txt'; | ||
} | ||
|
||
public function testPrepareSerialize(): void | ||
{ | ||
$this->spreadsheet = new Spreadsheet(); | ||
$sheet = $this->spreadsheet->getActiveSheet(); | ||
$this->spreadsheet->addNamedRange(new NamedRange('summedcells', $sheet, '$A$1:$A$5')); | ||
$sheet->setCellValue('A1', 1); | ||
$sheet->setCellValue('A2', 2); | ||
$sheet->setCellValue('A3', 3); | ||
$sheet->setCellValue('A4', 4); | ||
$sheet->setCellValue('A5', 5); | ||
$sheet->setCellValue('C1', '=SUM(summedcells)'); | ||
$ser = serialize($this->spreadsheet); | ||
$this->spreadsheet->disconnectWorksheets(); | ||
$outputFileName = self::getTempFileName(); | ||
self::assertNotFalse( | ||
file_put_contents($outputFileName, $ser) | ||
); | ||
} | ||
|
||
#[Attributes\RunInSeparateProcess] | ||
public function testReadSerialize(): void | ||
{ | ||
$inputFileName = self::getTempFileName(); | ||
$ser = (string) file_get_contents($inputFileName); | ||
unlink($inputFileName); | ||
$this->spreadsheet = unserialize($ser); | ||
$sheet = $this->spreadsheet->getActiveSheet(); | ||
self::assertSame('=SUM(summedcells)', $sheet->getCell('C1')->getValue()); | ||
self::assertSame(15, $sheet->getCell('C1')->getCalculatedValue()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters