Problem/Motivation

On Drupal Core issue #2264047: [meta] Document service definition tags, we were discussing how to document "service tags". A bit of background: Drupal 8 adopted Symfony's concept of Services. When you define a service, it can be tagged. But we do not have documentation on what the tags mean. We need a way to document them.

Proposed resolution

Adopt a new documentation tag @service_tag, with syntax similar to:

 * @service_tag foo
 *   Documentation for foo tag goes here. Explain what the tag means, and note that services tagged
 *   with this tag should implement this interface. Also reference related classes, such as collector classes.
 *   If the tag has parameters, use a syntax like this to document them:
 *   - parameter1: Description of parameter 1.
 *   - parameter2: (optional) Description of optional parameter 2. The default parameter2 is x.

This block of documentation would be put into a class or interface header related to the service tag, such as (in rough order of preference):
- An interface that services with this tag need to implement.
- A base class that services with this tag can extend.
- A service collector class that collects services with this tag.
- Some other relevant class, if none of the above applies.

Remaining tasks

Adopt or modify this standard.

Comments

jhodgdon created an issue.

drunken monkey’s picture

Sounds good to me.

joachim’s picture

The general principle looks good, though we might want to think about making this system extensible so it can be used for other parts of YML files -- see #2323895: [Meta] Document format/content of various YML files.

One thing that makes our YML files hard both to learn about and document is that in most cases, new properties and possible values for them can be invented by other modules. Eg, possible keys in a routing file's _default section can be invented by any service.

Here what we're actually documenting is possible value in a services.yml file's services.*.tags.name property:

services:
  access_check.cron:
    class: Drupal\system\Access\CronAccessCheck
    tags:
      - { name: access_check, applies_to: _access_system_cron }

So I am thinking if we could make the new docblock tag's name reflect that somehow, we could expand this to other YML docs.

jhodgdon’s picture

Hm... so in #3, .... if I'm understanding you correctly, you would rather have something more generic in place of @service_tag.... so the example in the issue summary currently would become something like:

 * @yml_component services tag foo
 *   Documentation for foo tag goes here. Explain what the tag means, and note that services tagged
 *   with this tag should implement this interface. Also reference related classes, such as collector classes.
 *   If the tag has parameters, use a syntax like this to document them:
 *   - parameter1: Description of parameter 1.
 *   - parameter2: (optional) Description of optional parameter 2. The default parameter2 is x.

And then we could document how to use _form in routing like this:

 * @yml_component routing defaults _form
 *   Document what you can put in a routing.yml file, in the defaults section, in component _form.

Then we could have a listing page that collects YML component documentation. It might have a sortable, filterable table like:

YML file type Component Item
services tag foo
routing defaults _form

Or something like that?

jhodgdon’s picture

One other note. The current proposal is to put these @service_tag or @yml_component things into the doc blocks for appropriate classes. But if we do that, we're very limited in what we can do with them -- all we can do is have some lines of documentation, but no @see or @ingroup (because, how would we distinguish between "this @see is for the @service_tag section" vs. "this @see is for the class itself").

So we might want to consider putting these things into .api.php files, and then in the appropriate class doc blocks, saying @see whatever_it_is.

However... if we want to do that, the name of whatever_it_is would need to be one string, not something like "routing defaults _form".

Hm.... not sure what to do...

joachim’s picture

I think the ' * @yml_component services tag foo' would need to somehow encapsulate the path into the structure, so then the docs could also list the possible values for a particular property in the place where they'll (eventually) document the structure of a particular yml file:

services:
  SERVICENAME:
    class: Drupal\system\Access\CronAccessCheck
    tags:
      - { name: ** possible values ** }

> However... if we want to do that, the name of whatever_it_is would need to be one string, not something like "routing defaults _form".

I'm wondering whether we need to come up with some sort of yml equivalent to api.php files...

jhodgdon’s picture

The point of @service_tag is that service tags are not specific to one service. They are a way of saying "This service I am defining should be collected with these other services of this type".

So we really do NOT want them to be organized by service.

joachim’s picture

Yes, agreed. My example was rubbish, sorry.

What I was trying to get at is that in D7 hook documentation, we'd document the structure, and in that we'd be able to say for a particular property 'Possible values are LIST'. So here, we should be able to say: "the possible values for MODULE.services.yml's services.NAME.tags.name property are: LIST".

jhodgdon’s picture

Um... I still don't understand #8...

The original objective here is to document the tags that can be put on services in *.services.yml files. For example, there is a tag called "event_subscriber". Any module can define a service and tag it event_subscriber. If they do that, they are saying "My service is an event subscriber", and they need to implement EventSubscriberInterface (which has a getSubscribedEvents() method on it). So, in the original proposal, we would want something like:

 * @service_tag event_subscriber
 *   Service tag for event subscriber services. The service class should implement
 *   \Symfony\Component\EventDispatcher\EventSubscriberInterface. See the
 *   @link foo_bar Foo Bar topic @endlink for more information.

The other tags that we need to define are similar -- if you tag a service 'foo', you are saying "This is a Foo type of service", and somewhere, there is a Foo service collector class that finds all of these Foo services, and instantiates them at appropriate times. The proposal is to put the service tag 'foo' documentation on the service collector, or on the interface that all Foo services need to implement. Or some other appropriate class.

joachim’s picture

What I mean is that eventually, documentation for services.yml files should say something like this:

- 'services': An array of the services this module provides, keyed by ID. Each service array has the following properties:
  - blah blah
  - tags: An array of blah blah. Possible values for tags are:
    - LIST COMPILED FROM THIS TAG
joachim’s picture

I'm probably overthinking this -- I was imagining that the @tag we use would magically describe where and which yml array it fits into.

But what we'd more sensibly do it refer to the tag in the yml docs, so something like:

<code>
- 'services': An array of the services this module provides, keyed by ID. Each service array has the following properties:
  - blah blah
  - tags: An array of blah blah. Possible values for tags are:
    - PLACEHOLDER FOR LIST OF ALL @yml_component services tag

and API module would insert the generated list.

jhodgdon’s picture

Oh. Yes. I think that would qualify as overthinking. And I am not sure how many other "things" like Service Tags there are, that we would even want to document this way.

If there are more, then let's list them and see what the commonalities are and whether it makes sense to document them in a generic way, and what it would look like, and how we would collate them into usable documentation.

If there aren't more, or aren't many more, then let's just concentrate here on service tags.

joachim’s picture

> And I am not sure how many other "things" like Service Tags there are, that we would even want to document this way.

Hmm you might have a point there.

Most of what needs documenting in other YML files such as routing, permissions, and the various menu ones is the keys only, and the values are arbitrary.

joachim’s picture

I went away and had a long think about this, and I've posted a proposal here: #2823463: Devise an extensible way to document extensible data structures.