Skip to content

Commit af490ed

Browse files
Fix Metadata Caching when it changes in EventListeners
1 parent 7e645d0 commit af490ed

File tree

2 files changed

+75
-42
lines changed

2 files changed

+75
-42
lines changed

lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
3535
/** @var ClassMetadata[] */
3636
private $loadedMetadata = [];
3737

38+
/** @var string[] */
39+
protected $aliasesMap = [];
40+
3841
/** @var bool */
3942
protected $initialized = false;
4043

@@ -152,58 +155,35 @@ abstract protected function isEntity(ClassMetadata $class);
152155
*/
153156
public function getMetadataFor($className)
154157
{
158+
$className = $this->getRealClassName($className);
159+
155160
if (isset($this->loadedMetadata[$className])) {
156161
return $this->loadedMetadata[$className];
157162
}
158163

159-
// Check for namespace alias
160-
if (strpos($className, ':') !== false) {
161-
[$namespaceAlias, $simpleClassName] = explode(':', $className, 2);
162-
163-
$realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
164-
} else {
165-
$realClassName = $this->getRealClass($className);
166-
}
167-
168-
if (isset($this->loadedMetadata[$realClassName])) {
169-
// We do not have the alias name in the map, include it
170-
return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
171-
}
172-
173164
$loadingException = null;
174165

175166
try {
176167
if ($this->cacheDriver) {
177-
$cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt);
168+
$cached = $this->cacheDriver->fetch($className . $this->cacheSalt);
178169
if ($cached instanceof ClassMetadata) {
179-
$this->loadedMetadata[$realClassName] = $cached;
170+
$this->loadedMetadata[$className] = $cached;
180171

181172
$this->wakeupReflection($cached, $this->getReflectionService());
182173
} else {
183-
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
184-
$this->cacheDriver->save(
185-
$loadedClassName . $this->cacheSalt,
186-
$this->loadedMetadata[$loadedClassName],
187-
null
188-
);
189-
}
174+
$this->loadMetadata($className);
190175
}
191176
} else {
192-
$this->loadMetadata($realClassName);
177+
$this->loadMetadata($className);
193178
}
194179
} catch (MappingException $loadingException) {
195-
$fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName);
180+
$fallbackMetadataResponse = $this->onNotFoundMetadata($className);
196181

197182
if (! $fallbackMetadataResponse) {
198183
throw $loadingException;
199184
}
200185

201-
$this->loadedMetadata[$realClassName] = $fallbackMetadataResponse;
202-
}
203-
204-
if ($className !== $realClassName) {
205-
// We do not have the alias name in the map, include it
206-
$this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
186+
$this->setMetadataFor($className, $fallbackMetadataResponse);
207187
}
208188

209189
return $this->loadedMetadata[$className];
@@ -218,6 +198,8 @@ public function getMetadataFor($className)
218198
*/
219199
public function hasMetadataFor($className)
220200
{
201+
$className = $this->getRealClassName($className);
202+
221203
return isset($this->loadedMetadata[$className]);
222204
}
223205

@@ -233,7 +215,18 @@ public function hasMetadataFor($className)
233215
*/
234216
public function setMetadataFor($className, $class)
235217
{
218+
$className = $this->getRealClassName($className);
236219
$this->loadedMetadata[$className] = $class;
220+
221+
if ($this->cacheDriver === null) {
222+
return;
223+
}
224+
225+
$this->cacheDriver->save(
226+
$className . $this->cacheSalt,
227+
$this->loadedMetadata[$className],
228+
null
229+
);
237230
}
238231

239232
/**
@@ -303,8 +296,7 @@ protected function loadMetadata($name)
303296
$this->initializeReflection($class, $reflService);
304297

305298
$this->doLoadMetadata($class, $parent, $rootEntityFound, $visited);
306-
307-
$this->loadedMetadata[$className] = $class;
299+
$this->setMetadataFor($className, $class);
308300

309301
$parent = $class;
310302

@@ -399,16 +391,28 @@ public function getReflectionService()
399391
}
400392

401393
/**
402-
* Gets the real class name of a class name that could be a proxy.
394+
* Gets the real class name of a class name that could be a proxy or alias.
403395
*/
404-
private function getRealClass(string $class) : string
396+
protected function getRealClassName(string $className) : string
405397
{
406-
$pos = strrpos($class, '\\' . Proxy::MARKER . '\\');
398+
if (isset($this->aliasesMap[$className])) {
399+
return $this->aliasesMap[$className];
400+
}
407401

408-
if ($pos === false) {
409-
return $class;
402+
switch (true) {
403+
case strpos($className, ':') !== false: // Check for namespace alias
404+
[$namespaceAlias, $simpleClassName] = explode(':', $className, 2);
405+
$realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
406+
break;
407+
case $pos = strrpos($className, '\\' . Proxy::MARKER . '\\') !== false: // Check for namespace proxy
408+
$realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2);
409+
break;
410+
default:
411+
$realClassName = $className;
410412
}
411413

412-
return substr($class, $pos + Proxy::MARKER_LENGTH + 2);
414+
$this->aliasesMap[$className] = $realClassName;
415+
416+
return $realClassName;
413417
}
414418
}

tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use Doctrine\Common\Persistence\Mapping\MappingException;
1111
use Doctrine\Common\Persistence\Mapping\ReflectionService;
1212
use Doctrine\Tests\DoctrineTestCase;
13-
use PHPUnit_Framework_MockObject_MockObject;
13+
use PHPUnit\Framework\MockObject\MockObject;
1414
use stdClass;
1515

1616
/**
@@ -140,7 +140,7 @@ public function testWillFailOnFallbackFailureWithNotLoadedMetadata()
140140
*/
141141
public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances()
142142
{
143-
/** @var Cache|PHPUnit_Framework_MockObject_MockObject $cacheDriver */
143+
/** @var Cache|MockObject $cacheDriver */
144144
$cacheDriver = $this->createMock(Cache::class);
145145

146146
$this->cmf->setCacheDriver($cacheDriver);
@@ -150,7 +150,7 @@ public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances()
150150
/** @var ClassMetadata $metadata */
151151
$metadata = $this->createMock(ClassMetadata::class);
152152

153-
/** @var PHPUnit_Framework_MockObject_MockObject|stdClass|callable $fallbackCallback */
153+
/** @var MockObject|stdClass|callable $fallbackCallback */
154154
$fallbackCallback = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
155155

156156
$fallbackCallback->expects(self::any())->method('__invoke')->willReturn($metadata);
@@ -159,6 +159,35 @@ public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances()
159159

160160
self::assertSame($metadata, $this->cmf->getMetadataFor('Foo'));
161161
}
162+
163+
public function testFallbackMetadataShouldBeCached() : void
164+
{
165+
/** @var Cache|MockObject $cacheDriver */
166+
$cacheDriver = $this->createMock(Cache::class);
167+
$cacheDriver->expects(self::once())->method('save');
168+
169+
$this->cmf->setCacheDriver($cacheDriver);
170+
171+
$classMetadata = $this->createMock(ClassMetadata::class);
172+
173+
$this->cmf->fallbackCallback = static function () use ($classMetadata) {
174+
return $classMetadata;
175+
};
176+
177+
$this->cmf->getMetadataFor('Foo');
178+
}
179+
180+
public function testSetMetadataForShouldUpdateCache() : void
181+
{
182+
/** @var Cache|MockObject $cacheDriver */
183+
$cacheDriver = $this->createMock(Cache::class);
184+
$cacheDriver->expects(self::once())->method('save');
185+
186+
$this->cmf->setCacheDriver($cacheDriver);
187+
188+
$classMetadata = $this->createMock(ClassMetadata::class);
189+
$this->cmf->setMetadataFor('Foo', $classMetadata);
190+
}
162191
}
163192

164193
class TestClassMetadataFactory extends AbstractClassMetadataFactory

0 commit comments

Comments
 (0)