Skip to content

[DI] Add runtime service exceptions to improve the error message when controller arguments cannot be injected #26627

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

Merged
merged 1 commit into from
Mar 28, 2018

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Mar 21, 2018

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #23997
License MIT
Doc PR -

image

@@ -266,6 +266,7 @@ private function getServiceCall(string $id, Reference $reference = null): string
{
if (null !== $reference) {
switch ($reference->getInvalidBehavior()) {
case ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_DEFINITION:
Copy link
Member

Choose a reason for hiding this comment

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

I would break immediately instead of doing a fallthrough to the break in the next case.

Copy link
Member Author

Choose a reason for hiding this comment

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

updated

@weaverryan
Copy link
Member

I've tested this out - it works really well. Wow :p. The only imperfection (for the main use-case of controller args) is that the message contains only the controller class, not the method. I can see technically why that is - it's tricky. But, is this possible by either passing some extra context information from RegisterControllerArgumentLocatorsPass or by catching the RuntimeException in ServiceValueResolver and adding more context with a new exception?

@nicolas-grekas nicolas-grekas changed the title [DI] Add ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_DEFINITION [DI] Add ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE Mar 22, 2018
@stof
Copy link
Member

stof commented Mar 22, 2018

what happens if multiple controllers are referencing the same broken typehint ?

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Mar 22, 2018

PR embeds #26636 until it's merged.
@stof I was fixing that while you commented :)
@weaverryan I'm surprised you did not ask for the name of the argument also. So I added it ;)

if (ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior && $value instanceof TypedReference) {
$e = new ServiceNotFoundException($id = (string) $value, $this->currentId);

$this->container->register($id, $value->getType())
Copy link
Member

Choose a reason for hiding this comment

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

what if a next processed service was also trying to reference this service in the container, with a stricter behavior ? The service is now defined and will throw only at runtime.

Copy link
Member Author

Choose a reason for hiding this comment

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

this cannot happen between ResolveInvalidReferencesPass and CheckReferenceValidityPass, so that this case doesn't exist AFAIK

Copy link
Member

Choose a reason for hiding this comment

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

I mean, next services processed by this same compiler pass (this logic is in a loop over services)

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, got it. Better now?

$message = $this->createTypeNotFoundMessage($value, 'it');

if (ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
$this->container->register((string) $value, $value->getType())
Copy link
Member

Choose a reason for hiding this comment

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

a next service referencing the same value in a stricter way would now use this service as it exists. This looks wrong to me.

Copy link
Member

Choose a reason for hiding this comment

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

and even if it also use RUNTIME_EXCEPTION_ON_INVALID_REFERENCE and so would be fine with a throwing definition, it would still need a different error message for the other service, and so a different throwing definition.

Copy link
Member Author

Choose a reason for hiding this comment

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

The behavior is on the reference, not on the definition. DefinitionErrorExceptionPass will throw if any references exist that are not RUNTIME_EXCEPTION_ON_INVALID_REFERENCE.
Or I missed your point.

Copy link
Member

@stof stof Mar 23, 2018

Choose a reason for hiding this comment

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

yes, the behavior is on the reference. but when processing a next autowired service, the service will exist in the container (due to registering it here). But then, when loading the second service at runtime, this errored definition will be loaded, triggering an exception talking about the first service (as that's the one for which this service was registered).

The issue is that even though the behavior is defined at the reference level, the error is registered at the definition level (and so multiple references will share the same definition)

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, fixed

Copy link
Member

Choose a reason for hiding this comment

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

please add tests covering this, to ensure that we keep getting warned with the right error message in the future, as this is a tricky case.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added a comment instead.

$message = $this->createTypeNotFoundMessage($value, 'it');

if (ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
$this->container->register($id = sprintf('errored.%s.%s', $this->currentId, (string) $value), $value->getType())
Copy link
Member

Choose a reason for hiding this comment

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

As we consider service ids starting with a underscore as reserved since 4.0 (at least in the YAML file loader, not sure about the XML one and the PHP DSL), I suggest using _errored. as prefix instead to reduce the likeliness of conflicts.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@nicolas-grekas
Copy link
Member Author

Comments addressed.

@nicolas-grekas nicolas-grekas changed the title [DI] Add ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE [DI] Add runtime service exceptions to improve the error message when controller arguments cannot be injected Mar 26, 2018
@fabpot
Copy link
Member

fabpot commented Mar 28, 2018

Thank you @nicolas-grekas.

@fabpot fabpot merged commit 9e8e063 into symfony:master Mar 28, 2018
fabpot added a commit that referenced this pull request Mar 28, 2018
…or message when controller arguments cannot be injected (nicolas-grekas)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[DI] Add runtime service exceptions to improve the error message when controller arguments cannot be injected

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23997
| License       | MIT
| Doc PR        | -

![image](https://user-images.githubusercontent.com/243674/37775694-e5c9814c-2de3-11e8-8290-8fd05086da28.png)

Commits
-------

9e8e063 [DI] Add ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE
@nicolas-grekas nicolas-grekas deleted the di-runtime-exception branch April 2, 2018 12:07
@fabpot fabpot mentioned this pull request May 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants