Skip to content
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

Prohibits class typo in the discriminator map #8122

Merged
merged 5 commits into from Jul 5, 2020

Conversation

gquemener
Copy link
Contributor

When a defined subclass has a case typo, the query builder will be lost
and will generate exotic table alias. This commit fixes the issue at the
root by prohibiting case typo in discriminator map.

See #8112 for the consequence of
such typo.

@gquemener gquemener changed the title Prevents incorrect table aliases to be generated Prohibits class typo in the discriminator map Apr 23, 2020
@gquemener
Copy link
Contributor Author

This PR is better than #8112 in term of DX as it will raise errors way before any sql query will hit the DB. Pointing the user exactly where the issue is.

@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch from 75ffa10 to 0d2c8d5 Compare April 23, 2020 09:12
$refl = new ReflectionClass($className);
if ($refl->name !== $className) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm opened to suggestion if you see a more efficient way to grab actual class name without using reflection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I'm not sure efficiency is really researched here, as metadata are cached.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the reflection API performance are that bad, and since this is about metadata, there is probably caching after this, right? So it's not a hot path, and it's cached, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's my belief as well 👍

@gquemener
Copy link
Contributor Author

This change could probably be considered as a BC break.

@@ -1002,6 +1003,11 @@ public function addDiscriminatorMapClass($name, string $className) : void
throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
}

$refl = new ReflectionClass($className);
Copy link
Contributor Author

@gquemener gquemener Apr 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throws ReflectionException if the class to reflect does not exist. Source

It won't as we check for class existence on line 1001.

greg0ire
greg0ire previously approved these changes Apr 24, 2020
Copy link
Member

@greg0ire greg0ire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A+ commit message, and nice tests! I'm unsure if this should really be considered a BC-break. If it is, we should probably trigger a deprecation but that sounds weird to me. In any case, I would target the bugfix branch (so 2.7)

$refl = new ReflectionClass($className);
if ($refl->name !== $className) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the reflection API performance are that bad, and since this is about metadata, there is probably caching after this, right? So it's not a hot path, and it's cached, isn't it?

@gquemener gquemener changed the base branch from master to 2.7 April 24, 2020 18:03
@gquemener gquemener dismissed greg0ire’s stale review April 24, 2020 18:03

The base branch was changed.

@gquemener gquemener changed the base branch from 2.7 to master April 24, 2020 18:03
@greg0ire
Copy link
Member

Why did you change it back?

@gquemener
Copy link
Contributor Author

Switching the base branch to 2.7 showed 500+ commits to merge. Let me work on that.

@greg0ire
Copy link
Member

git rebase --onto origin/2.7 origin/master prevents-sub-class-case-difference && git push --force ;)

@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch from 0d2c8d5 to cad84ac Compare April 24, 2020 19:24
@gquemener gquemener changed the base branch from master to 2.7 April 24, 2020 19:25
@gquemener
Copy link
Contributor Author

Done, thanks for the hint 👍

I'm curious, how would you transpose this fix into master? I've noticed quite a few changes that'll make it non trivial (ClassMetadataInfo has been renamed ClassMetadata and a few metadata drivers have been removed).

@greg0ire
Copy link
Member

I'm curious about that too to be honest 😅

greg0ire
greg0ire previously approved these changes Apr 24, 2020
@greg0ire
Copy link
Member

greg0ire commented May 3, 2020

The build fails, please run vendor/bin/phpcbf

@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch from 33a3e2d to 41ef28f Compare May 3, 2020 14:38
@gquemener
Copy link
Contributor Author

gquemener commented May 3, 2020

Done ✔️

@greg0ire greg0ire added the Bug label May 3, 2020
greg0ire
greg0ire previously approved these changes May 3, 2020
Copy link
Member

@greg0ire greg0ire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

In any case, I would target the bugfix branch (so 2.7)

Correction: if someone feels there is a BC-break and this should instead be a deprecation, then 2.8 should be targeted. Otherwise, 2.7 is fine.

SenseException
SenseException previously approved these changes May 4, 2020
Copy link
Member

@SenseException SenseException left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -2816,6 +2816,11 @@ public function addDiscriminatorMapClass($name, $className)
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}

$refl = new ReflectionClass($className);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to swoop in so late, but loading ReflectionClass inside ClassMetadataInfo is not allowed from architecture POV. This code doesn't work with static runtime reflection, which will break detached metadata use cases (such as everything with the EntityGenerator).

Instead this check is something you should put either into ClassMetadataFactory if we want this to fail at runtime, or in the SchemaValidator if we want this to fail only in debug/dev mode.

Copy link
Member

@beberlei beberlei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right place for validation is ClassMetadataFactory::validateRuntimeMetadata

@gquemener
Copy link
Contributor Author

The right place for validation is ClassMetadataFactory::validateRuntimeMetadata

Thanks for pointing this out.
I'll check this out, hopefully soon :)

@gquemener gquemener dismissed stale reviews from SenseException and greg0ire via f93336e May 13, 2020 19:48
@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch 2 times, most recently from 476a1c6 to 701b50a Compare May 13, 2020 20:11
When a defined subclass has a case typo, the query builder will be lost
and will generate exotic table alias. This commit fixes the issue at the
root by prohibiting case typo in discriminator map.

See doctrine#8112 for the consequence of
such typo.
The Cube class must be autoloaded with the correct case, otherwise
composer autoloader will complain.
@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch from 701b50a to 3b85ec8 Compare May 13, 2020 20:13
@gquemener gquemener force-pushed the prevents-sub-class-case-difference branch from 3b85ec8 to 2daee11 Compare May 15, 2020 08:23
@gquemener
Copy link
Contributor Author

gquemener commented May 15, 2020

I'm wondering if this case sensitivity check should not be performed more globally as class_exists and interface_exists are used in other places.

The underlying issue is that once the class has been included once (FYI, composer autoload will not autoload class with incorrect case, as it does not respect psr-0/4 AFAIK), class_exists and interface_exists will behave in a case insensitive manner.

In the case of the discriminator map, this will generate incorrect sql statement because the system won't be able to determine the correct table alias. I'm wondering if a similar issue could not appear anywhere where (class|interface)_exists is used 🤔

I'm thinking about associations (targetEntity with incorrect case), entity definition (name with incorrect case), ...

I need to take some more time to investigate, any hint is welcome in the meantime :)

@beberlei beberlei merged commit 64c3f68 into doctrine:2.7 Jul 5, 2020
beberlei added a commit that referenced this pull request Jul 5, 2020
@beberlei beberlei added this to the 2.7.4 milestone Jul 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants