-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DX][DI] Add compiler pass which check extistence of service class name #11315
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
[DX][DI] Add compiler pass which check extistence of service class name #11315
Conversation
l3l0
commented
Jul 5, 2014
Q | A |
---|---|
Bug fix? | no |
New feature? | yes |
BC breaks? | not sure - I hope that no |
Deprecations? | no |
Tests pass? | yes |
Fixed tickets | #11301 |
License | MIT |
Nice work! So, now the question is, does this break BC? Is there a situation where class will not exist during the compiler process but exist later at run-time? |
Just my 2 cents about naming :) I would use 'class' rather than 'name' to be consistent with the "class" configuration of the service : Anyway, good job! |
@pyrech good catch :) I changed names. |
IMO it's not a BC break, but in reality it is for those who like hacking Sf, maybe somebody that read a lot of issue about DI will be able to confirm. 👍 Anyway, nice work :) . |
How this change affects performance? Looks like it will loads all of the classes. If yes - imho better add console commands for checking. |
@Koc all classes should be loaded when container is compiled (not on every request), so probably can affect stuff in dev/debug mode IHMO. For prod configuration it should be fine. But I have not done any performance checks, so I cannot answer the question how this can affect performance. |
I forgot about this, sorry. It occurs only when tracked resources was changed. |
array('foo', '\LogicException', 'Unable to parse the controller name "foo".'), | ||
array('foo::bar', '\InvalidArgumentException', 'Class "foo" does not exist.'), | ||
array('myfoo', '\LogicException', 'Unable to parse the controller name "myfoo".'), | ||
array('myfoo::bar', '\InvalidArgumentException', 'Class "myfoo" does not exist.'), |
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.
why changing these tests ?
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.
Cause when we run all symfony test suites at once foo class exist (cause of this probably https://github.com/l3l0/symfony/blob/feature/better-error-on-bad-service-class-name-issue-11301/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/ProjectExtension.php#L16)
I think this should run after the removing passes. It does not make sense to throw an exception for a service which gets removed |
@stof I added checking class for factory_class. I moved compiler pass to part running after the removing passes - this somehow force me to resolve class name again using ParameterBag. |
@@ -53,7 +53,7 @@ public function __construct() | |||
new ResolveInvalidReferencesPass(), | |||
new AnalyzeServiceReferencesPass(true), | |||
new CheckCircularReferencesPass(), | |||
new CheckReferenceValidityPass(), | |||
new CheckReferenceValidityPass() |
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.
Please don't remove the trailing comma in multiline arrays. Our coding standards require them
factory_class name (using class_exists() ) if that is possible - definition is not syntetic or is not definition via factory method.
What about adding check for existence of factory method? |
What if I use a factory method and my class is not a class but an interface? This is perfectly legit right now and can cause a BC break if not allowed any more. See #11279 services:
session.flash_bag:
class: Symfony\Component\HttpFoundation\Session\FlashBagInterface
factory_service: session
factory_method: getFlashBag |
$key, | ||
$id | ||
) | ||
); |
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.
this should be on one line.
I like to give interfaces in my factory service definitions, so I'd vote for adding The reason for configuring an interface instead of a class is proper autocompletion (coding against an interface yada yada yada). |
The only pending issues that has been added on this PR are
@l3l0 Can you make those changes? We're in the last week before 2.6 feature freeze and I would love to see if we can get this considered for a merge :). |
We must also add support for the new way to register factories. See #12008 |
@l3l0 If you don't have time in the next coming days, I can also finish the PR for you, just let me know. |
The problem I can see is memory consumption as when compiling the container, it will load all classes referenced in the container. So, we should check that first as I fear it's going to slow down the very first request a lot. |
@fabpot this will likely have an impact in dev indeed. It needs to be measured. |
Ah, good point about performance! I would love to have this better error message, but certainly not if it significantly increases memory or the loading of the container in the dev environment. |
@fabpot Sure I haven't time recently and probably will be better if you take care about it :) |
I've just tested the impact on the memory and I think the increase is not acceptable. On a barebone Symfony Standard Edition project, it takes 20MB of memory to run So, except if we only run this when debug is enabled, I'm 👎 on this change. FWIW, the patch does not work as is as it does not take into account interfaces: diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckServiceClassPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckServiceClassPass.php
index 1f0897e..71427b0 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/CheckServiceClassPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckServiceClassPass.php
@@ -31,11 +31,17 @@ class CheckServiceClassPass implements CompilerPassInterface
{
$parameterBag = $container->getParameterBag();
foreach ($container->getDefinitions() as $id => $definition) {
- if ($this->allowsToCheckClassExistenceForClass($definition) && !class_exists($parameterBag->resolveValue($definition->getClass()))) {
- throw new BadServiceClassException($id, $definition->getClass(), 'class');
+ if ($this->allowsToCheckClassExistenceForClass($definition)) {
+ $class = $parameterBag->resolveValue($definition->getClass());
+ if (!class_exists($class) && !interface_exists($class) && !trait_exists($class)) {
+ throw new BadServiceClassException($id, $definition->getClass(), 'class');
+ }
}
- if ($this->allowsToCheckClassExistenceForFactoryClass($definition) && !class_exists($parameterBag->resolveValue($definition->getFactoryClass()))) {
- throw new BadServiceClassException($id, $definition->getFactoryClass(), 'factory_class');
+ if ($this->allowsToCheckClassExistenceForFactoryClass($definition)) {
+ $class = $parameterBag->resolveValue($definition->getFactoryClass());
+ if (!class_exists($class) && !interface_exists($class) && !trait_exists($class)) {
+ throw new BadServiceClassException($id, $definition->getFactoryClass(), 'factory_class');
+ }
}
}
} |
I suggest closing this in favor of https://github.com/matthiasnoback/symfony-service-definition-validator/ which covers this kind of validation already, as well as a bunch of other rules (it checks that the mandatory arguments are passed for instance). |