I've added a very rough class for a plugin generator.

Lots of todos!

Comments

joachim’s picture

See #2086397: Find a consistent way to document plugin types.

In order to make this useful, we're going to need a lot more data on plugin types that can be automatically parsed.

Ideally, you want to be able to just say: give me a plugin of type EntityType called 'Dinosaur' and have all the rest follow automatically.

joachim’s picture

Issue summary: View changes

What we need for this is:

- a way to detect all the plugin types that exist in a Drupal installation -- both core and from any contrib modules
- a way to know what properties and methods the plugin class should have

I could really do with input on both of these!

joachim’s picture

So let's take block plugins for instance.

We have:

- the interface \Drupal\Core\Block\BlockPluginInterface, from which we can presumably figure out how to create a template block plugin class
- the base class \Drupal\Core\Block\BlockBase, which the block_api help topic says we should probably extend
- the annotation class \Drupal\Core\Block\Annotation\Block, from which we can presumably figure out how to create the annotation for the top of the block plugin class

What's missing is how to find these, and how to know that they all are tied together to form the requirements for a block plugin.

joachim’s picture

tim.plunkett’s picture

The first line constructor of plugin.manager.block (Drupal\Core\Block\BlockManager):

    parent::__construct('Plugin/Block', $namespaces, $module_handler, 'Drupal\Core\Block\BlockPluginInterface', 'Drupal\Core\Block\Annotation\Block');

That is the subdirectory it must be in, the interface it must implement, and the annotation it must use.

joachim’s picture

Cool!

Is there a way to find all the plugin manager classes? Do they cover all the plugin types?

joachim’s picture

Ah, it looks like this can possibly be done by finding all services of the form 'plugin.manager.FOO'. Certainly, this gets me what looks like a long list:

> ack -g services.yml | ack -x plugin.manager.

So:

- is that the complete list of plugin types?
- is there an API for getting all services by name, or all services by a pattern matching their name? Will that API get us to a class name, such as Drupal\Core\Block\BlockManager? The alternative would be just finding files based on name pattern and then regexping the contents -- which is doable too, but if there's a way through the API, so much the better.

dawehner’s picture

Is there a way to find all the plugin manager classes? Do they cover all the plugin types?

There is not yet an API for collect plugin types, yes there isn't one. For most of them you can just look for plugin.manager.* and add entity.manager
There isn't even a concept of a plugin "type" by name. #2031859: Invoke an event[s] when a plugin ID disappears uses the subdir, which is no solution for you. I plan to provide some API for that in the future.

joachim’s picture

> and add entity.manager

Urgh! Why can't entity.manager follow suit?

> There isn't even a concept of a plugin "type" by name

I think for the MB UI I'll use the part of the plugin manager service that comes after 'plugin.manager.'.

joachim’s picture

Urgh, we have some plugin managers that do their own thing. This is \Drupal\entity_reference\Plugin\Type\SelectionPluginManager:

    // We're not using the parent constructor because we use a different factory
    // method and don't need the derivative discovery decorator.
    $this->factory = new ReflectionFactory($this, '\Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface');

That means the approach in https://www.drupal.org/node/2086181#comment-9151315 won't work for everything :(

joachim’s picture

Ok using a combination of PHP reflection and tokenizing, I can get this sort of data about plugin types:

    [element_info] => Array
        (
            [type_id] => element_info
            [subdir] => 'Element'
            [plugin_interface] => 'Drupal\Core\Render\Element\ElementInterface'
            [plugin_definition_annotation_name] => 'Drupal\Core\Render\Annotation\RenderElement'
        )

    [field.field_type] => Array
        (
            [type_id] => field.field_type
            [subdir] => 'Plugin/Field/FieldType'
            [plugin_interface] => 'Drupal\Core\Field\FieldItemInterface'
            [plugin_definition_annotation_name] => 'Drupal\Core\Field\Annotation\FieldType'
        )

    [field.formatter] => Array
        (
            [type_id] => field.formatter
            [subdir] => 'Plugin/Field/FieldFormatter'
            [plugin_interface] => 'Drupal\Core\Field\FormatterInterface'
            [plugin_definition_annotation_name] => 'Drupal\Core\Field\Annotation\FieldFormatter'
        )

    [field.widget] => Array
        (
            [type_id] => field.widget
            [subdir] => 'Plugin/Field/FieldWidget'
            [plugin_interface] => 'Drupal\Core\Field\WidgetInterface'
            [plugin_definition_annotation_name] => 'Drupal\Core\Field\Annotation\FieldWidget'
        )

However, the following plugin managers don't follow the pattern and so don't get analyzed:

Unable to find call to parent constructor in plugin manager class constructor method for service plugin.manager.entity_reference.selection, class .
Unable to find call to parent constructor in plugin manager class constructor method for service plugin.manager.menu.contextual_link, class .
Unable to find call to parent constructor in plugin manager class constructor method for service plugin.manager.menu.link, class .
Unable to find call to parent constructor in plugin manager class constructor method for service plugin.manager.menu.local_action, class .
Unable to find call to parent constructor in plugin manager class constructor method for service plugin.manager.menu.local_task, class .

What can we do about these?

Can these be standardized, or should we conclude that the code analysis approach is flawed -- and in that case, seek to create a documentation standard for formally documenting the plugin service name/subdir/interface/annotation set in such a way that Module Builder (and others) can find it?

tim.plunkett’s picture

Well the menu stuff doesn't use annotated discovery, they use YAML files. So perhaps that is correct, and not really a problem?

joachim’s picture

I've pushed to work in progress code for this. If you now do 'drush mbdl' on a D8 site, you'll get a plugins_processed.php file containing all the data as a serialized array.

joachim’s picture

> Well the menu stuff doesn't use annotated discovery, they use YAML files. So perhaps that is correct, and not really a problem?

I don't really follow what you're getting at, sorry. Are you saying MB doesn't need to generate plugins relating to the menu system?

And what about the plugin.manager.entity_reference.selection plugin type?

At any rate, I've carried on with the plugin type sniffing described in #5 -- we can refine that later and it's enough to go on to work on generating the plugins.

It's pushed to the 7.x-2.x branch (which works on D8), and you can generate a block plugin like this:

drush mb foo plugins: block

Here's the code it generates (except that d.org appears to be eating the empty lines between methods!):

Proposed src/Plugin/Block/FooBlock.php:
<?php

/**
 * @file FooBlock.php
 * Contains \Drupal\foo\Plugin\Block\FooBlock
 */

namespace Drupal\foo\Plugin\Block;

use yadayada;

/**
 * @Block
 *   id = "TODO: replace this with a value",
 *   admin_label = @Translation("TODO: replace this with a value"),
 *   category = @Translation("TODO: replace this with a value"),
 *   derivative = "TODO: replace this with a value",
 */

class FooBlock {

  /**
   * {@inheritdoc}
   */
  public function label() {
  }

  /**
   * {@inheritdoc}
   */
  public function access(AccountInterface $account) {
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
  }
SNIP

Some problems I can see:

- the 'derivative' annotation property isn't in the block plugins in core that I've looked at. However, it appears here because it's a public property of the annotation class
- I've no way of figuring out which namespaces to bring in with the 'use' line. Is that just something that will vary for each different individual plugin implementation, and so something that MB shouldn't bother with?
- Blocks seems to inherit from BlockBase. I don't think I've seen a way of detecting that base class.
- the interface is giving me a TON of methods. Are all of them useful -- are there any that no block plugin is ever likely to implement? It seems a bit overwhelming to developers to be faced with so many. I'll work on extracting the first line of docblock from the interface, and adding that to the generated plugin as a code comment, to help get developers started.

Would be great if someone who understands D8 plugins better than me could give this a spin and see if it looks ok.

Chi’s picture

Is there a way to find all the plugin manager classes? Do they cover all the plugin types?

Here is an example adopted from the Plugin module:

foreach (\Drupal::getContainer()->getServiceIds() as $serviceId) {
  if (strpos($serviceId, 'plugin.manager') === 0) {
     var_dump(get_class(\Drupal::service($serviceId)));
  }
}

The code of the plugins is so varied. I think it would be more flexible to have a separate generator for each plugin type. I am going to build plugin generators in that way in the Drupal Code Generator. Feel free to take any code or ideas from it if necessary.

joachim’s picture

Status: Active » Fixed

This is now implemented in DCB.

Status: Fixed » Closed (fixed)

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