88use Doctrine \Persistence \Mapping \ClassMetadata ;
99use Doctrine \Persistence \Mapping \Driver \MappingDriver ;
1010use Doctrine \Persistence \Mapping \MappingException ;
11+ use Doctrine \Persistence \Mapping \ReflectionService ;
1112use Doctrine \Tests \DoctrineTestCase ;
13+ use RuntimeException ;
1214
1315final class AbstractClassMetadataFactoryTest extends DoctrineTestCase
1416{
17+ /** @param ClassMetadata<object>|null $metadata */
18+ private function createTestFactory (
19+ MappingDriver |null $ driver = null ,
20+ ClassMetadata |null $ metadata = null ,
21+ ): TestAbstractClassMetadataFactory {
22+ return new TestAbstractClassMetadataFactory ($ driver , $ metadata );
23+ }
24+
1525 public function testItSkipsTransientClasses (): void
1626 {
17- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class);
18- $ cmf
19- ->method ('newClassMetadataInstance ' )
20- ->withConsecutive ([SomeGrandParentEntity::class], [SomeEntity::class])
21- ->willReturnOnConsecutiveCalls (
22- $ this ->createMock (ClassMetadata::class),
23- $ this ->createMock (ClassMetadata::class),
24- );
2527 $ driver = $ this ->createMock (MappingDriver::class);
26- $ cmf ->method ('getDriver ' )
27- ->willReturn ($ driver );
28+ $ cmf = $ this ->createTestFactory ($ driver );
29+
30+ $ metadataCallCount = 0 ;
31+ $ cmf ->newClassMetadataInstanceCallback = function ($ className ) use (&$ metadataCallCount ) {
32+ $ metadataCallCount ++;
33+ if ($ metadataCallCount === 1 ) {
34+ self ::assertEquals (SomeGrandParentEntity::class, $ className );
35+ } elseif ($ metadataCallCount === 2 ) {
36+ self ::assertEquals (SomeEntity::class, $ className );
37+ }
2838
39+ return $ this ->createMock (ClassMetadata::class);
40+ };
41+
42+ $ driverCallCount = 0 ;
2943 $ driver ->expects (self ::exactly (2 ))
3044 ->method ('isTransient ' )
31- ->withConsecutive (
32- [SomeGrandParentEntity::class],
33- [SomeParentEntity::class],
34- )
35- ->willReturnOnConsecutiveCalls (false , true );
45+ ->willReturnCallback (static function ($ className ) use (&$ driverCallCount ) {
46+ $ driverCallCount ++;
47+ if ($ driverCallCount === 1 ) {
48+ self ::assertEquals (SomeGrandParentEntity::class, $ className );
49+
50+ return false ;
51+ }
52+
53+ if ($ driverCallCount === 2 ) {
54+ self ::assertEquals (SomeParentEntity::class, $ className );
55+
56+ return true ;
57+ }
58+ });
3659
3760 $ cmf ->getMetadataFor (SomeEntity::class);
3861 }
3962
4063 public function testItThrowsWhenAttemptingToGetMetadataForAnonymousClass (): void
4164 {
42- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class );
65+ $ cmf = $ this ->createTestFactory ( );
4366 $ this ->expectException (MappingException::class);
4467 $ cmf ->getMetadataFor ((new class {
4568 })::class);
4669 }
4770
4871 public function testAnonymousClassIsNotMistakenForShortAlias (): void
4972 {
50- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class);
73+ $ driver = $ this ->createMock (MappingDriver::class);
74+ $ driver ->method ('isTransient ' )->willReturn (false );
75+ $ cmf = $ this ->createTestFactory ($ driver );
5176
5277 self ::assertFalse ($ cmf ->isTransient ((new class () {
5378 })::class));
5479 }
5580
5681 public function testItThrowsWhenAttemptingToGetMetadataForShortAlias (): void
5782 {
58- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class );
83+ $ cmf = $ this ->createTestFactory ( );
5984 $ this ->expectException (MappingException::class);
6085 // @phpstan-ignore-next-line
6186 $ cmf ->getMetadataFor ('App:Test ' );
6287 }
6388
6489 public function testItThrowsWhenAttemptingToCheckTransientForShortAlias (): void
6590 {
66- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class );
91+ $ cmf = $ this ->createTestFactory ( );
6792 $ this ->expectException (MappingException::class);
6893 // @phpstan-ignore-next-line
6994 $ cmf ->isTransient ('App:Test ' );
7095 }
7196
7297 public function testItGetsTheSameMetadataForBackslashedClassName (): void
7398 {
74- $ cmf = $ this ->getMockForAbstractClass (AbstractClassMetadataFactory::class);
75- $ cmf
76- ->method ('newClassMetadataInstance ' )
77- ->with (SomeOtherEntity::class)
78- ->willReturn (
79- self ::createStub (ClassMetadata::class),
80- );
99+ $ driver = $ this ->createMock (MappingDriver::class);
100+ $ cmf = $ this ->createTestFactory ($ driver );
101+
102+ $ metadata = self ::createStub (ClassMetadata::class);
103+ $ cmf ->newClassMetadataInstanceCallback = static function ($ className ) use ($ metadata ) {
104+ self ::assertEquals (SomeOtherEntity::class, $ className );
105+
106+ return $ metadata ;
107+ };
81108
82109 self ::assertSame ($ cmf ->getMetadataFor (SomeOtherEntity::class), $ cmf ->getMetadataFor ('\\' . SomeOtherEntity::class));
83110 }
@@ -98,3 +125,61 @@ final class SomeEntity extends SomeParentEntity
98125final class SomeOtherEntity
99126{
100127}
128+
129+ /** @template-extends AbstractClassMetadataFactory<ClassMetadata<object>> */
130+ class TestAbstractClassMetadataFactory extends AbstractClassMetadataFactory
131+ {
132+ /** @var callable|null */
133+ public $ newClassMetadataInstanceCallback ;
134+
135+ /** @param ClassMetadata<object>|null $defaultMetadata */
136+ public function __construct (
137+ private MappingDriver |null $ driver = null ,
138+ private ClassMetadata |null $ defaultMetadata = null ,
139+ ) {
140+ }
141+
142+ protected function initialize (): void
143+ {
144+ $ this ->initialized = true ;
145+ }
146+
147+ protected function getDriver (): MappingDriver
148+ {
149+ return $ this ->driver ?? throw new RuntimeException ('Driver not set ' );
150+ }
151+
152+ protected function wakeupReflection (ClassMetadata $ class , ReflectionService $ reflService ): void
153+ {
154+ // No-op for tests
155+ }
156+
157+ protected function initializeReflection (ClassMetadata $ class , ReflectionService $ reflService ): void
158+ {
159+ // No-op for tests
160+ }
161+
162+ protected function isEntity (ClassMetadata $ class ): bool
163+ {
164+ return true ;
165+ }
166+
167+ /** @param list<class-string> $nonSuperclassParents */
168+ protected function doLoadMetadata (
169+ ClassMetadata $ class ,
170+ ClassMetadata |null $ parent ,
171+ bool $ rootEntityFound ,
172+ array $ nonSuperclassParents ,
173+ ): void {
174+ // No-op for tests - metadata loading is handled by driver
175+ }
176+
177+ protected function newClassMetadataInstance (string $ className ): ClassMetadata
178+ {
179+ if ($ this ->newClassMetadataInstanceCallback !== null ) {
180+ return ($ this ->newClassMetadataInstanceCallback )($ className );
181+ }
182+
183+ return $ this ->defaultMetadata ?? throw new RuntimeException ('Default metadata not set ' );
184+ }
185+ }
0 commit comments