Problem/Motivation

DefaultFactory::getPluginClass() does empty($plugin_definition['class']) which fatals if $plugin_definition is an object. In the entity and field system, where we actually have such plugin definition objects, we never use the DefaultFactory but any contrib plugin type that tries to follow the supposed best practice of making its plugin definitions objects will fall into this trap.

Proposed resolution

Introduce an interface that object definitions should implement if their plugins are to be instantiated through DefaultFactory.

Remaining tasks

None.

User interface changes

None.

API changes

None.

Blockers

This issue blocks #2458769: Derivative plugin definitions contain base plugin IDs instead of derivative IDs.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug, because DefaultFactory cannot handle object definitions, while it should
Issue priority Normal, because normal site operation is not affected
Prioritized changes Bug fix
Disruption No disruption
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tstoeckler’s picture

Note 1: Because this is such an integral part of the D8 architecture I would consider this major but I'll let others fight that fight so marking normal for now.

Note 2: I must admit that I did not read through all the related issues in the plugin system queue, but I couldn't find anything for this particular bug using a bunch of targeted searches. Sorry if I missed something.

tstoeckler’s picture

Status: Active » Needs review
Issue tags: +Needs tests
FileSize
1.49 KB

I was thinking something like this.

Should be straightforward to write a test for this, but I'd like some feedback first before pursuing that.

Xano’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
@@ -77,11 +77,24 @@ public function createInstance($plugin_id, array $configuration = array()) {
+    elseif (is_object($plugin_definition) && method_exists($plugin_definition, 'getClass')) {

Magic method access is not a solution, but a workaround. I know we're in a tight spot, but let's not 'fix' things the wrong way.

tstoeckler’s picture

Care to share how you think it should be? ;-)

dawehner’s picture

I guess xano wants to have an interface ... #2458789: Introduce PluginDefinitionInterface

tstoeckler’s picture

Status: Needs work » Needs review

I agree with that issue. But I fear that will be more controversial and this is an actual bug. Marking "needs review" to get some more opinions on this.

Xano’s picture

Xano would like to see an interface, but he would even more so like to see we stop magically figuring out which properties or methods to call, like #2494719: ConfigFormBaseTrait is confusing. Such solutions only make things worse.

Care to share how you think it should be? ;-)

Ideally an interface, of course. I'm aware of the fact we are in code freeze right now, but that's no reason to commit workarounds that utilize very bad practices. We *can* still break BC to fix severe fundamental problems, which is why I don't want to rule out solving this issue (and several others) by adding the interface.
If this problem cannot be solved by introducing a new interface, then I have no potential fix in mind right now.

tstoeckler’s picture

Status: Needs review » Postponed

OK. If this does not get in before Drupal 8.0.0, @Xano owes me a $beverage. :-P

Xano’s picture

#2458789: Introduce PluginDefinitionInterface now passes all tests and is ready for manual review. If that patch gets committed, fixing this issue will be really easy.

Fabianx’s picture

Tagging for framework manager review as #2458789: Introduce PluginDefinitionInterface was pushed back.

Xano’s picture

To re-iterate because this issue was referenced elsewhere: the patch is no workaround, but a hack. The new code is much more difficult to debug and will only work when object definitions work according to hardcoded, undocumented assumptions. I understand why it was proposed, but code like this should never be used.

Xano’s picture

Status: Postponed » Active

Bump.

Status: Active » Needs review

tstoeckler queued 2: 2485513-2.patch for re-testing.

Xano’s picture

Status: Needs review » Needs work

We shouldn't be using magic method access, even if it works in core's current configuration. See #11.

Xano’s picture

The only good solution involves working with an interface on entity/object definitions (reasons in #11). Seeing as this bug lives in \Drupal\Component, we cannot use EntityTypeInterface::getClass(), unfortunately, as that lives in \Drupal\Core. We can either introduce a new small generic interface, or pursue #2458789: Introduce PluginDefinitionInterface.

Xano’s picture

Status: Needs work » Needs review
FileSize
4.18 KB
4.73 KB

Here's a solution without magic or BC breaks. It works with entity types as well. I also fixed some left-over array access.

Xano’s picture

FileSize
676 bytes
5.13 KB

I forgot to remove one more interface method.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Component/Plugin/Definition/PluginDefinitionInterface.php
    @@ -0,0 +1,38 @@
    +/**
    + * Defines a plugin definition.
    + *
    + * @ingroup Plugin
    + */
    +interface PluginDefinitionInterface {
    

    IMHO we should explain when to use it

  2. +++ b/core/lib/Drupal/Component/Plugin/Definition/PluginDefinitionInterface.php
    @@ -0,0 +1,38 @@
    +   *   If the class is invalid.
    

    What defines an invalid class?

Xano’s picture

FileSize
550 bytes
5.2 KB

What defines an invalid class?

Good question. It depends on the plugin type, as most of them have interfaces that must be implemented, or base classes to be extended. I'll have to think about how to phrase this. Suggestions are welcome.

Xano queued 19: drupal_2485513_19.patch for re-testing.

Xano’s picture

Issue tags: -Needs tests
FileSize
16.89 KB
20.92 KB

Changed SHOULD to MUST as per @dawehner's recommendation, and added tests.

Xano’s picture

FileSize
12.53 KB

So I screwed up the patch after playing with Composer. Here's a clean one.

The last submitted patch, 21: drupal_2485513_21.patch, failed testing.

dawehner’s picture

This kinda reverts the original idea of #2005716: Promote EntityType to a domain object which was about:

This patch removes Entity type's dependency on Plugins among other things.

Xano’s picture

Entity types have depended on plugins for as long as I can remember, at that issue didn't change that. I don't disagree that decoupling these two systems may potentially be a good idea, but the situation at the moment is that entity types are discovered through the plugin system, but that we don't really support object definitions properly. We're not going to change this dependency, as we don't only use plugin system classes, but have tied the entity API to the plugin system by at least making EntityManagerInterface extend PluginManagerInterface and CachedDiscoveryInterface. Changing this would be a BC break.

TLDR; entity API depends on the plugin system and will do so until Drupal 8's EOL. Let's make sure this dependency works better than it does now.

lauriii’s picture

Issue tags: +Needs beta evaluation

The change looks nice compared to #2458789: Introduce PluginDefinitionInterface which caused quite bad BC breaks. In the patch, there is quite a good test coverage being added for the interface. We still need beta-evaluation, sign off from the framework manager and feedback about #24/#25

+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -6,6 +6,7 @@
 namespace Drupal\Core\Entity;
+use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;

Minor detail; missing line change between namespace and use statement

Xano’s picture

Issue summary: View changes
Issue tags: -Needs beta evaluation
Fabianx’s picture

Xano’s picture

FileSize
395 bytes
296.92 KB

Addressing #26.

Status: Needs review » Needs work

The last submitted patch, 29: drupal_2485513_29.patch, failed testing.

Xano’s picture

Status: Needs work » Needs review
FileSize
12.53 KB

Re-roll.

lauriii’s picture

Status: Needs review » Reviewed & tested by the community

Everything has been addressed from #26

tstoeckler’s picture

Yes, this looks awesome, thanks a lot!

+++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
@@ -77,18 +78,32 @@ public function createInstance($plugin_id, array $configuration = array()) {
+      throw new PluginException(sprintf('% can only handle plugin definitions that are arrays or that implement %s, but %s given.', __CLASS__, PluginDefinitionInterface::class, $plugin_definition_type));

The first placeholder is missing a "s". i.e. % -> %s

Assuming this can be fixed on commit, so RTBC++.

Xano’s picture

Let's fix it now. The committers have more important things to work on than fixing typos :-)

We can use the newly added interface to fix #2458769: Derivative plugin definitions contain base plugin IDs instead of derivative IDs in the same way we fixed the bug here.

lauriii’s picture

Status: Reviewed & tested by the community » Needs work

The size of patch increased quite a lot..

Xano’s picture

Status: Needs work » Needs review
FileSize
995 bytes
12.53 KB

I had forgotten to rebase. Here's the correct patch, plus interdiff (same as the previous one).

lauriii’s picture

Status: Needs review » Reviewed & tested by the community
Xano’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
562 bytes
12.53 KB

Let's actually not require a fluent interface, to allow definitions to be immutable. EntityType::setId() can continue to return $this, as that is covariant with static.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Sure, whatever ;-P

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Status: Needs work » Needs review

Xano queued 38: drupal_2485513_38.patch for re-testing.

Xano’s picture

Status: Needs review » Reviewed & tested by the community

That looked like a random test failure.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

lauriii’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Status: Needs work » Needs review

Xano queued 38: drupal_2485513_38.patch for re-testing.

Xano’s picture

Status: Needs review » Reviewed & tested by the community

Random migration test fail.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Status: Needs work » Needs review

Xano queued 38: drupal_2485513_38.patch for re-testing.

Xano’s picture

Status: Needs review » Reviewed & tested by the community

Random HTTP query fail?

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Xano’s picture

Status: Needs work » Reviewed & tested by the community

That was an aborted re-test. RTBC and triggering a re-test.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

lauriii’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Xano’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Xano’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 38: drupal_2485513_38.patch, failed testing.

Status: Needs work » Needs review

Xano queued 38: drupal_2485513_38.patch for re-testing.

Xano’s picture

Status: Needs review » Reviewed & tested by the community
dawehner’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -15,7 +17,7 @@
  */
-interface EntityTypeInterface {
+interface EntityTypeInterface extends PluginDefinitionInterface {
 

Life is sad, but we should be honest. Entities are plugins, given that we use plugin manager interfaces. This coupling doesn't make things worse, it just shows it you directly.

alexpott’s picture

Category: Bug report » Task
Status: Reviewed & tested by the community » Fixed

I think this is a good compromise - we should be honest that we did truly decouple plugins and entity type discovery and this is a very minimal surface area. It also will make plugin managers that use objects for plugin definitions are more robust. I'm committing this under the committer discretion provision of the beta policy - but let's be honest - this is not a bug.

Committed 125cda4 and pushed to 8.0.x. Thanks!

  • alexpott committed 125cda4 on 8.0.x
    Issue #2485513 by Xano, tstoeckler, lauriii: DefaultFactory cannot deal...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.