Problem/Motivation
In the Image Effects module, we have been providing a set of image operations for both the 'gd' and the 'imagemagick' toolkits.
There never was a formal dependency to the ImageMagick module, though. If the module is installed, it benefits from the operations; if it isn't, the operations just stay there silently.
In converting the plugins to use attributes in place of annotations, though, I came across the fact that if the ImageMagick module is not installed, all the GD image toolkit operations fail with
The image toolkit 'gd' failed processing 'scale' for image 'core/modules/image/sample.png'. Reported error: Error - Class "Drupal\imagemagick\Plugin\ImageToolkit\Operation\imagemagick\ImagemagickImageToolkitOperationBase" not found
I think this is occurring during plugin discovery, and is probably due to class reflection actually trying to load the extended class.
Proposed resolution
If reflection failed on the class, it could be due to the class implementing or extending from interfaces/classes that are not available to the classloader. This can happen in contrib when a class in a module extends from a class in another module that is not installed.
In that case, just skip the plugin.
Drupal\Component\Plugin\Discovery\AttributeClassDiscovery no longer uses FileCache to store results of plugin class parsing.
Differently that discovery via annotations, discovery via attributes is subject to PHP runtime checks that may fail due to circumstances outside of the class itself (for instance, if a plugin class extends from an uninstalled module class). Caching the result leads to errors due if the external circumstances changes (i.e. the module that is missing is installed, or an installed module is uninstalled).
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3458177
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mondrakeComment #4
mondrakeComment #5
mondrakeComment #6
mondrakeIt's not just about image related plugins, it's about ALL plugins.
Comment #7
cmlaraComment #8
longwaveI think this makes sense and I can't see a simpler or better way to do it.
However let's open a followup to discuss that return value of
parseClass()because I agree the structure with NULLs is strange, perhaps it should throw an exception in the failure case instead? The reason we do it is:so the return value is always expected in that structure. I think this is all internal enough that we can change it without BC implications, but the followup can help us decide that.
Comment #9
mondrakeThanks @longwave, opened #3461024: Consider using exceptions instead of null values to signal invalid plugins during discovery for follow up.
Comment #10
longwaveUgh, yeah, there is caching bug there, but not sure how to work around it.
Also, how about logging something if we encounter an exception, instead of swallowing it - it might help developers if they have typo'd something or similar that prevents discovery from working?
Comment #11
mondrakeNot much into the plugin system, so this is naive…
can we just drop caching to filecache?
any module install/uninstall can potentially add/remove plugin types and actual plugins, so full scan is needed anyway on discovery?
Comment #12
mondrakeIf caching to filecache is due to considerations on reflection performance, then I think that needs to be reconsidered at the light of improvements in PHP 8.
https://gist.github.com/mindplay-dk/3359812 has benchmarks - and concludes
Comment #13
mondrakeComment #14
longwaveFile cache was probably much more important when we were parsing for annotations? But maybe we can move it to that layer and drop it for attributes, though it would be good to do some benchmarking if possible.
Comment #15
mondrakeAdded deprecations and a draft CR.
AFAICS,
Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscoveryis already using file caching.This is above me ATM
Comment #16
mondrakeComment #17
mondrakeCan benchmarking per #14 be done in a follow up? It would be a bummer to release D11 without this fix.
Comment #20
catchI don't think we should get rid of the file cache here without performance numbers - but even worse we don't know what attribute discovery even looks like on a large site yet because not all of core and contrib is converted yet, so any numbers we did get would be artificially better than they might otherwise be.
However, I think I found a way to fix the error, avoid writing to cache just in this case, and leave the file cache for everything that currently would be successfully found and cached. Because this is an alternate approach, have made the change in a separate MR. I restored the test that was removed here, and made the minimal changes to the new test so that it still parses - I think it might be enough but maybe we need to explictly test the missing attribute class with file caching too. I initially did the fix wrong and the test found the error though at least.
Comment #21
mondrake@catch thought about this earlier, what was preventing me to go in this direction is the fact that you might end up with info cached for files that no longer exist (i.e. if you uninstall a module + composer remove it). I am not fully in details of plugin discovery, but if that's not a problem, I think it's a great compromise.
Comment #22
catchPlugin discovery caches should be cleared on module install/uninstall, so that part ought to be OK I think?
Comment #24
mondrakeSo on module (un)install all the plugin files will be scanned in the directories, but only those missing from the file cache effectively also parsed? Sounds good - the file cache will have more but will not be retrieved.
Closed the initial MR, cannot really RTBC @catch's one, needs someone else's review.
Comment #25
catchYes - it'll find all the files, but only actually parse the ones that aren't in the cache (which my MR should stop them going in), or whose mtime has changed.
I think I realised more what you meant in #21 though, scenario would be something like this:
1. Module A has a plugin that relies on an an attribute defined by an uninstalled module B -> doesn't enter into FileCache or discovery.
2. Module B is installed - now the plugin is in both FileCache and discovery.
3. Module B is uninstalled again - the discovery cache will be cleared, but the FileCache won't.
So now we have a file in FileCache that uses an attribute defined by an uninstalled module.
I guess that leaves a couple of questions:
1. Should we be clearing the file cache for attributes on module uninstall (just uninstall, since the MR should cover install).
2. Or is it OK, given that this is how annotations used to work anyway.
Comment #26
mondrakeWell my gut feeling is that’s ok for now. When annotations will be eventually gone, then it will make sense the benchmark to see if file caching performs better than running reflection every time. We may have surprises…
Comment #27
godotislateMade one comment on the MR: there's been discussion on #3421014: Convert MigrateSource plugin discovery to attributes not to just eat all exceptions, so I put in a suggestion to target the specific error based on pending work over there.
There's also discussion on how to deal with missing traits in that issue as well, but the solution is more complicated.
Comment #28
mondrakepreg_match('/(Class|Interface) .* not found$/', $e->getMessage())isn't the exception message (potentially) localised in PHP?Just asking.
Comment #29
godotislateLooks like no.
https://stackoverflow.com/a/11609263
https://github.com/php/php-src/blob/ab449a7e466570f5700ff91548d2154e9c44...
Comment #30
godotislateComment #31
mondrakeApplied @godotislate suggestion, and rebased.
Comment #32
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #33
catchRebased.
Comment #34
mondrakeGreen now.
Comment #35
mondrakeComment #37
godotislatelgtm.
Comment #38
xjmPosted some comments on the MR. There are some docs minutiae that seem out of scope, or if they're in scope, I don't understand why and so clarification of the docs changes would therefore be needed. :)
Comment #39
catchI think I've addressed everything except the two comment changes that may or may not be out of scope, and the $file_path_2 naming which I don't have a strong opinion on.
Comment #40
mondrakeAddressed remaining three points.
Comment #41
mondrakeRetracted the draft CR as no longer relevant.
Comment #42
catchOK I think that's enough to move this back to RTBC.
Comment #48
larowlanCommitted to 11.x and then backported to 11.0.x/10.4.x/10.3.x after confirming with catch
Thanks folks 🚀
Comment #49
catchLooks like this is breaking pipelines on 10.3.x somehow (seen in performance test pipeline, not sure about anywhere else) https://git.drupalcode.org/project/drupal/-/jobs/2310094
Comment #50
catchOK this is because phpunit test discovery is different in 10.x, and the test class is being picked up as if it's a test. run-tests.sh doesn't use the suites, so it's mainly a problem for local phpunit test runs + performance tests.
Two options I think:
1. Just remove the test coverage in 10.x, rely on it being in 11.x
2. Move the intentionally broken class somewhere else - a test module, fixture directory etc., where it won't be mistaken for a test class.
Comment #51
mondrakeLet's try option 2. Should not be too difficult.
Comment #53
mondrakeLet's see MR!9039.
Comment #54
alexpottI was just going to comment that the only way to fix this is to move the classes under core/tests/fixtures ... 1 small nit on the MR. +1 for the fix.
Comment #55
godotislateTests are green, #54 was addressed, so lgtm.
Comment #60
catchCommitted/pushed to 11.x and cherry-picked back through to 10.3.x, thanks!
Comment #63
xjmAmending attribution.