-
-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: proxy with BackedEnum integer on identifier #997
base: 3.4.x
Are you sure you want to change the base?
Conversation
* @param ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $type | ||
* | ||
*/ | ||
public static function containsEnumType($type): bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would type the argument as ReturnType|null
, to match the one of ReflectionMethod::getReturnType
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use self::
here. there is no reason to enable late static binding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, It's corrected
4f878f3
to
5b54ff3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whichever way we go, this PR will need test to prevent regressions in the future :)
@@ -940,7 +941,12 @@ private function generateMethods(ClassMetadata $class) | |||
if ($this->isShortIdentifierGetter($method, $class)) { | |||
$identifier = lcfirst(substr($name, 3)); | |||
$fieldType = $class->getTypeOfField($identifier); | |||
$cast = in_array($fieldType, ['integer', 'smallint'], true) ? '(int) ' : ''; | |||
|
|||
if (ClassUtils::containsEnumType($method->getReturnType())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @beberlei I'll try to have a closer look there in ORM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@malarzm any news on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I help ?
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 int
Original entity:
Generated proxy method before fix:
Generated proxy method after fix: