doctrine/orm · error · LogicException

Doctrine ORM does not support property hook on %s::%s withou

Error message

Doctrine ORM does not support property hook on %s::%s without using native lazy objects. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.

What it means

Doctrine's reflection-based lazy ghosts/proxies cannot intercept PHP 8.4 property hooks. When ProxyFactory builds a proxy (on first lazy load or getReference()) it walks every property of the class, and on PHP >= 8.4 any property with hooks makes it throw immediately — unless native lazy objects are enabled, which use the engine's lazy-object mechanism that supports hooks. Tracked in issue #11624.

Source

Thrown at src/Proxy/ProxyFactory.php:341

        return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . InternalProxy::MARKER
            . str_replace('\\', '', $className) . '.php';
    }

    private function getProxyFactory(string $className): Closure
    {
        $skippedProperties = [];
        $class             = $this->em->getClassMetadata($className);
        $identifiers       = array_flip($class->getIdentifierFieldNames());
        $filter            = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
        $reflector         = $class->getReflectionClass();

        while ($reflector) {
            foreach ($reflector->getProperties($filter) as $property) {
                $name = $property->name;

                if (PHP_VERSION_ID >= 80400 && count($property->getHooks()) > 0) {
                    throw new LogicException(sprintf(
                        'Doctrine ORM does not support property hook on %s::%s without using native lazy objects. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.',
                        $property->getDeclaringClass()->getName(),
                        $property->getName(),
                    ));
                }

                if ($property->isStatic() || ! isset($identifiers[$name])) {
                    continue;
                }

                $prefix = $property->isPrivate() ? "\0" . $property->class . "\0" : ($property->isProtected() ? "\0*\0" : '');

                $skippedProperties[$prefix . $name] = true;
            }

            $filter    = ReflectionProperty::IS_PRIVATE;
            $reflector = $reflector->getParentClass();
        }

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. Enable native lazy objects: $config->enableNativeLazyObjects(true) (requires doctrine/orm >= 3.3 with PHP >= 8.4)
  2. Remove property hooks from proxied entity classes
  3. Upgrade doctrine/orm to a release supporting property hooks (see issue #11624) and follow its upgrade notes

Example fix

// before (PHP 8.4, no native lazy objects)
public string $name {
    set (string $value) { $this->name = trim($value); }
}
// $em->getReference(User::class, 1) throws

// after
$config = ORMSetup::createAttributeMetadataConfiguration([...], true);
$config->enableNativeLazyObjects(true);
Defensive patterns

Strategy: validation

Validate before calling

if (PHP_VERSION_ID >= 80400) {
    foreach ((new ReflectionClass($entityClass))->getProperties() as $prop) {
        if (count($prop->getHooks()) > 0 && ! $config->isNativeLazyObjectsEnabled()) {
            throw new RuntimeException('Enable native lazy objects or remove hooks on ' . $entityClass);
        }
    }
}

Type guard

function entityHasPropertyHooks(string $class): bool
{
    if (PHP_VERSION_ID < 80400) { return false; }
    foreach ((new ReflectionClass($class))->getProperties() as $p) {
        if (count($p->getHooks()) > 0) { return true; }
    }
    return false;
}

Prevention

When it happens

Trigger: PHP >= 8.4, an entity (or its parent classes) declares a property with a get/set hook, and a proxy gets created for it — e.g., $em->getReference(User::class, $id) or accessing a lazy to-one association — while native lazy objects are NOT enabled via Configuration::enableNativeLazyObjects(true) (ORM 3.3+).

Common situations: Upgrading PHP to 8.4 and adopting property hooks (virtual hooks, `set` validation) on entities with existing lazy associations; new projects on 8.4 using hooks before enabling the ORM's native lazy object support.

Related errors


AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21). Data as JSON: /api/errors/8aaf7fb693f006e9. Report an issue: GitHub.