PhpSpreadsheet
PhpSpreadsheet copied to clipboard
Error: Typed property PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::$parent must not be accessed before initialization
What is the expected behavior?
private ?Spreadsheet $parent = null;
What is the current behavior?
private ?Spreadsheet $parent;
Which versions of PhpSpreadsheet and PHP are affected?
2.0.0
Do you have code which demonstrates that the current code fails as described?
I have the same issue happening on destruct a mocked Worksheet.
object(TestStub_Worksheet_87f22e33)#748 (39) {
["parent":"PhpOffice\PhpSpreadsheet\Worksheet\Worksheet":private]=>
uninitialized(?PhpOffice\PhpSpreadsheet\Spreadsheet)
...
Error: Typed property PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::$parent must not be accessed before initialization in /app/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php:380
Stack trace:
#0 /app/src/XXXHandler.php(54): PhpOffice\PhpSpreadsheet\Worksheet\Worksheet->__destruct()
#1 /app/tests/XXXHandlerTest.php(105): XXXHandler->__invoke(Object(XXXCommand))
For me it gets fixed by initializing the parent property on class level with null as it's nullable anyway:
BEFORE: private ?Spreadsheet $parent;
AFTER: private ?Spreadsheet $parent = null;
@marc-mabe Can you share more of your code? Based on your description, I tried:
$body = $this->getMockBuilder(Worksheet::class)
->disableOriginalConstructor()
->getMock();
$body = null;sleep(5);
gc_collect_cycles();
It did not throw an exception/error.
Even if the mock method were to work in simulating the problem, I'm just having trouble understanding how it can happen "natively". The first statement in the constructor initializes the property that is supposedly uninitialized. There is no unset statement for the property. How does it ever get to a state where the property is uninitialized?
Hi @oleibman,
I'm also unsure why exactly it happens but I'm not actively creating the mock but a sheet creator service and testing a specific function (creating the worksheet) to be called.
It looks more like this (shorten as much as possible - not tested):
class SheetCreator {
public function createInfoSheet(Spreadsheet $workbook, $info): Worksheet
{
$worksheet = $workbook->createSheet();
$worksheet->setTitle($info);
// ...
return $worksheet;
}
}
class MyHandler {
public function __construct(
private readonly CreateTableHandler $createTableHandler,
private readonly SheetCreator $infoSheetCreator,
) {}
public function __handle(MyCommand $command):void {
$table = $this->createTableHandler->__invoke($command->whatever);
$workbook = TableToPhpSpreadsheet::createSpreadsheet($table);
$this->infoSheetCreator->createInfoSheet($workbook, $info);
// ...
$xlsx = new Xlsx($workbook);
$xlsx->save($file);
}
}
class MyHandlerTest {
public function testHandler(): void
{
$tableCreatorHandlerMock = $this->createMock(CreateTableHandler::class);
$creatorMock = $this->createMock(SheetCreator::class);
$handler = new MyHandler($creatorMock);
// ...
$table = new MyTable();
$tableCreatorHandlerMock
->expects(static::exactly(1))
->method('__invoke')
->with($whatever)
->willReturn($table);
$creatorMock
->expects(static::exactly(1))
->method('createInfoSheet')
->with(static::isInstanceOf(Spreadsheet::class), $info);
// ...
$handler->__invoke($command);
// ....
}
}
I apologize, I am not able to get your code into a state where I can work with it, e.g. CreateTableHandler class is not defined, ditto for MyTable, MyHandler constructor requires 2 arguments but you supply only 1, no idea what command should be on the last line with code, and more. Are you able to flesh it out a bit to something that I can run?
Good news! We currently use Phpunit 9 for our unit tests, but, now that we support only Php 8.1+, Dependabot recommended a switch to Phpunit 10. This requires some changes, mostly to test members. But ... test Reader/Xlsx/AutoFilterTest, which indeed uses mocking, failed with "must not be accessed before initialization"! And the suggested change eliminates that error. So expect a fix when I'm ready to push the Phpunit change.
Ran into this issue today, glad to see it has already been solved ! May i ask if a patch release is scheduled soon ?
New release happened this week.