| Project: | Drupal core |
| Version: | 8.x-dev |
| Component: | plugin system |
| Category: | task |
| Priority: | normal |
| Assigned: | EclipseGc |
| Status: | needs work |
| Issue tags: | Plugin system |
Issue Summary
Plugin discovery is completely untrustworthy. After a long drawn out conversation in #1851706: Make all core plugin managers be consistent as to whether $this or $this->discovery is passed to the factory we came to a couple of conclusions that were never formalized there.
1.) Unnecessary method calls should be avoided when possible.
2.) The CacheDecorator is almost always the last in the chain of discovery decorators.
3.) The Plugin manager is the final authority about plugin definitions, which adds unnecessary method calls and can break points 1 and 2.
To that end, these are not all solvable with separate classes representing all of this. A single plugin manager class that solves all these problems at once is what's actually needed. To that end I've build a new DrupalPluginManagerBase which should likely become the default plugin manager implementation for both Core and Contrib.
Features of DrupalPluginManagerBase:
- Extends CacheDecorator
- Extending the Cache Decorator allows us to do custom cache miss logic, and add additional static or dynamic plugin definitions which are not easily added in other ways.
- Restores trust in the PluginManager's methods instead of encouraging developers to bypass them.
- Sets up caching correctly and provides a central mechanism by which to change all plugin cache implementations simultaneously if that proves to be a false statement. (and it might be)
Stuff that's needed:
- A bit of debate about constructor arguments. We may want to expose some of the existing cache arguments in it, but they should all be protected variables of the manager now, so it's not like they're inaccessible. But I'm sure this needs some additional tweaking.
- Tests. We don't actually have tests for the existing PluginManagerBase, we should provide some for this one since it actually does stuff.
Hopefully this is a starting point to a better solution we can ALL get on board with.
Eclipse
| Attachment | Size | Status | Test result | Operations |
|---|---|---|---|---|
| DrupalPluginManagerBase.patch | 3.88 KB | Idle | PASSED: [[SimpleTest]]: [MySQL] 48,937 pass(es). | View details | Re-test |
Comments
#1
What worries me a bit in this code is that the code in the constructor becomes relatively hard to override. AFAIK it's not possible to "skip" the constructor of this class but still call the one from CacheDecorator. This makes it hard for an actual manager to add an additional decorator. You would have to copy code from two classes and adjust it.
Which seems to make the whole decorator pattern kinda... pointless? Why having a very pluggable/mixable pattern if in most if not all cases, we end up with problems if we don't define it in the exact same way.
For example, the main reason for adding the ProcessDecorator was that it can happen within the Cache decorator, right? We are the cache decorator now, so everything happens within us anyway now. Why go through the trouble?
Given this base class, what use do decorators give us exactly?
+++ b/core/lib/Drupal/Core/Plugin/DrupalPluginManagerBase.phpundefined@@ -0,0 +1,107 @@
+ parent::__construct($this->discovery, $cache_key . ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'cache', CacheBackendInterface::CACHE_PERMANENT, array());
As discussed before, a dynamic cache key needs special handling to be able to clear the caches. There are two options:
a) cache keys. Very easy to use, is what we've used so far but the downside is that at least for the default database cache backend, it means an additional query for each used cache tag.
b) Loop over all possible cache key variations and delete them explicitly. A bit more complicated but no runtime overhead. And in this case (with just a language suffix) easy to implement. We could override the cache clear method and do something like $this->cache->deleteMultiple($this->getCacheKeys()) if no cache tags are defined. That method would then loop over the language list.
#2
After spending vastly more time with the plugin system, I have an even more radical stance:
+ $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));+ $this->discovery = new AlterDecorator($this->discovery, $alter_hook);
+ parent::__construct($this->discovery, $cache_key . ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'cache', CacheBackendInterface::CACHE_PERMANENT, array());
Everything including the ProcessDecorator + AlterDecorator + CacheDecorator SHOULD be the business of the PluginManager, as the single/atomic source of plugin definitions.
All discovery is different logic that does not pertain to the plugin manager. Discovery != Processing + Caching + Management.
You can swap out the discovery at any time, and you can swap out the plugin manager any time, but these two major logic parts are mutually exclusive, and they each form their own vertical processing stacks. In fact, the discovery should be *injected* into plugin managers.
Caching is too special in order to happen at a decorator level:
The AlterDecorator should not exist in the first place. It SHOULD be an inherent part of processing any plugin definitions.
We don't need a decorator for something that simply has to be a standard for all plugin managers. Anything that is not alterable, does not account for Drupal's modular architecture and thus is simply wrong. Period. Therefore, it is the responsibility of every PluginManager to invoke an alter hook. That has absolutely *nothing* to do with discovery though. Hence, the current AlterDecorator on the discovery stack makes no sense.
The ProcessDecorator is what completely blows up any
debug()andvar_export(), as it recursively references the PluginManager class, which means that any kind ofdebug($entity)blows up with a fatal error, which is the utterly worst DX we ever had in Drupal's history.By moving the entire processing of definitions into the plugin manager, we inherently eliminate that worst-of-all-time DX disaster, since there is no recursion anymore.
#3
We just had a long discussion in IRC about this.
2. Agreed about injecting. The main discussion point in IRC was whether processing/altering belongs into a custom manager or a custom discovery/processing class. Injecting is possible either way. While it doesn't make much sense to inject something different (e.g. yaml instead of annotation), it could be a valid use case to inject a faster implementation of the same thing. A native implementation for annotations for example :p
3. Making discovery + processing the same would also allow to keep the cache a decorator. @msonnabaum argued for this, making the main purpose of the manager the composition and delegation to discovery/process, factory, .. and not actually do stuff himself. Extending the manager from CacheDecorator is essentially what you are saying, just not explicit. I'm not yet 100% sure which is the correct approach but it should IMHO either be what you said (making it explicitly the responsibility of the manager) or moving processing out.
5. That is unfortunately also true for EntityNG anyway I think. I'm not yet sure if that can be fixed. I do think that debug(), dpm() and so on should have explicit support for entities/TypedData and if passed in a compatible class, read the properties of it and display the *data*, not the class structure. DebugSerializer? ;)
#4
My stance on #3:
Processing/altering definitely does NOT belong into the discovery. Discovery SHOULD mean exactly what the name encompasses, nothing else.
Processing/altering belongs into the manager, because it SHOULD be responsible for managing and processing definitions. If you want to swap that out, swap out the manager.
Alas, the concrete swappable example of #3.2) wasn't actually related to the manager though — it circled back into discovery: A YAML-based discovery instead of an Annotation-based discovery still means discovery, not management.
I'm strongly against "making discovery + processing" the same. They SHOULD not be the same and they are not the same.
Let me ask you:
Now, I am being asked on behalf of you to serve someone some ice-cream:
You are just the one who discovered that it is available. You have no business at all in serving ice-cream.
At the same time, I CANNOT invent a new flavor that we're not able to serve. I'm bound to your discovery results.
But I do NOT care at all HOW you discovered the flavors you discovered. That's your business, not mine.
EDIT: I care for the "default info" I need in order to serve ice-cream, I care for my co-workers, and I obviously make sure that any changes to ice-cream flavors, be it the available flavors, or the served flavors, are appropriately taken into account. All of that is my business.
#5
Not necessarily that we *should* do this, but that sounds like a separate "Processor" object that gets injected just like the "Discovery" object. The two are still separate, but it keeps the re-use power for plugin managers. I.e. we could have a DefaultProcessor or whatever that applies defaults, runs *_alter(), etc. and the flexibility would still be left to the plugin manager.
#6
That's true - technically.
What I meant to say is that nothing is stopping you from altering the service definitions and inject a yaml discovery but then you won't be able to find any blocks as blocks are documented and implemented as an annotation-based discovery. My point was that there *is* a use case for doing injection, which is allowing to inject a different implementation that uses the same standard for discovery.
#7
Logging/quoting myself:
PluginBaginto the mix. Which currently exists for Views, and my own experience comes from #1868772: Change notice: Convert filters to plugins — I'd be very grateful for an architectural review on the plugin system aspects from your side on that issue.__construct(DiscoveryInterface $discovery) { $this->discovery = $discovery; }...and when actually needed:$this->discovery->find($this->owner, $this->type);— Swap out AnnotationDiscovery with a better/faster one? No problem, swap out the service, like any other. Right now, the decorator pattern forces me to swap out a dozen of plugin managers, in order to swap out the annotation discovery. That's bad.DiscoveryInterface->find($owner, $type, $derivatives = TRUE);?#8
What is injecting getting us? Its a pretty big conceptual shift in responsibilities. Specifically, the manager is less "the keeper and sole authority on how plugins work" now because discovery is being dictated by something outside its control. Because of that, I want to be clear on why we're doing it not just "its cleary a big win for ."
What use cases are we looking to support?
What measurable improvements does it make to experiences for type developers or plugin developers?
Quick under-caffeinated thoughts on the last point:
Pros:
I have to admit it provides for a much better method of testing. Easily mocked discovery can better test code we are now requiring be in the manager.
Cons:
I can no longer just do
new FooPluginManager()and start interacting with it and expect the behavior to be consistent. Any supporting code for figuring out how to inject the discovery also has to be available which means then really the only way to do that is bootstrap Drupal to some level and pull out of the container.A possible conclusion is putting the discovery for the manager on the global container. This means that we not have 2 methods for accessing discovery on the container globally available for everyone and one of them is completely wrong to interact with the sole exception of you being the manager.
other thoughts?
#9
thinking further, the issue of injecting is at best tangentially related to this issue isn't it? can we kick it to a new issue?
#10
Sorry to add to the confusion with a shameless plug, but - any major rearchitecturing plans regarding Plugin API and injectability should probably also keep an eye towards #1863816: [Followup] Allow plugins to have services injected.
Our main mechanism for object instantiation currently does not allow proper dependency injection and forces plugins classes to pull their dependencies using the procedural drupal_container(). With entities & config entities now being plugins, that's like 80% of our classes that are locked out of the acknowledged "D8 right way" & the associated unit testability.
In short, current plans over there, based on the current architecture, involve :
- Making each plugin expose their dependencies - putting that in the plugin definition probably won't cut it because we need inheritance (plugin B extends plugin A and probably needs plugin A's dependencies too). So a Plugin::getDependencyInfo() method maybe ?
- at DIC-compile time, collect the info and place it in the DIC so that it's alterable (e.g let me swap database.slave instead of database for this specific plugin_id on my site). Either in "abstract factory" service entries (one per plugin_id), but that probably wouldn't work well with the current design; or in container params (one per plugin_id) rather than services.
#11
Ok, I've commented on #1863816: [Followup] Allow plugins to have services injected at this point, and I don't think it has anything to do with this issue.
I've had a number of conversations with people about this topic and other related to it at this point, so I'm going to attempt to dump all of that here now.
The plugin manager separates code into sub classes it can delegate to for the sake of reuse, not for the sake of concerns/responsibility/separation. All responsibilities concerned with managing the plugins of a given type reside with the plugin manager at all times. Separation of code is 100% for the purposes of reuse. Any plugin manager could contain all the code necessary to perform any discovery, instantiation or mapping task. That is the nature of the plugin system. Injection of discovery or factory or mapper is unnecessary and doesn't actually buy us anything. Any benefits we might see to mocking discovery or factory or mapper are within unit test situations that prove the concept, not within implementations (which is what we're discussing here). It is important to note that despite the fact that sun makes a very very good argument for removing the vast majority of decorators (basically everything except DerivativeDecorator) the removal of base Discovery classes has not been argued for, and as such, I don't think we should entertain that at all. We still have a need for annotation, hook and static discovery, and the option to build completely new base discovery components is ++ in my opinion. In short, a manager instantiates the classes it needs because it has to be its own final authority. Injecting a different sort of discovery/factory/mapper into the manager is likely to be little more than a novelty. Replacement of ALL use cases of a particular discovery is the only argument that has merit here in my opinion, and we lose so much control and increase the barrier to creating new plugin types substantially that I have to discourage it. This will lead to arguments that we should inject the factory (which is also not true) which will lead to more confusion once people understand there's a dependency there that appears circular.
With that being said, I still feel a DrupalPluginManagerBase is the appropriate way to solve ALL of these issues. This class will remove the need for the Cache/Process/Alter decorators, and this will simplify all discovery declaration in the constructor to simply 1 discovery class and a decorator (if derivatives are desired). https://gist.github.com/4674513 is my abstract class for this approach. I will finish that soon and provide some tests for it, but the most common pattern has been discovery->defaults->alter->cache, and this class will formalize that in code.
Once this class goes in, the vast majority of PluginManagers should be able to just extend this class and declare their discovery and factory. We should also formalize that all factory classes that have been created be removed in favor of code in the manager itself unless they are reusable. This should end a lot of the confusion that has persisted about the nature of the relationship between manager, discovery and factory.
I hope this seems sane and straight forward and makes sense to all parties here. I've stressed a lot over this topic because I want to eliminate the apparent confusion over what the intention within the manager is and hopefully put an end to this debate before it can spread to contrib-land.
Eclipse
#12
I'm going to try to reframe this issue, because it seems like you are talking past each other.
It seems like everyone agree that what is desired is to split the "discovery" responsibility into three individual steps of "discovery" (per se), "processing" and "caching".
@EclipseGc is suggesting to implement those steps as methods of a base abstract class. @berdir seems to be worried that doing so would reduce the co-reuse and overridability that CacheDecorator gives us.
It doesn't have to be that way. We can implement the three new steps with three different objects. That would enable a dependable ("trustworthing") flow while still allowing code reuse by composition. Also, it would nicely remove the needs for decorators, an anti-pattern that should be avoided at all costs :)
#13
Actually, totally fine with a base class. I'm even fine with a default manager class that works out of the box.
Worked a bit on @EclipseGc's proposal and made into a DefaultPluginManager that works out of the box (almost) and just needs to be configured in the Bundle.
Notes:
- Tried to inject as much as possible
- Renamed to DefaultPluginManager and made it not abstract.
- Not extending from CacheDecorator currently, that somehow simply feels wrong. Also can't inject the cache backend into that currently
- Added a setCache() method that allows to set the used cache backend and the cache key prefix. Using a loop to clear the caches, which makes it possible to not having to use tags for a known, simple set of variations. language related stuff is the only thing that is currently not injected.
- Added a setAlterHook() that works with an injected module handler.
- Both methods can be directly defined in the Bundle, the advantage of using methods is the constructor doesn't have like 10 arguments and I can ensure that I can require everything for groups of arguments (e.g. cache backend + cache key or module handler + alter hook name)
This basically seems to work. I think I'm also starting to understand @EclipseGc now because right now we pass the discovery to the factory which means that the factory accesses the uncached, unaltered definitions.
Patch will probably explode, just trying to get some early feedback.
#14
The last submitted patch, default-plugin-manager-1903346-13.patch, failed testing.
#15
The moment alter, process & cache are taken out of the discovery and done in the manager (i.e discovery is only "the mechanism for initial collecting of definitions" rather than "the final list of definitions"), then sure, the factory totally needs to receive the manager rather than the discovery.
Also, +1 for not extending CacheDecorator, felt really confusing IMO.
DefaultPluginManager maintains a statc cache of definitions even if setCache() has not been used to configure a persistent cache. Probably makes sense, but given everything is called "cache" indistinctly, it's a little bit confusing (e.g you'll still need to call clearCachedDefinitions() when appropriate even though you haven't setCache())
#16
I apologize about the CacheDecorator extension thing. It was just to get the ball rolling. Once this goes in I think we should actually remove process, cache, and basically every other decorator except derivatives. They have caused too much confusion and too many arguments about the nature of the system. This should be a solution going forward, I generally like what I see in this patch, and I think it makes a lot of sense.
A few little things:
Aren't we passing the module_handler? why call:
<?phpDrupal::service('module_handler')->alter(...
?>
Shouldn't it be:
<?php$this->moduleHandler->alter(...
?>
Otherwise we should probably get the various test passing properly.
Eclipse
#17
+1 on removing the decorators once this is in :-)
#18
This should fix the test failures or most of them.
The aggregator render one is a bit weird. That was exposed because the definition caching did not yet work and it looks like something in the test changes the feed.block to 0 so the query doesn't find it anymore which then results in those notices. Fixing caching made them go away but this sounds like something that should be made more fail-safe anyway, at least if we don't convert that to block instance configuration.
Not sure what do do about ->definitions. I can do that only if there is a cache bin, any real manager should be using one anyway. Not yet changed.
As there seems to be a basic consensus on the approach, I converted a number of additional plugin managers, 3 of them were removed completely (aggregator fetcher, tour tip and edit.module EditorManager (editor.module also has a EditorManager)) as all they did was setting the discovery and decorators. Mostly made that to see how this affects all those plugin managers ( we have a lot already!) Let's see if that introduces any new fails.
#19
The last submitted patch, default-plugin-manager-1903346-17.patch, failed testing.
#20
Forgot some use statements, this should fix the installer.
#21
The last submitted patch, default-plugin-manager-1903346-20.patch, failed testing.
#22
This should fix the test failures.
#23
The last submitted patch, default-plugin-manager-1903346-22.patch, failed testing.
#24
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,274 @@
+ public function __construct($owner, $type, array $namespaces) {
+ $this->owner = $owner;
+ $this->type = $type;
+++ b/core/lib/Drupal/Core/CoreBundle.phpundefined@@ -149,7 +149,11 @@ public function build(ContainerBuilder $container) {
$container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager')
+ ->addArgument('Core')
+ ->addArgument('Entity')
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.phpundefined@@ -64,7 +64,7 @@ function setUp() {
- $manager = new EditorManager($this->container->getParameter('container.namespaces'));
+ $manager = new EditorManager('editor', 'editor', $this->container->getParameter('container.namespaces'));
Seems a bit weird / needlessly verbose to inject the $owner and $type properties - like, do we want to allow using EntityManager with different $owner and $type values ?
Unlike the list of namespaces, that depend on external conditions and need to be injected, it looks like those would be hardcoded in the manager ?
#25
I just did that because that's the constructor arguments that the default plugin manager now expects. Probably makes sense to override the constructor and specify it there in case we still have a manager.
#26
Those are already protected values in the manager, just override the constructor and set those properties. The only reason to do anything else is if we're actually using the class instead of extending it, and I generally expect we'll be extending it.
Eclipse
#27
Ok, that was too much there, did run into serialization issues with the database connection and so on.
Re-roll without changing all the plugin managers yet. Want to get this green again first and then attack them one be one or at least in smaller groups.
#28
The last submitted patch, default-plugin-manager-1903346-27.patch, failed testing.
#29
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.phpundefined@@ -31,12 +26,12 @@ class BlockManager extends PluginManagerBase {
- $this->factory = new DefaultFactory($this->discovery);
+ public function createInstance($plugin_id, array $configuration = array(), Block $entity = NULL) {
+ $plugin_class = DefaultFactory::getPluginClass($plugin_id, $this);
+ return new $plugin_class($configuration, $plugin_id, $this, $entity);
This looks like a bad merge, block plugins no longer need the entity.
This certainly makes all of the managers smaller, but now that each one will need its own annotation (see #1966246: [meta] Introduce specific annotations for each plugin type), should we come up with a Drupalism to match the setAlterHook, setCache, etc.?
#30
This is now possible as #1953800: Make the database connection serializable just got in!
#31
Yes it was :) Let's try this.
A method for that might make sense, will integrate that when I convert the first one that already uses such a class.
We might want to get this in without converting all managers as I started above to avoid making the patch too big I guess. But probably a few example conversions to see that it works.
#32
@amateescu: I know, that's why I picked this up again :)
#33
The last submitted patch, default-plugin-manager-1903346-30.patch, failed testing.
#34
\Traversable it is.
#35
Great stuff so far, here are a couple of tiny points.
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ * Implements Drupal\Component\Plugin\PluginManagerInterface::createInstance().
...
+ * Implements Drupal\Component\Plugin\PluginManagerInterface::getInstance().
...
+ * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
...
+ * Implements \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::getDefinitions().
...
+ * Implements \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions().
This could all use {@inheritdoc}
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ public function processDefinition(&$definition, $plugin_id) {
Needs parameter docs.
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/DefaultsTestPluginManager.phpundefined@@ -23,7 +22,6 @@ public function __construct() {
$this->factory = new DefaultFactory($this->discovery);
I guess this line could also be removed?
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ * @var \Drupal\Core\Extension\ModuleHandler
...
+ * @param \Drupal\Core\Extension\ModuleHandler $module_handler
...
+ public function setAlterHook(ModuleHandler $module_handler, $alter_hook = NULL) {
Just a nitpick: We have an interface for module handlers.
#36
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ public function getDefinition($plugin_id) {
+ // Optimize for fast access to definitions if they are already in memory.
+ if (isset($this->definitions)) {
+ // Avoid using a ternary that would create a copy of the array.
+ if (isset($this->definitions[$plugin_id])) {
+ return $this->definitions[$plugin_id];
+ }
+ else {
+ return;
+ }
+ }
+
+ $definitions = $this->getDefinitions();
+ // Avoid using a ternary that would create a copy of the array.
+ if (isset($definitions[$plugin_id])) {
+ return $definitions[$plugin_id];
+ }
This is a bit redundant and could be simplified. I dunno if we care, but just reading through it that became obvious.
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ public function getDefinitions() {
+ $definitions = $this->getCachedDefinitions();
+ if (!isset($definitions)) {
+ $definitions = $this->discovery->getDefinitions();
+ foreach ($definitions as $plugin_id => &$definition) {
+ $this->processDefinition($definition, $plugin_id);
+ }
+ if ($this->alterHook) {
+ $this->moduleHandler->alter($this->alterHook, $definitions);
+ }
+ $this->setCachedDefinitions($definitions);
+ }
+ return $definitions;
Just clarifying that this doesn't actually force a cache to be used (which I approve of) but I think we have an issue in the block implementation later in the patch because of it.
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.phpundefined@@ -0,0 +1,266 @@
+ * Implements \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions().
The class doesn't implement this interface, and I don't think the PluginManagerInterface requires caching, so we should probably add that interface to this class.
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.phpundefined@@ -31,12 +26,6 @@ class BlockManager extends PluginManagerBase {
public function __construct(\Traversable $namespaces) {
- $this->discovery = new AnnotatedClassDiscovery('Block', $namespaces);
- $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
- $this->discovery = new AlterDecorator($this->discovery, 'block');
- $this->discovery = new CacheDecorator($this->discovery, 'block_plugins:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'block', CacheBackendInterface::CACHE_PERMANENT, array('block'));
-
- $this->factory = new DefaultFactory($this->discovery);
+ parent::__construct('Block', $namespaces);
Well that got concise didn't it? Still I think that we need to have some $this->alterHook = "block"; or the various cache properties set as well. no?
Also, there are a few relevant parameters to the AnnotatedClassDiscovery that we should be supporting, like what the annotation class is, and where to find it.
Beyond that stuff, I'm REALLY liking where this is heading, and basically +10000000 for getting back to this before me. I am so happy to se this moving forward.
Eclipse
#37
- Fixed the inheritdoc stuff and cleaned up the class a bit, there were a few methods that are already in the base implementation and aren't needed.
- The DefaultFactory can't be removed in the test manager because we don't invoke parent::__construct() as we're using a completely different discovery. Maybe move that to a separate method so that we can override it separately from the constructor?
- Changed to ModuleHandlerInterface.
- Simplified getDefinition()
- Not sure I follow the remark about cache problem with block manager?
- CachedDiscoveryInterface: I'm wondering if we shouldn't merge that in the default interface? It doesn't say anything about persistent cache and I can't see the reason to not at least to a static caching and when you do that, being able to reset that could be useful in a test or so. And even if not, in the worst case it's an empty function that you have to implement. The reason I'm wondering about this is that you don't get autocomplete for that method in e.g. Drupal::pluginManager() (see #1950670: Decide what methods should be added for plugin managers to \Drupal) and e.g. for block manager, it's used quite frequently. For now, we need the separate interface for the cache decorator but we can simply do PluginManagerInterface extends CachedDiscoveryInterface. Thoughts?
- alterHook/cache id is set through the methods defined in services.yml.
- Converted the managers in core.services.yml and also added support for the annotation namespace/class name arguments. Can't be a separate method as we need it in the constructor and it can't really be defined in services.yml anyway. So managers with a custom class might need to keep their constructor override. For those where it's just a trivial parent::__construct('Block', $annotations), we could completely move it to services.yml as my earlier patches did. Not sure how I feel about that yet. And how many of those will actually stay like that.
Let's see how this goes...
#38
The last submitted patch, default-plugin-manager-1903346-37.patch, failed testing.
#39
Hm, silly mistake.
#40
The last submitted patch, default-plugin-manager-1903346-39.patch, failed testing.
#41
Excluding the entity manager for now although I guess those test fails were due to the missing cache tag.
#42
I really like that there is no a common base, so for example most plugins will have derivative support.
It's a good point that you will get autocompletion support though conception it just feels wrong. Maybe we have to accept it.
In general i'm wondering whether there should be tests for this new plugin manager. For sure there are enough users of this code, so we are save, but it always feels wrong to rely on such functionality.
#43
Work in progress.
- As discussed with a few people here, we're making the default plugin manager abstract for the following two reasons:
a) Being able to have a single spot where the plugin discovery logic is specified: which subdirectory, alter hook etc.
b) Being able to identify a certain plugin manager based on the class, e.g. $class being a ConditionPluginManager instead of a DefaultPluginManager.
- Also injected the language manager, there's an issue that will make language_list() a method but that's not yet in.
- Started to add mocked PHPUnit tests. Having some troubles there with t().
#44
Ok, converted the already converted plugin managers.
Also using the static discovery now in the test which avoids the t() problem, so was able to remove that again.
#45
The phpunit test looks quite good, with the exception of calling parent::setup. That should never be necessary when using UnitTestCase.
#46
The last submitted patch, default-plugin-manager-1903346-44.patch, failed testing.