Summary
php bin/console cache:clear crashes with a ReflectionException when the oro_entity_config
database table contains entity class names that no longer exist as PHP files.
This happens naturally when switching git branches where different bundles are present.
The error completely blocks cache clearing with no hint about the cause or fix.
Steps to reproduce
- Install Oro with a custom bundle (e.g.
AcmeShipmentBundle) that has entities registered
in oro_entity_config via migrations
- Switch to a git branch that does not include that bundle
- Run
composer dump-autoload
- Run
php bin/console cache:clear
Actual Result
Cache clear aborts with:
In AbstractManagerRegistry.php line 175:
Class "Acme\Bundle\ShipmentBundle\Entity\Delivery" does not exist
Full stack trace:
ReflectionClass->__construct()
at vendor/doctrine/persistence/src/Persistence/AbstractManagerRegistry.php:175
Doctrine\Persistence\AbstractManagerRegistry->getManagerForClass()
at vendor/oro/platform/src/Oro/Bundle/EntityBundle/ORM/Registry.php:79
Oro\Bundle\EntityBundle\ORM\Registry->getManagerForClass()
at vendor/oro/marketing/src/Oro/Bundle/MarketingListBundle/Provider/ContactInformationExclusionProvider.php:56
Oro\Bundle\MarketingListBundle\Provider\ContactInformationExclusionProvider->isIgnoredEntity()
at vendor/oro/platform/src/Oro/Bundle/EntityBundle/Provider/ChainExclusionProvider.php:37
Oro\Bundle\EntityBundle\Provider\ChainExclusionProvider->isIgnoredEntity()
at vendor/oro/marketing/src/Oro/Bundle/MarketingActivityBundle/Provider/MarketingActivityVirtualRelationProvider.php:66
Oro\Bundle\MarketingActivityBundle\Provider\MarketingActivityVirtualRelationProvider->getVirtualRelations()
at vendor/oro/platform/src/Oro/Bundle/EntityBundle/Provider/ChainVirtualRelationProvider.php:56
Oro\Bundle\EntityBundle\Provider\ChainVirtualRelationProvider->getVirtualRelations()
at vendor/oro/platform/src/Oro/Bundle/EntityConfigBundle/Config/ConfigCacheWarmer.php:285
Oro\Bundle\EntityConfigBundle\Config\ConfigCacheWarmer->loadVirtualFields()
at vendor/oro/platform/src/Oro/Bundle/EntityConfigBundle/Config/ConfigCacheWarmer.php:93
Oro\Bundle\EntityConfigBundle\Config\ConfigCacheWarmer->warmUpCache()
at vendor/oro/platform/src/Oro/Bundle/EntityConfigBundle/Cache/CacheWarmer.php:21
Root cause: ConfigCacheWarmer::loadVirtualFields() iterates every class in oro_entity_config
and calls Registry::getManagerForClass($class) on each one.
That method delegates to AbstractManagerRegistry which calls new ReflectionClass($class)
with no class_exists() guard. When $class no longer exists, PHP throws a
ReflectionException and aborts the entire warmup.
// Oro\Bundle\EntityBundle\ORM\Registry — no guard before delegating to parent
public function getManagerForClass($class)
{
if (\array_key_exists($class, $this->managersMap)) {
return $this->cachedManagers[$this->managersMap[$class]];
}
$manager = parent::getManagerForClass($class); // crashes if class is missing
...
}
// Doctrine\Persistence\AbstractManagerRegistry — detonator
public function getManagerForClass(string $class)
{
$proxyClass = new ReflectionClass($class); // ReflectionException if class gone
...
}
Expected Result
cache:clear should complete successfully.
A class that no longer exists has no Doctrine manager — getManagerForClass() should
return null for it (consistent with its existing contract for unknown classes)
rather than throwing.
Suggested fix in Oro\Bundle\EntityBundle\ORM\Registry::getManagerForClass():
public function getManagerForClass($class)
{
if (\array_key_exists($class, $this->managersMap)) {
return $this->cachedManagers[$this->managersMap[$class]];
}
// Guard: oro_entity_config is DB-driven and can contain stale class references
// (e.g. after switching git branches or removing a bundle).
// Return null instead of crashing — no manager exists for a non-existent class.
if (!class_exists($class) && !interface_exists($class, false)) {
$this->managersMap[$class] = '';
$this->cachedManagers[''] = null;
return null;
}
$manager = parent::getManagerForClass($class);
$hash = null !== $manager ? spl_object_hash($manager) : '';
$this->managersMap[$class] = $hash;
$this->cachedManagers[$hash] = $manager;
return $manager;
}
This fix belongs in Oro\Bundle\EntityBundle\ORM\Registry (Oro's own code, not Doctrine)
because Oro drives getManagerForClass() from a database-sourced class list
(oro_entity_config) that can legitimately contain stale references.
Doctrine's expectation that callers pass valid class names is reasonable;
Oro must guard its own DB-driven usage.
Details about your environment
- OroPlatform version: 6.1.0
- PHP version: 8.2
- Database: PostgreSQL 14
Additional information
Workaround until fixed — remove the stale rows manually:
SELECT id, class_name FROM oro_entity_config WHERE class_name LIKE '%RemovedBundleName%';
DELETE FROM oro_entity_config WHERE class_name LIKE '%RemovedBundleName%';
Then: composer dump-autoload && php bin/console cache:clear
This issue affects any developer doing routine branch switching across feature branches
that add or remove bundles — a very common workflow on multi-team Oro projects.
Summary
php bin/console cache:clearcrashes with aReflectionExceptionwhen theoro_entity_configdatabase table contains entity class names that no longer exist as PHP files.
This happens naturally when switching git branches where different bundles are present.
The error completely blocks cache clearing with no hint about the cause or fix.
Steps to reproduce
AcmeShipmentBundle) that has entities registeredin
oro_entity_configvia migrationscomposer dump-autoloadphp bin/console cache:clearActual Result
Cache clear aborts with:
Full stack trace:
Root cause:
ConfigCacheWarmer::loadVirtualFields()iterates every class inoro_entity_configand calls
Registry::getManagerForClass($class)on each one.That method delegates to
AbstractManagerRegistrywhich callsnew ReflectionClass($class)with no
class_exists()guard. When$classno longer exists, PHP throws aReflectionExceptionand aborts the entire warmup.Expected Result
cache:clearshould complete successfully.A class that no longer exists has no Doctrine manager —
getManagerForClass()shouldreturn
nullfor it (consistent with its existing contract for unknown classes)rather than throwing.
Suggested fix in
Oro\Bundle\EntityBundle\ORM\Registry::getManagerForClass():This fix belongs in
Oro\Bundle\EntityBundle\ORM\Registry(Oro's own code, not Doctrine)because Oro drives
getManagerForClass()from a database-sourced class list(
oro_entity_config) that can legitimately contain stale references.Doctrine's expectation that callers pass valid class names is reasonable;
Oro must guard its own DB-driven usage.
Details about your environment
Additional information
Workaround until fixed — remove the stale rows manually:
Then:
composer dump-autoload && php bin/console cache:clearThis issue affects any developer doing routine branch switching across feature branches
that add or remove bundles — a very common workflow on multi-team Oro projects.