Task/Collect/ServiceTagTypesCollector.php
public function collect($job_list) {
    $container_builder = $this->containerBuilderGetter->getContainerBuilder();

    $collectors_info = $this->getCollectorServiceIds($container_builder);

    $data = [];

    // Declare event subscriber services. These don't have collectors in Drupal
    // as they are native to Symfony, so we need to declare explicitly.
    $data['event_subscriber'] = [
      'label' => 'Event subscriber',
      'interface' => 'Symfony\Component\EventDispatcher\EventSubscriberInterface',
      'methods' => $this->methodCollector->collectMethods('Symfony\Component\EventDispatcher\EventSubscriberInterface'),
      // TODO: services of this type should go in the EventSubscriber namespace.
    ];

    foreach ($collectors_info as $service_name => $tag_infos) {
      // Sloppy array structure...
      $collector_type = $tag_infos['collector_type'];
      unset($tag_infos['collector_type']);

      $service_definition = $container_builder->getDefinition($service_name);
      $service_class = $service_definition->getClass();

      // A single service collector service can collect on more than one tag.
      foreach ($tag_infos as $tag_info) {
        $tag = $tag_info['tag'];

        if ($collector_type == 'service_id_collector') {
          // Service ID collectors don't give us anything but the service ID,
          // so nothing can be detected about the interface collected services
          // should use.
          // The only example of a service collector using this tag
          // in core is 'theme.negotiator', which is self-consuming: it expects
          // its tagged services to implement its own interface. So all we can
          // do is assume other implementations will do the same.
          $service_class_reflection = new \ReflectionClass($service_class);

          $label = CaseString::pascal($service_class_reflection->getShortName())->title();

          // Hope there's only one interface...
          $service_interfaces = $service_class_reflection->getInterfaceNames();
          $collected_services_interface = array_shift($service_interfaces);

          $interface_methods = $this->methodCollector->collectMethods($collected_services_interface);
        }
        else {
          // Service collectors tell us what the container should call on the
          // collector to add a tagged service, and so from that we can deduce
          // the interface that tagged services must implement.
          if (!isset($tag_info['call'])) {
            // Shouldn't normally happen, but protected against badly-declated
            // services.
            continue;
          }

          $collecting_method = $tag_info['call'];

          $collecting_methodR = new \ReflectionMethod($service_class, $collecting_method);
          $collecting_method_paramR = $collecting_methodR->getParameters();

          // TODO: skip if more than 1 param.
          // getNumberOfParameters

          $collected_services_interface = (string) $collecting_method_paramR[0]->getType();

          if (!interface_exists($collected_services_interface)) {
            // Shouldn't happen, as the typehint will be an interface the
            // collected services must implement.
            continue;
          }

          // Make a label from the interface name: take the short interface name,
          // and remove an 'Interface' suffix, convert to title case.
          $interface_pieces = explode('\\', $collected_services_interface);
          $label = array_pop($interface_pieces);
          $label = preg_replace('@Interface$@', '', $label);
          $label = CaseString::pascal($label)->title();

          $interface_methods = $this->methodCollector->collectMethods($collected_services_interface);
        }

        $data[$tag] = [
          'label' => $label,
          'collector_type' => $collector_type,
          'interface' => $collected_services_interface,
          'methods' => $interface_methods,
        ];
      }
    }

    // Sort by ID.
    ksort($data);

    return $data;
  }
public function collect($job_list) {
    $container_builder = $this->containerBuilderGetter->getContainerBuilder();

    $collectors_info = $this->getCollectorServiceIds($container_builder);

    $data = [];

    // Declare event subscriber services. These don't have collectors in Drupal
    // as they are native to Symfony, so we need to declare explicitly.
    $data['event_subscriber'] = [
      'label' => 'Event subscriber',
      'interface' => 'Symfony\Component\EventDispatcher\EventSubscriberInterface',
      'methods' => $this->methodCollector->collectMethods('Symfony\Component\EventDispatcher\EventSubscriberInterface'),
      // TODO: services of this type should go in the EventSubscriber namespace.
    ];

    foreach ($collectors_info as $service_name => $tag_infos) {
      // Sloppy array structure...
      $collector_type = $tag_infos['collector_type'];
      unset($tag_infos['collector_type']);

      $service_definition = $container_builder->getDefinition($service_name);
      $service_class = $service_definition->getClass();

      // A single service collector service can collect on more than one tag.
      foreach ($tag_infos as $tag_info) {
        $tag = $tag_info['tag'];

        if ($collector_type == 'service_id_collector') {
          // Service ID collectors don't give us anything but the service ID,
          // so nothing can be detected about the interface collected services
          // should use.
          // The only example of a service collector using this tag
          // in core is 'theme.negotiator', which is self-consuming: it expects
          // its tagged services to implement its own interface. So all we can
          // do is assume other implementations will do the same.
          $service_class_reflection = new \ReflectionClass($service_class);

          $label = CaseString::pascal($service_class_reflection->getShortName())->title();

          // Hope there's only one interface...
          $service_interfaces = $service_class_reflection->getInterfaceNames();
          $collected_services_interface = array_shift($service_interfaces);

          $interface_methods = $this->methodCollector->collectMethods($collected_services_interface);
        }
        else {
          // Service collectors tell us what the container should call on the
          // collector to add a tagged service, and so from that we can deduce
          // the interface that tagged services must implement.
          if (!isset($tag_info['call'])) {
            // Shouldn't normally happen, but protected against badly-declated
            // services.
            continue;
          }

          $collecting_method = $tag_info['call'];

          $collecting_methodR = new \ReflectionMethod($service_class, $collecting_method);
          $collecting_method_paramR = $collecting_methodR->getParameters();

          // TODO: skip if more than 1 param.
          // getNumberOfParameters

          $collected_services_interface = (string) $collecting_method_paramR[0]->getType();

          if (!interface_exists($collected_services_interface)) {
            // Shouldn't happen, as the typehint will be an interface the
            // collected services must implement.
            continue;
          }

          // Make a label from the interface name: take the short interface name,
          // and remove an 'Interface' suffix, convert to title case.
          $interface_pieces = explode('\\', $collected_services_interface);
          $label = array_pop($interface_pieces);
          $label = preg_replace('@Interface$@', '', $label);
          $label = CaseString::pascal($label)->title();

          $interface_methods = $this->methodCollector->collectMethods($collected_services_interface);
        }

        $data[$tag] = [
          'label' => $label,
          'collector_type' => $collector_type,
          'interface' => $collected_services_interface,
          'methods' => $interface_methods,
        ];
      }
    }

    // Sort by ID.
    ksort($data);

    return $data;
  }
public function collect ( $ job_list ) { $ container_builder = $ this -> containerBuilderGetter -> getContainerBuilder ( ) ; $ collectors_info = $ this -> getCollectorServiceIds ( $ container_builder ) ; $ data = [ ] ; // Declare event subscriber services. These don't have collectors in Drupal // as they are native to Symfony, so we need to declare explicitly. $ data [ 'event_subscriber' ] = [ 'label' => 'Event subscriber' , 'interface' => 'Symfony\Component\EventDispatcher\EventSubscriberInterface' , 'methods' => $ this -> methodCollector -> collectMethods ( 'Symfony\Component\EventDispatcher\EventSubscriberInterface' ) , // TODO: services of this type should go in the EventSubscriber namespace. ] ; foreach ( $ collectors_info as $ service_name => $ tag_infos ) { // Sloppy array structure... $ collector_type = $ tag_infos [ 'collector_type' ] ; unset ( $ tag_infos [ 'collector_type' ] ) ; $ service_definition = $ container_builder -> getDefinition ( $ service_name ) ; $ service_class = $ service_definition -> getClass ( ) ; // A single service collector service can collect on more than one tag. foreach ( $ tag_infos as $ tag_info ) { $ tag = $ tag_info [ 'tag' ] ; if ( $ collector_type == 'service_id_collector' ) { // Service ID collectors don't give us anything but the service ID, // so nothing can be detected about the interface collected services // should use. // The only example of a service collector using this tag // in core is 'theme.negotiator', which is self-consuming: it expects // its tagged services to implement its own interface. So all we can // do is assume other implementations will do the same. $ service_class_reflection = new \ ReflectionClass ( $ service_class ) ; $ label = CaseString :: pascal ( $ service_class_reflection -> getShortName ( ) ) -> title ( ) ; // Hope there's only one interface... $ service_interfaces = $ service_class_reflection -> getInterfaceNames ( ) ; $ collected_services_interface = array_shift ( $ service_interfaces ) ; $ interface_methods = $ this -> methodCollector -> collectMethods ( $ collected_services_interface ) ; } else { // Service collectors tell us what the container should call on the // collector to add a tagged service, and so from that we can deduce // the interface that tagged services must implement. if ( ! isset ( $ tag_info [ 'call' ] ) ) { // Shouldn't normally happen, but protected against badly-declated // services. continue ; } $ collecting_method = $ tag_info [ 'call' ] ; $ collecting_methodR = new \ ReflectionMethod ( $ service_class , $ collecting_method ) ; $ collecting_method_paramR = $ collecting_methodR -> getParameters ( ) ; // TODO: skip if more than 1 param. // getNumberOfParameters $ collected_services_interface = ( string ) $ collecting_method_paramR [ 0 ] -> getType ( ) ; if ( ! interface_exists ( $ collected_services_interface ) ) { // Shouldn't happen, as the typehint will be an interface the // collected services must implement. continue ; } // Make a label from the interface name: take the short interface name, // and remove an 'Interface' suffix, convert to title case. $ interface_pieces = explode ( '\\' , $ collected_services_interface ) ; $ label = array_pop ( $ interface_pieces ) ; $ label = preg_replace ( '@Interface$@' , '' , $ label ) ; $ label = CaseString :: pascal ( $ label ) -> title ( ) ; $ interface_methods = $ this -> methodCollector -> collectMethods ( $ collected_services_interface ) ; } $ data [ $ tag ] = [ 'label' => $ label , 'collector_type' => $ collector_type , 'interface' => $ collected_services_interface , 'methods' => $ interface_methods , ] ; } } // Sort by ID. ksort ( $ data ) ; return $ data ; }
Collect data on tagged services.

@return
An array whose keys are service tags, and whose values arrays containing:
- 'label': A label for the tag.
- 'interface': The fully-qualified name (without leading slash) of the
interface that each tagged service must implement.
- 'methods': An array of the methods of this interface, in the same
format as returned by MethodCollector::collectMethods().
Collect data on tagged services .