diff --git a/app/config/eccube/packages/doctrine.yaml b/app/config/eccube/packages/doctrine.yaml index f8805bcbe44..cf9c34df96a 100644 --- a/app/config/eccube/packages/doctrine.yaml +++ b/app/config/eccube/packages/doctrine.yaml @@ -30,6 +30,12 @@ doctrine: auto_generate_proxy_classes: '%kernel.debug%' naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true + # EccubeBundle の Entity は Kernel::addEntityExtensionPass が TraitProxyAttributeDriver で + # 明示登録する。auto_mapping が同じ src/Eccube/Entity を素の AttributeDriver でも登録すると、 + # そちらが Entity ソースを無条件に require_once するため、Proxy (app/proxy/entity) を + # ロード済みの状態で "Cannot redeclare class" となる (Entity の class_exists ガード全廃後). + mappings: + EccubeBundle: false controller_resolver: auto_mapping: false dql: diff --git a/tests/Eccube/Tests/Doctrine/ORM/Mapping/EccubeEntityMetadataDriverTest.php b/tests/Eccube/Tests/Doctrine/ORM/Mapping/EccubeEntityMetadataDriverTest.php new file mode 100644 index 00000000000..046f30e9a6b --- /dev/null +++ b/tests/Eccube/Tests/Doctrine/ORM/Mapping/EccubeEntityMetadataDriverTest.php @@ -0,0 +1,101 @@ +get(EntityManagerInterface::class); + $driver = $entityManager->getConfiguration()->getMetadataDriverImpl(); + + $coreEntityDir = realpath(__DIR__.'/../../../../../../src/Eccube/Entity'); + $this->assertIsString($coreEntityDir); + + $coreEntityDrivers = []; + foreach ($this->flattenDrivers($driver) as $each) { + if (!method_exists($each, 'getPaths')) { + continue; + } + foreach ($each->getPaths() as $path) { + if (realpath($path) !== $coreEntityDir) { + continue; + } + $coreEntityDrivers[] = $each; + $this->assertInstanceOf(TraitProxyAttributeDriver::class, $each, 'src/Eccube/Entity は TraitProxyAttributeDriver 以外から登録してはならない' + .' (素の AttributeDriver は Entity ソースを無条件に require_once するため、' + .'Proxy ロード済みの環境で "Cannot redeclare class" になる)'); + } + } + + // 対象ドライバが 0 件でもループが素通りするため、明示登録そのものが失われた構成も検知する + $this->assertNotEmpty( + $coreEntityDrivers, + 'src/Eccube/Entity のマッピングドライバ (Kernel::addEntityExtensionPass の TraitProxyAttributeDriver) が登録されていない' + ); + } + + /** + * MappingDriverChain を再帰的に展開して、実際にマッピングを解決するドライバを列挙する. + * + * @return list + */ + private function flattenDrivers(?MappingDriver $driver): array + { + if ($driver === null) { + return []; + } + + // doctrine-bundle の MappingDriver は実ドライバをラップしているため中身を取り出す + if ($driver instanceof BundleMappingDriver) { + return $this->flattenDrivers($driver->getDriver()); + } + + if (!$driver instanceof MappingDriverChain) { + return [$driver]; + } + + $drivers = []; + foreach ($driver->getDrivers() as $each) { + $drivers = [...$drivers, ...$this->flattenDrivers($each)]; + } + + return [...$drivers, ...$this->flattenDrivers($driver->getDefaultDriver())]; + } +}