Description
Symfony version(s) affected
7.0.3
Description
heyho
(this was tested with php 8.3.3 and php 8.3.2)
while enabling lazyghosts with doctrine i noticed some weird errors in production directly after cache-warmup. after some tinkering around i came to the conclusion, that the cache contents are just wrong while having jit enabled.
i tried to reduce the project as far as i could, while still beeing able to reproduce the error which i am going to outline here:
sample code: https://github.com/verfriemelt-dot-org/lazyghost-trait-bug
while loading the cached objects here
public static function getClassResetters($class)
{
$classProperties = [];
if ((self::$classReflectors[$class] ??= new \ReflectionClass($class))->isInternal()) {
$propertyScopes = [];
} else {
$propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class);
}
it would receive weird data defined and will eventually fail here:
withIn LazyGhostTrait.php line 47:
[Error]
Value of type null is not callable
after some trail and error getting to now the error, i noticed that this is dependend on the current jit settings with php.
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=256M
opcache.jit=on
on is the same as tracing for that matter. if you run make function
in the provided project, you'll get a cached result looking like this:
private const LAZY_OBJECT_PROPERTY_SCOPES = [
"\0".parent::class."\0".'apps' => [parent::class, 'apps', null],
"\0".parent::class."\0".'code' => [parent::class, 'code', null],
"\0".parent::class."\0".'config' => [parent::class, 'config', null],
"\0".parent::class."\0".'created' => [parent::class, 'created', null],
"\0".parent::class."\0".'currentRevisionNumber' => [parent::class, 'currentRevisionNumber', null],
"\0".parent::class."\0".'inUse' => [parent::class, 'inUse', null],
"\0".parent::class."\0".'name' => [parent::class, 'name', null],
"\0".parent::class."\0".'token' => [parent::class, 'token', null],
"\0".parent::class."\0".'updated' => [parent::class, 'updated', null],
'apps' => [parent::class, 'apps', null],
'code' => [parent::class, 'code', null],
'config' => [parent::class, 'config', null],
'created' => [parent::class, 'created', null],
'currentRevisionNumber' => [parent::class, 'currentRevisionNumber', null],
'inUse' => [parent::class, 'inUse', null],
'name' => [parent::class, 'name', null],
'token' => [parent::class, 'token', null],
'updated' => [parent::class, 'updated', null],
];
which works as expected. while running make tracing
we get this wrong defintion.
private const LAZY_OBJECT_PROPERTY_SCOPES = [
"\0".parent::class."\0".'apps' => [parent::class, 'apps', 'apps'],
"\0".parent::class."\0".'code' => [parent::class, 'code', 'code'],
"\0".parent::class."\0".'config' => [parent::class, 'config', 'config'],
"\0".parent::class."\0".'created' => [parent::class, 'created', 'created'],
"\0".parent::class."\0".'currentRevisionNumber' => [parent::class, 'currentRevisionNumber', 'currentRevisionNumber'],
"\0".parent::class."\0".'inUse' => [parent::class, 'inUse', 'inUse'],
"\0".parent::class."\0".'name' => [parent::class, 'name', 'name'],
"\0".parent::class."\0".'token' => [parent::class, 'token', 'token'],
"\0".parent::class."\0".'updated' => [parent::class, 'updated', 'updated'],
'apps' => [parent::class, 'apps', 'apps'],
'code' => [parent::class, 'code', 'code'],
'config' => [parent::class, 'config', 'config'],
'created' => [parent::class, 'created', 'created'],
'currentRevisionNumber' => [parent::class, 'currentRevisionNumber', 'currentRevisionNumber'],
'inUse' => [parent::class, 'inUse', 'inUse'],
'name' => [parent::class, 'name', 'name'],
'token' => [parent::class, 'token', 'token'],
'updated' => [parent::class, 'updated', 'updated'],
];
the line beeing responsible for that can be found within Hydrator::getPropertyScopes()
located here: https://github.com/symfony/symfony/blob/7.1/src/Symfony/Component/VarExporter/Internal/Hydrator.php#L265
if (\ReflectionProperty::IS_PRIVATE & $flags) {
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $property];
continue;
}
while toying around with that, i got it working as expected with pulling the ternary operation out of the line:
if (\ReflectionProperty::IS_PRIVATE & $flags) {
$tmp = null;
if ($flags & \ReflectionProperty::IS_READONLY) {
$tmp = $class;
}
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $tmp, $property];
continue;
}
will work just fine.
also
if (\ReflectionProperty::IS_PRIVATE & $flags) {
$propertyScopes["\0$class\0$name"] = $propertyScopes[$name] = [$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $property];
var_dump([$class, $name, $flags & \ReflectionProperty::IS_READONLY ? $class : null, $property]);
continue;
}
will segfault with my system 🤔 so actually i suspect a php jit bug here.
i hope we can resolv that issue. it was quite some work to track that down
How to reproduce
checkout the linked repo and the makefile within
Possible Solution
No response
Additional Context
No response