Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 57c6a62

Browse files
[VarExporter] Fix adding a key to an uninitialized array
1 parent beaef9e commit 57c6a62

File tree

4 files changed

+106
-16
lines changed

4 files changed

+106
-16
lines changed

src/Symfony/Component/VarExporter/LazyGhostTrait.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,37 @@ public function &__get($name): mixed
193193

194194
get_in_scope:
195195

196-
if (null === $scope) {
197-
if (null === $readonlyScope) {
198-
return $this->$name;
196+
try {
197+
if (null === $scope) {
198+
if (null === $readonlyScope) {
199+
return $this->$name;
200+
}
201+
$value = $this->$name;
202+
203+
return $value;
199204
}
200-
$value = $this->$name;
205+
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
201206

202-
return $value;
203-
}
204-
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
207+
return $accessor['get']($this, $name, null !== $readonlyScope);
208+
} catch (\Error $e) {
209+
if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
210+
throw $e;
211+
}
212+
213+
try {
214+
if (null === $scope) {
215+
$this->$name = [];
216+
217+
return $this->$name;
218+
}
205219

206-
return $accessor['get']($this, $name, null !== $readonlyScope);
220+
$accessor['set']($this, $name, []);
221+
222+
return $accessor['get']($this, $name, null !== $readonlyScope);
223+
} catch (\Error) {
224+
throw $e;
225+
}
226+
}
207227
}
208228

209229
public function __set($name, $value): void

src/Symfony/Component/VarExporter/LazyProxyTrait.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,37 @@ public function &__get($name): mixed
130130

131131
get_in_scope:
132132

133-
if (null === $scope) {
134-
if (null === $readonlyScope && 1 !== $parent) {
135-
return $instance->$name;
133+
try {
134+
if (null === $scope) {
135+
if (null === $readonlyScope && 1 !== $parent) {
136+
return $instance->$name;
137+
}
138+
$value = $instance->$name;
139+
140+
return $value;
136141
}
137-
$value = $instance->$name;
142+
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
138143

139-
return $value;
140-
}
141-
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
144+
return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
145+
} catch (\Error $e) {
146+
if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
147+
throw $e;
148+
}
149+
150+
try {
151+
if (null === $scope) {
152+
$instance->$name = [];
153+
154+
return $instance->$name;
155+
}
142156

143-
return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
157+
$accessor['set']($instance, $name, []);
158+
159+
return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
160+
} catch (\Error) {
161+
throw $e;
162+
}
163+
}
144164
}
145165

146166
public function __set($name, $value): void

src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\VarExporter\Internal\LazyObjectState;
16+
use Symfony\Component\VarExporter\ProxyHelper;
1617
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildMagicClass;
1718
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildStdClass;
1819
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildTestClass;
@@ -393,4 +394,41 @@ public function testFullInitializationAfterPartialInitialization()
393394
$this->assertSame(3, ((array) $instance)["\0".TestClass::class."\0private"]);
394395
$this->assertSame(1001, $counter);
395396
}
397+
398+
public function testIndirectModification()
399+
{
400+
$obj = new class() {
401+
public array $foo;
402+
};
403+
$proxy = $this->createLazyGhost($obj::class, fn () => null);
404+
405+
$proxy->foo[] = 123;
406+
407+
$this->assertSame([123], $proxy->foo);
408+
}
409+
410+
/**
411+
* @template T
412+
*
413+
* @param class-string<T> $class
414+
*
415+
* @return T
416+
*/
417+
private function createLazyGhost(string $class, \Closure|array $initializer, array $skippedProperties = null): object
418+
{
419+
$r = new \ReflectionClass($class);
420+
421+
if (str_contains($class, "\0")) {
422+
$class = __CLASS__.'\\'.debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'].'_L'.$r->getStartLine();
423+
class_alias($r->name, $class);
424+
}
425+
$proxy = str_replace($r->name, $class, ProxyHelper::generateLazyGhost($r));
426+
$class = str_replace('\\', '_', $class).'_'.md5($proxy);
427+
428+
if (!class_exists($class, false)) {
429+
eval((\PHP_VERSION_ID >= 80200 && $r->isReadOnly() ? 'readonly ' : '').'class '.$class.' '.$proxy);
430+
}
431+
432+
return $class::createLazyGhost($initializer, $skippedProperties);
433+
}
396434
}

src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@ public function setFoo($foo): static
233233
$this->assertSame(234, $proxy->foo);
234234
}
235235

236+
public function testIndirectModification()
237+
{
238+
$obj = new class() {
239+
public array $foo;
240+
};
241+
$proxy = $this->createLazyProxy($obj::class, fn () => $obj);
242+
243+
$proxy->foo[] = 123;
244+
245+
$this->assertSame([123], $proxy->foo);
246+
}
247+
236248
/**
237249
* @requires PHP 8.2
238250
*/

0 commit comments

Comments
 (0)