fix: proxy with BackedEnum integer on identifier#997
fix: proxy with BackedEnum integer on identifier#997Gwemox wants to merge 1 commit intodoctrine:3.4.xfrom
Conversation
| * @param ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $type | ||
| * | ||
| */ | ||
| public static function containsEnumType($type): bool |
There was a problem hiding this comment.
I would type the argument as ReturnType|null, to match the one of ReflectionMethod::getReturnType
There was a problem hiding this comment.
Union types are only allowed since PHP 8.0.
The PHP min version for this project is 7.1.
src/Util/ClassUtils.php
Outdated
|
|
||
| if ($type instanceof ReflectionUnionType || $type instanceof ReflectionIntersectionType) { | ||
| foreach ($type->getTypes() as $unionedType) { | ||
| if (static::containsEnumType($unionedType)) { |
There was a problem hiding this comment.
I would use self:: here. there is no reason to enable late static binding.
4f878f3 to
5b54ff3
Compare
malarzm
left a comment
There was a problem hiding this comment.
Whichever way we go, this PR will need test to prevent regressions in the future :)
| $fieldType = $class->getTypeOfField($identifier); | ||
| $cast = in_array($fieldType, ['integer', 'smallint'], true) ? '(int) ' : ''; | ||
|
|
||
| if (ClassUtils::containsEnumType($method->getReturnType())) { |
There was a problem hiding this comment.
I'm afraid this is only half-baked solution as without method's return type we still do not know what's inside. Maybe it'd be more efficient to generate an instanceof \BackedEnum check for the return statement?
There was a problem hiding this comment.
Check instance in template, like this ?
from:
if ($this->__isInitialized__ === false) {
return (int) parent::getId();
}
to:
if ($this->__isInitialized__ === false) {
return parent::getId() instanceof \BackedEnum ? parent::getId() : (int) parent::getId();
}
| if (ClassUtils::containsEnumType($method->getReturnType())) { | ||
| $cast = ''; | ||
| } else { | ||
| $cast = in_array($fieldType, ['integer', 'smallint'], true) ? '(int) ' : ''; |
There was a problem hiding this comment.
Or better yet: idea that needs somebody with a deeper ORM knowledge than me to be validated: what if we get rid of the cast instead? The proxied entity will have the field already casted to a correct type thanks to initial id hydration. This $cast construct here is 10 years old and removing it doesn't break any test so I think it's worth challenging.
There was a problem hiding this comment.
Yes I think that's the best way.
If the method is typed, for the scalar types PHP already cast return value if needed?
There was a problem hiding this comment.
@malarzm AbstractProxyFactory::getProxy is responsible for setting the id field on the proxy, its not gone through hydration, instead it might just be the value passed to EntityManager::getReference($entity, $id) without casting. So getting rid of the cast would require to move the cast to a higher level in the ORM. Same for UnitOfWork::createEntity using getProxy, associated ids don't have types and are not converted to int for example.
A test in ORM would be:
$proxy = $em->getReference(CmsUser, "1");
$proxy->getId();Unsure if we test this at the moment.
There was a problem hiding this comment.
Thanks @beberlei I'll try to have a closer look there in ORM 👍
There was a problem hiding this comment.
Quoting myself from #998
Probably we won't end with this exact solution as it seems ORM has no tests for this behaviour either. The change looks OK ORM-wise, but I'm afraid it may hit users in an unexpected and hard to debug way.
Sadly I didn't have time to get back to this :(
If entity identifier return an int BackedEnum, Proxy Generator generate method with cast to int. But it's impossible to cast a BackedEnum to int.
Call
getId()on proxy throw an error :Warning: Object of class App\\Enum\\ProviderId could not be converted to intOriginal entity:
Generated proxy method before fix:
Generated proxy method after fix: