Problem/Motivation
Currently, when a plugin cannot be found, we get this obtuse error message:
Drupal\Component\Plugin\Exception\PluginException: The plugin (%plugin) did not specify an instance class in Drupal\Component\Plugin\Factory\DefaultFactory->getPluginClass() (line 60 of /path/to/webroot/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php).
first, this is incorrect - what's usually happening is that no plugin definition was found, not that the found plugin definition did not specify an instance class. second, it gives us very little useful information about WHICH plugin actually failed - we just have the plugin id, not the owner/type information. that's a little tricky to get at.
Proposed resolution
introduce new exceptions - PluginNotFoundException and DerivativeNotFoundException - that can be thrown by the various Discovery implementations when plugins aren't actually found.
Remaining tasks
the exceptions still don't include contextual information about the plugin type when throwing exceptions. we could probably expand the DiscoveryInterface to include such contextual information purely for the purpose of these exceptions.
API changes
any new Discovery classes, decorators, etc., should follow the practice of throwing the appropriate exception for their case.
| Comment | File | Size | Author |
|---|---|---|---|
| #51 | 1846070-51.patch | 14.79 KB | nwom |
| #45 | 1846070-45.patch | 14.93 KB | narendra.rajwar27 |
| #44 | 1846070-44.patch | 16.78 KB | narendra.rajwar27 |
| #43 | 1846070-43.patch | 16.66 KB | narendra.rajwar27 |
| #29 | patch.png | 25.85 KB | lokeshwaran97 |
Comments
Comment #1
sdboyer commentedInitial patch.
Comment #2
sdboyer commentedComment #3
sdboyer commentedwe should really differentiate the messages in the exceptions, especially for some of the decorators.
Comment #5
neclimdulI agree with the things said here.
Comment #6
sdboyer commentedok, this should go green. i've made the following changes:
DiscoveryInterface::getDefinition()to have a second parameter, a boolean indicating whether or not an exception should be thrown on failure. this allows plugin managers that want to expect failure in the discovery process to avoid having to use exceptions at runtime to provide default/fallbacks. i'm not really sure i like this - i think i'd rather the default selection happen within discovery more easily, somehow - but this was a reasonable stopgap i discussed with yched after solving some of the field[_ui]-related failures in the initial approach.PluginManagerBase::getDefinition()now has its own try/catch block in it, wherein it waits for one of thesePluginNotFoundExceptions, catches it, then throws a new one but includes the current class name in it. this is the simplest thing i could come up with to provide more context than simply the plugin id..however, still less than optimal.i would really like a review from yched on this to see if there's a way we could make the interaction of formatters & widgets with this new system a bit more elegant.
Comment #8
sdboyer commentedpheh. silly use statement conflict.
Comment #9
yched commentedSo yeah, as discussed on IRC, we do have valid runtimes cases where we want to be able to query a non-existing plugin_id - like "does this plugin exist ?".
That's what field_info_widget_types($widget_plugin_id) does, and it currently returns NULL - switching that to "throws an exception woudl be a non minor API change".
Also, WidgetPluginManager::getInstance() checks whether the requested plugin_id exists and switches back to the default widget if not.
For all those cases, we agreed that we don't really want to bring "throw exceptions / catch them and handle" to become a regular pattern in "normal" runtime execution.
But as sdboyer points, for other cases - notably when the retrieved definition is passed to a factory, passing NULL only causes cryptic exceptions down the line, and raising an exception at the origin of the error would make much easier debugging.
So we need a way to say how we want getDefinition() to behave - exception or NULL.
Actually Symfony's DIC has something like this this already :
(also used for
new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE);on ContainerBuilders, so that's a pattern that can potentially be found in our Bundle::build() implementations already)So, while I approve the approach, I wonder whether, rather than a bool whose signification is never too clear when looking at the calling code, we should go with similar constants (although I couldn't really make sense of IGNORE_ON_INVALID_REFERENCE).
Also, whether bool on constants, having to reproduce the supporting logic in all discoveries and decorators kind of begs for #1809200: Consolidate discovery code in a DiscoveryBase and DiscoveryDecoratorBase :-)
Comment #11
sdboyer commentedyeah, the named params is a much nicer way of doing this. switching over to that.
agreed that all the redundant code begs for a consolidated approach.
i think part of the reason this code makes me twitchy is because the invalid behavior param doesn't really pass down through the decorated layers. each decorator calls the getDefinitions() method of what it decorates, not getDefinition(), which makes the contract suggested by the parameter somewhat brittle and reliant on other wrapping code making sure to respect it. not a critical concern, but a touch concerning.
i still feel that the inclusion of a parameter to allow specifically for this defaulting use case smells of bad architecture. the fact that we need to think about that at all is a product of the fact that we've been abusing decorators like we have. really, these exceptions are more intended for client code outside of the plugin system.
Comment #12
sdboyer commentednew patch. not sure if it'll green or not, i may have missed something when switching over to the new named params. they are:
seem workable? 'ignore' seems rather the same as null, so i didn't think it applicable here.
note that this:
is still as it was. even worse, actually, it now reflects on the exception to generate a new one of the same type. i added a @todo for it, it desperately needs improving, but i need someone with more insight into what info we currently/could reliably provide to discovery plugins to figure out a better approach, i think.
Comment #13
clemens.tolboom#12: discovery-exceptions-1846070-12.patch queued for re-testing.
Comment #15
clemens.tolboomXREF #1897762: Improve exception handling in case of NULL from getPluginClass
Comment #16
clemens.tolboomComment #16.0
clemens.tolboomfix doubling of API changes section
Comment #17
xanoWhat about something like this? Instead of using constants to alter methods' behaviors, we simply add
has*()methods that allow us to fail gracefully by checking for something before we request it.The code is untested and probably not complete, but you'll get the idea.
Comment #18
damiankloip commentedThen should has() be used in calling code too? We could also think about getting more info in that exception message.
Comment #19
yched commentedLooks like this overlaps with #2178795: Allow DiscoveryInterface::getDefinition() to throw an exception for an invalid plugin ?
Comment #20
tim.plunkettParts of this are unknowingly duplicated by #2178795: Allow DiscoveryInterface::getDefinition() to throw an exception for an invalid plugin (the hasDefinition).
The other part are explicitly duplicated in #2189985: Rename UnknownPluginException to PluginNotFoundException, which is my fault...
I could close that if you'd rather continue here, but that one was quick and RTBC.
Comment #21
xanoIf you really only want to check whether a plugin exists, or if you want to be able to degrade gracefully (using a fallback plugin, for instance), than calling
has()is the way to go. The exception should ideally not be thrown on live sites as it should only occur when a developer makes a typo in a plugin ID, for instance.I'll take a look at #2178795: Allow DiscoveryInterface::getDefinition() to throw an exception for an invalid plugin. This patch doesn't only touch DiscoveryInterface, though, but also deals with derivers and derivatives, for instance.
Comment #22
xanoAlso #1875996: Reconsider naming conventions for derivative classes will pave the way for more self-explanatory exception names.
Comment #23
neclimdulJust noticed, probably more appropriate to make this an InvalidArgumentException. Just implement the Plugin "ExceptionInterface" instead.
Comment #24
wim leersComment #25
nitesh sethia commentedComment #29
lokeshwaran97 commentedpatch doesn't work
Comment #30
vijaycs85Comment #33
MerryHamster commentedas I understand it's not actual for 8.6, the structure has many changes.
Comment #36
mpp commentedWe can (should) improve the wording to clarify the problem.
But the problem remains that you'll run into this error when adding a plugin to an existing module:
Plugin (my_plugin) instance class "Drupal\my_module\Plugin\ConfigFilter\MyPlugin" does not exist.In DefaultFactory.php line 97.The discovery will find the plugin id but the class is not yet loaded (a new plugin requires a cache-clear).
There are a large number of issues in the queue describing this error message, some have a faulty namespace but in a number of cases it's due to cached plugins.
There is an issue that should solve this for plugin derivers but I couldn't find one for plugins.
Comment #37
adityaj commentedDrupal plugin system has issues, it in some cases crashes the whole site when the plugin is missing. We should have some drush command to recover from site crash. Or plugin system should auto recover it self and prevent site crash.
Comment #38
renguer0 commentedSame problem here. I'm uploaded my site from localhost, database connects but I see this error message when I try to get into drupal admin page.
Looking for a solution. Thanks for your time.
Comment #40
jungleelse here is unnecessary, as in if, there is a return
Similar to the above i guess
And here
Comment #42
narendra.rajwar27Comment #43
narendra.rajwar27applied the patch after making changes.
Comment #44
narendra.rajwar27Comment #45
narendra.rajwar27Comment #46
narendra.rajwar27Comment #47
sivaji_ganesh_jojodae commentedComment #48
quietone commented@narendra.rajwar27, Thank you for looking into to this issue and working to get a viable patch. The rerolls in comment #43, #44 and #45 do not have interdiffs (or a diff) so it isn't clear what changes have been made or why. You are more likely to get a review if the interdiff is provided as well as a comment describing what you have changed and why. This makes it much easier for the reviewer to understand the changes.
This issue began in 2012 and much has changed. The proposed resolution in the IS suggests adding a new PluginNotFoundException. That exception already exists, \Drupal\Component\Plugin\Exception\PluginNotFoundException, so let's have an issue summary update.
Comment #49
quietone commentedChecking my work and this isn't a bug and I didn't look at this as part of Bug Smash Initiative work, so removing tag.
Comment #51
nwom commentedThe patch no longer applies against 9.2.x. Here is a re-roll without any changes.
Comment #52
nwom commentedEven after re-rolling, it appears there is another issue. I get a WSOD with the following message in watchdog:
Error: Class 'Drupal\Component\Utility\SafeMarkup' not found in Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator->getDefinition() (line 83 of /core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php)Comment #58
damienmckennaRelated: #3387596: views.settings config object should not be used to cache list of display extenders