DevelCommands.php

Same filename in this branch
  1. 5.x src/Commands/DevelCommands.php
Same filename and directory in other branches
  1. 8.x-1.x src/Commands/DevelCommands.php
  2. 4.x src/Commands/DevelCommands.php

Namespace

Drupal\devel\Drush\Commands

File

src/Drush/Commands/DevelCommands.php

View source
<?php

namespace Drupal\devel\Drush\Commands;

use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Consolidation\SiteAlias\SiteAliasManagerInterface;
use Consolidation\SiteProcess\Util\Escape;
use Drupal\Component\Uuid\Php;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Utility\Token;
use Drush\Attributes as CLI;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands;
use Drush\Commands\pm\PmCommands;
use Drush\Drush;
use Drush\Exceptions\UserAbortException;
use Drush\Exec\ExecTrait;
use Drush\Utils\StringUtils;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Output\Output;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
final class DevelCommands extends DrushCommands {
  use AutowireTrait;
  use ExecTrait;
  const REINSTALL = 'devel:reinstall';
  const HOOK = 'devel:hook';
  const EVENT = 'devel:event';
  const TOKEN = 'devel:token';
  const UUID = 'devel:uuid';
  const SERVICES = 'devel:services';
  
  /**
   * Constructs a new DevelCommands object.
   */
  public function __construct(protected Token $token, protected EventDispatcherInterface $eventDispatcher, protected ModuleHandlerInterface $moduleHandler, private readonly SiteAliasManagerInterface $siteAliasManager) {
    parent::__construct();
  }
  
  /**
   * Gets the module handler.
   *
   * @return \Drupal\Core\Extension\ModuleHandlerInterface
   *   The moduleHandler.
   */
  public function getModuleHandler() : ModuleHandlerInterface {
    return $this->moduleHandler;
  }
  
  /**
   * Gets the event dispatcher.
   *
   * @return \Symfony\Contracts\EventDispatcher\EventDispatcherInterface
   *   The eventDispatcher.
   */
  public function getEventDispatcher() : EventDispatcherInterface {
    return $this->eventDispatcher;
  }
  
  /**
   * Gets the container.
   *
   * @return \Drupal\Component\DependencyInjection\ContainerInterface
   *   The container.
   */
  public function getContainer() : ContainerInterface {
    return Drush::getContainer()->get('service_container');
  }
  
  /**
   * Gets the token.
   *
   * @return \Drupal\Core\Utility\Token
   *   The token.
   */
  public function getToken() : Token {
    return $this->token;
  }
  
  /**
   * Uninstall, and Install modules.
   */
  public function reinstall($modules) : void {
    /** @var \Drush\SiteAlias\ProcessManager $process_manager */
    $process_manager = $this->processManager();
    $modules = StringUtils::csvToArray($modules);
    $modules_str = implode(',', $modules);
    $process = $process_manager->drush($this->siteAliasManager
      ->getSelf(), PmCommands::UNINSTALL, [
      $modules_str,
    ]);
    $process->mustRun();
    $process = $process_manager->drush($this->siteAliasManager
      ->getSelf(), PmCommands::INSTALL, [
      $modules_str,
    ]);
    $process->mustRun();
  }
  
  /**
   * List implementations of a given hook and optionally edit one.
   */
  public function hook(string $hook, string $implementation) : void {
    // Get implementations in the .install files as well.
    include_once DRUPAL_ROOT . '/core/includes/install.inc';
    drupal_load_updates();
    $info = $this->codeLocate($implementation . ('_' . $hook));
    $exec = self::getEditor('');
    $cmd = sprintf($exec, Escape::shellArg($info['file']));
    $process = $this->processManager()
      ->shell($cmd);
    $process->setTty(TRUE);
    $process->mustRun();
  }
  
  /**
   * Asks the user to select a hook implementation.
   */
  public function hookInteract(Input $input, Output $output) : void {
    $hook_implementations = [];
    if (!$input->getArgument('implementation')) {
      foreach (array_keys($this->moduleHandler
        ->getModuleList()) as $key) {
        if ($this->moduleHandler
          ->hasImplementations($input->getArgument('hook'), [
          $key,
        ])) {
          $hook_implementations[] = $key;
        }
      }
      if ($hook_implementations !== []) {
        if (!($choice = $this->io()
          ->select('Enter the number of the hook implementation you wish to view.', array_combine($hook_implementations, $hook_implementations)))) {
          throw new UserAbortException();
        }
        $input->setArgument('implementation', $choice);
      }
      else {
        throw new \Exception(dt('No implementations'));
      }
    }
  }
  
  /**
   * List implementations of a given event and optionally edit one.
   */
  public function event($event, $implementation) : void {
    $info = $this->codeLocate($implementation);
    $exec = self::getEditor('');
    $cmd = sprintf($exec, Escape::shellArg($info['file']));
    $process = $this->processManager()
      ->shell($cmd);
    $process->setTty(TRUE);
    $process->mustRun();
  }
  
  /**
   * Asks the user to select an event and the event's implementation.
   */
  public function interactEvent(Input $input, Output $output) : void {
    $event = $input->getArgument('event');
    if (!$event) {
      // @todo Expand this list.
      $events = [
        'kernel.controller',
        'kernel.exception',
        'kernel.request',
        'kernel.response',
        'kernel.terminate',
        'kernel.view',
      ];
      $events = array_combine($events, $events);
      if (!($event = $this->io()
        ->select('Enter the event you wish to explore.', $events))) {
        throw new UserAbortException();
      }
      $input->setArgument('event', $event);
    }
    /** @var \Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher */
    $event_dispatcher = $this->eventDispatcher;
    if ($implementations = $event_dispatcher->getListeners($event)) {
      $choices = [];
      foreach ($implementations as $implementation) {
        $callable = $implementation[0]::class . '::' . $implementation[1];
        $choices[$callable] = $callable;
      }
      if (!($choice = $this->io()
        ->select('Enter the number of the implementation you wish to view.', $choices))) {
        throw new UserAbortException();
      }
      $input->setArgument('implementation', $choice);
    }
    else {
      throw new \Exception(dt('No implementations.'));
    }
  }
  
  /**
   * List available tokens.
   */
  public function token($options = [
    'format' => 'table',
  ]) : RowsOfFields {
    $rows = [];
    $all = $this->token
      ->getInfo();
    foreach ($all['tokens'] as $group => $tokens) {
      foreach ($tokens as $key => $token) {
        $rows[] = [
          'group' => $group,
          'token' => $key,
          'name' => $token['name'],
        ];
      }
    }
    return new RowsOfFields($rows);
  }
  
  /**
   * Generate a Universally Unique Identifier (UUID).
   */
  public function uuid() : string {
    $uuid = new Php();
    return $uuid->generate();
  }
  
  /**
   * Get source code line for specified function or method.
   */
  public function codeLocate($function_name) : array {
    // Get implementations in the .install files as well.
    include_once DRUPAL_ROOT . '/core/includes/install.inc';
    drupal_load_updates();
    if (!str_contains($function_name, '::')) {
      if (!function_exists($function_name)) {
        throw new \Exception(dt('Function not found'));
      }
      $reflect = new \ReflectionFunction($function_name);
    }
    else {
      [
        $class,
        $method,
      ] = explode('::', $function_name);
      if (!method_exists($class, $method)) {
        throw new \Exception(dt('Method not found'));
      }
      $reflect = new \ReflectionMethod($class, $method);
    }
    return [
      'file' => $reflect->getFileName(),
      'startline' => $reflect->getStartLine(),
      'endline' => $reflect->getEndLine(),
    ];
  }
  
  /**
   * Get a list of available container services.
   */
  public function services(?string $prefix = NULL, array $options = [
    'format' => 'yaml',
  ]) : array {
    $container = $this->getContainer();
    $services = $container->getServiceIds();
    // If there is a prefix, try to find matches.
    if (isset($prefix)) {
      $services = preg_grep(sprintf('/%s/', $prefix), $services);
    }
    if (empty($services)) {
      throw new \Exception(dt('No container services found.'));
    }
    sort($services);
    return $services;
  }

}

Classes

Title Deprecated Summary
DevelCommands