Skip to content

Commit 5972d98

Browse files
HypeMCnicolas-grekas
authored andcommitted
[DoctrineBridge] Fix resetting the manager when using native lazy objects
1 parent 77bcded commit 5972d98

File tree

3 files changed

+99
-18
lines changed

3 files changed

+99
-18
lines changed

src/Symfony/Bridge/Doctrine/ManagerRegistry.php

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,35 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) {
8080
return;
8181
}
8282

83-
try {
84-
$r->resetAsLazyProxy($manager, \Closure::bind(
85-
function () use ($name) {
86-
$name = $this->aliases[$name] ?? $name;
83+
$asProxy = $r->initializeLazyObject($manager) !== $manager;
84+
$initializer = \Closure::bind(
85+
function ($manager) use ($name, $asProxy) {
86+
$name = $this->aliases[$name] ?? $name;
87+
if ($asProxy) {
88+
$manager = false;
89+
}
90+
91+
$manager = match (true) {
92+
isset($this->fileMap[$name]) => $this->load($this->fileMap[$name], $manager),
93+
!$method = $this->methodMap[$name] ?? null => throw new \LogicException(\sprintf('The "%s" service is synthetic and cannot be reset.', $name)),
94+
(new \ReflectionMethod($this, $method))->isStatic() => $this->{$method}($this, $manager),
95+
default => $this->{$method}($manager),
96+
};
97+
98+
if ($asProxy) {
99+
return $manager;
100+
}
101+
},
102+
$this->container,
103+
Container::class
104+
);
87105

88-
return match (true) {
89-
isset($this->fileMap[$name]) => $this->load($this->fileMap[$name], false),
90-
!$method = $this->methodMap[$name] ?? null => throw new \LogicException(\sprintf('The "%s" service is synthetic and cannot be reset.', $name)),
91-
(new \ReflectionMethod($this, $method))->isStatic() => $this->{$method}($this, false),
92-
default => $this->{$method}(false),
93-
};
94-
},
95-
$this->container,
96-
Container::class
97-
));
106+
try {
107+
if ($asProxy) {
108+
$r->resetAsLazyProxy($manager, $initializer);
109+
} else {
110+
$r->resetAsLazyGhost($manager, $initializer);
111+
}
98112
} catch (\Error $e) {
99113
if (__FILE__ !== $e->getFile()) {
100114
throw $e;

src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?php
22

3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
312
namespace Symfony\Bridge\Doctrine\Tests\Fixtures;
413

514
use Doctrine\Persistence\Mapping\ClassMetadata;
@@ -11,6 +20,10 @@ class DummyManager implements ObjectManager
1120
{
1221
public $bar;
1322

23+
public function __construct()
24+
{
25+
}
26+
1427
public function find($className, $id): ?object
1528
{
1629
}

src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
class ManagerRegistryTest extends TestCase
2424
{
25-
public static function setUpBeforeClass(): void
25+
public function testResetService()
2626
{
2727
$container = new ContainerBuilder();
2828

@@ -32,10 +32,7 @@ public static function setUpBeforeClass(): void
3232

3333
$dumper = new PhpDumper($container);
3434
eval('?>'.$dumper->dump(['class' => 'LazyServiceDoctrineBridgeContainer']));
35-
}
3635

37-
public function testResetService()
38-
{
3936
$container = new \LazyServiceDoctrineBridgeContainer();
4037

4138
$registry = new TestManagerRegistry('name', [], ['defaultManager' => 'foo'], 'defaultConnection', 'defaultManager', 'proxyInterfaceName');
@@ -52,6 +49,63 @@ public function testResetService()
5249
$this->assertFalse(isset($foo->bar));
5350
}
5451

52+
/**
53+
* @requires PHP 8.4
54+
*
55+
* @dataProvider provideResetServiceWithNativeLazyObjectsCases
56+
*/
57+
public function testResetServiceWithNativeLazyObjects(string $class)
58+
{
59+
$container = new $class();
60+
61+
$registry = new TestManagerRegistry(
62+
'irrelevant',
63+
[],
64+
['defaultManager' => 'foo'],
65+
'irrelevant',
66+
'defaultManager',
67+
'irrelevant',
68+
);
69+
$registry->setTestContainer($container);
70+
71+
$foo = $container->get('foo');
72+
self::assertSame(DummyManager::class, $foo::class);
73+
74+
$foo->bar = 123;
75+
self::assertTrue(isset($foo->bar));
76+
77+
$registry->resetManager();
78+
79+
self::assertSame($foo, $container->get('foo'));
80+
self::assertSame(DummyManager::class, $foo::class);
81+
self::assertFalse(isset($foo->bar));
82+
}
83+
84+
public static function provideResetServiceWithNativeLazyObjectsCases(): iterable
85+
{
86+
$container = new ContainerBuilder();
87+
88+
$container->register('foo', DummyManager::class)->setPublic(true);
89+
$container->getDefinition('foo')->setLazy(true);
90+
$container->compile();
91+
92+
$dumper = new PhpDumper($container);
93+
94+
eval('?>'.$dumper->dump(['class' => 'NativeLazyServiceDoctrineBridgeContainer']));
95+
96+
yield ['NativeLazyServiceDoctrineBridgeContainer'];
97+
98+
$dumps = $dumper->dump(['class' => 'NativeLazyServiceDoctrineBridgeContainerAsFiles', 'as_files' => true]);
99+
100+
$lastDump = array_pop($dumps);
101+
foreach (array_reverse($dumps) as $dump) {
102+
eval('?>'.$dump);
103+
}
104+
eval('?>'.$lastDump);
105+
106+
yield ['NativeLazyServiceDoctrineBridgeContainerAsFiles'];
107+
}
108+
55109
/**
56110
* When performing an entity manager lazy service reset, the reset operations may re-use the container
57111
* to create a "fresh" service: when doing so, it can happen that the "fresh" service is itself a proxy.

0 commit comments

Comments
 (0)