-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Don't stop PSR-4 service discovery if a parent class is missing #25932
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
Conversation
derrabus
commented
Jan 25, 2018
•
edited
Loading
edited
Q | A |
---|---|
Branch? | 3.4 |
Bug fix? | yes |
New feature? | no |
BC breaks? | no |
Deprecations? | no |
Tests pass? | yes |
Fixed tickets | #25929 |
License | MIT |
Doc PR | N/A |
0efe70e
to
d0f53e3
Compare
try { | ||
$r = $this->container->getReflectionClass($class); | ||
} catch (\ReflectionException $e) { | ||
$classes[$class] = $e->getMessage(); |
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.
It's a little awkward to return an array with the error as the key, but since this method is private, it seems safe.
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.
Actually, the class is the key here. I agree that the structure I'm using now is a bit more awkward than the old one, but as you said, the method is private, so it's a purely internal data structure.
That being said, if you have a suggestion regarding a better data structure, I'm all ears. 😃
foreach ($classes as $class) { | ||
foreach ($classes as $class => $errorMessage) { | ||
if (null !== $errorMessage) { | ||
$definition = new Definition($class); |
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 is a nit-pick, but what happens of $class
is actually an alias? I imagine it would make no difference: if that Definition is ultimately used (likely due to autowiring), it would throw the error.
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 don't expect this case to be any different than with an alias to a non-broken class. But I'll test it.
foreach ($classes as $class => $errorMessage) { | ||
if (null !== $errorMessage) { | ||
$definition = new Definition($class); | ||
$definition->addError($errorMessage); |
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.
We need to think about this error message holistically :). Currently, the user will see:
Class App\Foo\MissingParentClass not found
I think we need to add more... as much as possible really. Something like:
Error when loading registering services from resource "../src/*": an error was thrown when processing the class "App\Foo\ActualClass": "Class App\Foo\MissingParentClass not found".
I would LOVE to even say the line number in YAML or XML... but we definitely do not know that :p (pipe dream for someday?)
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 code was more or less meant as a POC. I'll see what I can do about improving the DX.
Well-done @derrabus! The question is: feature or bug-fix? It's a safe change: I mean, anyone with bad classes is currently getting an error... and this just makes it not an error. I vote bug fix - we'll see what other people think. |
Agree on it being a bugfix. It doesn't introduce a new public-facing API, it just fixes incorrect behavior. |
@@ -60,7 +60,16 @@ public function registerClasses(Definition $prototype, $namespace, $resource, $e | |||
$interfaces = array(); | |||
$singlyImplemented = array(); | |||
|
|||
foreach ($classes as $class) { | |||
foreach ($classes as $class => $errorMessage) { | |||
if (null !== $errorMessage) { |
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.
what about putting this "if" just after the existing $this->setDefinition($class, unserialize($serializedPrototype));
line?
no need to explicitly create a new definition, the prototype-based one is better, isn't it?
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 agree about the prototype definition. About moving this block down: I'd like to avoid the interface_exists()
call below, so the block should probably stay here.
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'd like to avoid the interface_exists() call below
is there any reason for that? I'm not sure this is a valid concern :)
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 expected the interface_exists
call to trigger the autoloader again, but appearently it doesn't. I'm moving the block.
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.
The call uses false
as second argument, precisely to avoid the autoloading call.
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.
Exactly. Block moved. ✅
d0f53e3
to
08e17eb
Compare
@nicolas-grekas, @stof: Do you agree with @weaverryan on turning this PR into a bugfix? I'd try to apply my patch against 3.4 then. |
I do, go for 3.4 on my side |
08e17eb
to
12d93fc
Compare
12d93fc
to
bc34213
Compare
I've added some context to the error message extracted from the exception. Please have a look @weaverryan. |
I've set up a When I link the project to the code of this PR, the console is executed without failure. Now, if I add a public alias to the service discovered from my broken class, the console fails again. |
bc34213
to
ba512d9
Compare
The AppVeyor failure looks unrelated. |
$r = $this->container->getReflectionClass($class); | ||
} catch (\ReflectionException $e) { | ||
$classes[$class] = sprintf( | ||
'While discovering services from namespace "%s", an error was thrown when processing the class "%s": %s', |
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.
nit pick :). I think we usually try to end errors with a period. So the end of this could be:
... processing the class "%s": "%s".
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.
✅
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.
Nice job!
ba512d9
to
3d6c3ba
Compare
Thank you @derrabus. |
…ssing (derrabus) This PR was merged into the 3.4 branch. Discussion ---------- Don't stop PSR-4 service discovery if a parent class is missing | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #25929 | License | MIT | Doc PR | N/A Commits ------- 3d6c3ba Don't stop PSR-4 service discovery if a parent class is missing.
not sure if it's related to this, however I'm not able to run "composer update --no-dev"
the output:
|
You're looking for #41673 |
thanks @nicolas-grekas . I'll try not to do this again |