Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Annotated Commands in favor of native Symfony Console commands #6135

Open
wants to merge 49 commits into
base: 13.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2f63ffc
Deprecate AnnotatedCommands in favor of native Symfony Console commands
weitzman Oct 14, 2024
ec1f512
PHPCS and rename directory to just Listeners (was EventListener)
weitzman Oct 14, 2024
47bf610
PHPStan
weitzman Oct 14, 2024
a201b1e
Restore handle() methods on a few Formatter Attribute classes
weitzman Oct 14, 2024
756d384
A few fixes
weitzman Oct 14, 2024
000c215
Remove illegal operation
weitzman Oct 14, 2024
2eeb287
A bit ugly but test has to pass
weitzman Oct 14, 2024
ff65ece
Add --filter option automatically for Console commands. Implementatio…
weitzman Oct 14, 2024
f2f179d
Incorporate into the Trait
weitzman Oct 14, 2024
41a8aa6
Support --filter during execute() of formatter supporting commands
weitzman Oct 15, 2024
141fa70
Convert image:flush to a Console command. Add ValidateEntityLoad list…
weitzman Oct 15, 2024
2eea0de
Deprecate constants
weitzman Oct 15, 2024
68cad77
Inject a new io service when StyleInterface is the type hint
weitzman Oct 15, 2024
b3fbd3a
Add visibility and remove use of dt()
weitzman Oct 15, 2024
a194c59
Use io instead of logger for success messages
weitzman Oct 15, 2024
4a9f239
Deal with a PHPStan failure by aliasing SynfonyStyle to DrushStyle in…
weitzman Oct 15, 2024
e78b50a
in ImageFlushCommand, use choice() and remove the deprecation from it
weitzman Oct 15, 2024
2f42025
Convert sql:dump. Optionsets are provided via a Listener
weitzman Oct 15, 2024
ac5ad80
PHPCS
weitzman Oct 15, 2024
0239f0f
Experiment with static method to add OptionSets
weitzman Oct 16, 2024
8b16ff4
Demonstrate a static method approach for Validators
weitzman Oct 16, 2024
7d6e8ea
Experiment with FormatterOptions via code not Attributes
weitzman Oct 16, 2024
9847b61
Build io when needed - don't get it from the container
weitzman Oct 16, 2024
b9e11f3
Use DI
weitzman Oct 16, 2024
6216ad6
Less magic - commands that format have own execute() with a bit of bo…
weitzman Oct 18, 2024
8d7b2e4
FormatterOptions now has methods we need to deprecate formatting Attr…
weitzman Oct 18, 2024
1a795af
Revert Formatting Attributes and use new fluid syntax for setInput
weitzman Oct 18, 2024
680da18
Start restore of automatic mention of the formatting help topic
weitzman Oct 18, 2024
12e74f8
Pass formattterOptions during configure()
weitzman Oct 20, 2024
74becdc
Simplify ImageFlushCommands a bit
weitzman Oct 20, 2024
6c899f4
Start deprecating a few Attribute classes
weitzman Oct 21, 2024
74b697c
Minor improvements
weitzman Oct 24, 2024
5b55f98
Convert sql:sanitize command. Convert the UserTable sanitizer to a Li…
weitzman Oct 24, 2024
ebd367a
PHPStan fixes
weitzman Oct 24, 2024
34afdc7
Convert to UserTableFields listener. Needed for green
weitzman Oct 24, 2024
f923a80
Introduce writeFormattedOutput() - use in execute()
weitzman Oct 24, 2024
2e75912
Just put validators right into the Command
weitzman Oct 25, 2024
a2b44fd
Lets go with calling Optionset methods statically from configure()
weitzman Oct 25, 2024
b4ed0bf
Experiment with CommandTester - it works
weitzman Oct 25, 2024
58c2ddd
Move the install check to setUp()
weitzman Oct 26, 2024
c1056ed
A bit more experimentation with Applicationtester
weitzman Oct 30, 2024
8f4f573
Delay getting $application because that can make entitytypeManager stale
weitzman Oct 31, 2024
fdfaf0f
Dont store $application in local variable. it gets stale.
weitzman Oct 31, 2024
3712e5d
drush() method is good enough for now
weitzman Oct 31, 2024
f0ffbd9
Docs for Console commands
weitzman Nov 1, 2024
955511e
Merge branch '13.x' into use-command-directly
weitzman Nov 2, 2024
aa21ad9
Convert image:derive
weitzman Nov 2, 2024
8ef8994
Migrate config:set into a Console command
weitzman Nov 2, 2024
b9817b9
Config:get is now a Console command
weitzman Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Drush\Boot\DrupalBootLevels;
use Drush\Command\RemoteCommandProxy;
use Drush\Config\ConfigAwareTrait;
use Drush\Event\ConsoleDefinitionsEvent;
use Drush\Runtime\RedispatchHook;
use Drush\Runtime\ServiceManager;
use Drush\Runtime\TildeExpansionHook;
Expand Down Expand Up @@ -309,6 +310,8 @@ public function configureAndRegisterCommands(InputInterface $input, OutputInterf
// any of the configuration steps we do here.
$this->configureIO($input, $output);

$this->addListeners($commandfileSearchpath);

// Directly add the yaml-cli commands.
$this->addCommands($this->serviceManager->instantiateYamlCliCommands());

Expand All @@ -327,6 +330,9 @@ public function configureAndRegisterCommands(InputInterface $input, OutputInterf
// Note that Robo::register can accept either Annotated Command
// command handlers or Symfony Console Command objects.
Robo::register($this, $commandInstances);

// Dispatch our custom event. It also fires later in \Drush\Boot\DrupalBoot8::bootstrapDrupalFull.
Drush::getContainer()->get('eventDispatcher')->dispatch(new ConsoleDefinitionsEvent($this), ConsoleDefinitionsEvent::class);
}

/**
Expand All @@ -338,4 +344,12 @@ public function renderThrowable(\Throwable $e, OutputInterface $output): void

$this->doRenderThrowable($e, $output);
}

// Discover event listeners, and add those that do not require bootstrap.
protected function addListeners($commandfileSearchpath): void
{
$listenerClasses = $this->serviceManager->discoverListeners($commandfileSearchpath, '\Drush');
$listenerClasses = $this->serviceManager->filterListeners($listenerClasses);
$this->serviceManager->addListeners($listenerClasses, Drush::getContainer());
}
}
21 changes: 19 additions & 2 deletions src/Attributes/DefaultFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,25 @@
namespace Drush\Attributes;

use Attribute;
use Drush\Formatters\FormatterConfigurationItemProviderInterface;

#[Attribute(Attribute::TARGET_METHOD)]
class DefaultFields extends \Consolidation\AnnotatedCommand\Attributes\DefaultFields
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class DefaultFields implements FormatterConfigurationItemProviderInterface
{
const KEY = 'default-fields';

/**
* @param $fields
* An array of field names to show by default.
*/
public function __construct(public array $fields)
{
}

public function getConfigurationItem(\ReflectionAttribute $attribute): array
{
$args = $attribute->getArguments();
return [self::KEY => $args['fields']];
}

}
20 changes: 18 additions & 2 deletions src/Attributes/DefaultTableFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,24 @@
namespace Drush\Attributes;

use Attribute;
use Drush\Formatters\FormatterConfigurationItemProviderInterface;

#[Attribute(Attribute::TARGET_METHOD)]
class DefaultTableFields extends \Consolidation\AnnotatedCommand\Attributes\DefaultTableFields
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class DefaultTableFields implements FormatterConfigurationItemProviderInterface
{
const KEY = 'default-table-fields';

/**
* @param $fields
* An array of field names to show by default when using table formatter.
*/
public function __construct(public array $fields)
{
}

public function getConfigurationItem(\ReflectionAttribute $attribute): array
{
$args = $attribute->getArguments();
return [self::KEY => $args['fields']];
}
}
24 changes: 20 additions & 4 deletions src/Attributes/FieldLabels.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
<?php

declare(strict_types=1);

namespace Drush\Attributes;

use Attribute;
use Drush\Formatters\FormatterConfigurationItemProviderInterface;

#[Attribute(Attribute::TARGET_METHOD)]
class FieldLabels extends \Consolidation\AnnotatedCommand\Attributes\FieldLabels
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class FieldLabels implements FormatterConfigurationItemProviderInterface
{

const KEY = 'field-labels';

/**
* @param $labels
* An associative array of field names and labels for display.
*/
public function __construct(
public array $labels
) {
}

public function getConfigurationItem(\ReflectionAttribute $attribute): array
{
$args = $attribute->getArguments();
return [self::KEY => $args['labels']];
}
}
23 changes: 19 additions & 4 deletions src/Attributes/FilterDefaultField.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
<?php

declare(strict_types=1);

namespace Drush\Attributes;

use Attribute;
use Drush\Formatters\FormatterConfigurationItemProviderInterface;

#[Attribute(Attribute::TARGET_METHOD)]
class FilterDefaultField extends \Consolidation\AnnotatedCommand\Attributes\FilterDefaultField
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class FilterDefaultField implements FormatterConfigurationItemProviderInterface
{
const KEY = 'filter-default-field';

/**
* @param $field
* A field name to filter on by default.
*/
public function __construct(
public string $field
) {
}

public function getConfigurationItem(\ReflectionAttribute $attribute): array
{
$args = $attribute->getArguments();
return [self::KEY => $args['field']];
}
}
1 change: 0 additions & 1 deletion src/Attributes/Format.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Attribute;
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Drush\Boot\Kernels;
use JetBrains\PhpStorm\ExpectedValues;

#[Attribute(Attribute::TARGET_METHOD)]
Expand Down
2 changes: 1 addition & 1 deletion src/Attributes/ValidateModulesEnabled.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\CommandError;

#[Attribute(Attribute::TARGET_METHOD)]
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class ValidateModulesEnabled extends ValidatorBase implements ValidatorInterface
{
/**
Expand Down
24 changes: 22 additions & 2 deletions src/Boot/DrupalBoot8.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
use Drush\Config\ConfigLocator;
use Drush\Drupal\DrushLoggerServiceProvider;
use Drush\Drush;
use Drush\Event\ConsoleDefinitionsEvent;
use Drush\Runtime\LegacyServiceFinder;
use Drush\Runtime\LegacyServiceInstantiator;
use Drush\Runtime\ServiceManager;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Robo\Robo;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand Down Expand Up @@ -215,12 +214,20 @@ public function bootstrapDrupalFull(BootstrapManager $manager): void
// Directly add the Drupal core bootstrapped commands.
Drush::getApplication()->addCommands($this->serviceManager->instantiateDrupalCoreBootstrappedCommands());

$this->addBootstrapListeners();

$this->addDrupalModuleDrushCommands($manager);

// Dispatch our custom event. It also fires earlier in \Drush\Application::configureAndRegisterCommands.
Drush::getContainer()->get('eventDispatcher')->dispatch(new ConsoleDefinitionsEvent(Drush::getApplication()), ConsoleDefinitionsEvent::class);

// Set a default account to make sure the correct timezone is set
$this->kernel->getContainer()->get('current_user')->setAccount(new AnonymousUserSession());
}

/**
* Adds module supplied commands, as well as Drush Console commands that require bootstrap.
*/
public function addDrupalModuleDrushCommands(BootstrapManager $manager): void
{
$application = Drush::getApplication();
Expand Down Expand Up @@ -323,4 +330,17 @@ public function bootstrapDrupalSite(BootstrapManager $manager)
{
$this->bootstrapDoDrupalSite($manager);
}

// Add the Listeners that require bootstrap.
public function addBootstrapListeners(): void
{
$listenersInThisModule = [];
$moduleHandler = \Drupal::moduleHandler();
foreach ($moduleHandler->getModuleList() as $moduleId => $extension) {
$path = DRUPAL_ROOT . '/' . $extension->getPath() . '/src/Drush';
$listenersInThisModule = array_merge($listenersInThisModule, $this->serviceManager->discoverListeners([$path], "\Drupal\\$moduleId\Drush"));
}
$classes = $this->serviceManager->bootstrapListenerClasses();
$this->serviceManager->addListeners(array_merge($listenersInThisModule, $classes), Drush::getContainer(), \Drupal::getContainer());
}
}
4 changes: 2 additions & 2 deletions src/Commands/core/CoreCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Drush\Commands\core;

use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\PropertyList;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;
use Drush\Drush;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Consolidation\OutputFormatters\Options\FormatterOptions;

final class CoreCommands extends DrushCommands
{
Expand Down
49 changes: 0 additions & 49 deletions src/Commands/core/TwigCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,22 @@

namespace Drush\Commands\core;

use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Core\DrupalKernelInterface;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Template\TwigEnvironment;
use Drush\Attributes as CLI;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands;
use Drush\Drush;
use Drush\Utils\StringUtils;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Finder;

final class TwigCommands extends DrushCommands
{
use AutowireTrait;

const UNUSED = 'twig:unused';
const COMPILE = 'twig:compile';
const DEBUG = 'twig:debug';

Expand All @@ -36,51 +32,6 @@ public function __construct(
) {
}

/**
* Find potentially unused Twig templates.
*
* Immediately before running this command, web crawl your entire web site. Or
* use your Production PHPStorage dir for comparison.
*/
#[CLI\Command(name: self::UNUSED, aliases: [])]
#[CLI\Argument(name: 'searchpaths', description: 'A comma delimited list of paths to recursively search')]
#[CLI\Usage(name: 'drush twig:unused --field=template /var/www/mass.local/docroot/modules/custom,/var/www/mass.local/docroot/themes/custom', description: 'Output a simple list of potentially unused templates.')]
#[CLI\FieldLabels(labels: ['template' => 'Template', 'compiled' => 'Compiled'])]
#[CLI\DefaultTableFields(fields: ['template', 'compiled'])]
public function unused($searchpaths): RowsOfFields
{
$unused = [];
$phpstorage = PhpStorageFactory::get('twig');

// Find all templates in the codebase.
$files = Finder::create()
->files()
->name('*.html.twig')
->exclude('tests')
->in(StringUtils::csvToArray($searchpaths));
$this->logger()->notice(dt('Found !count templates', ['!count' => count($files)]));

// Check to see if a compiled equivalent exists in PHPStorage
foreach ($files as $file) {
$relative = Path::makeRelative($file->getRealPath(), Drush::bootstrapManager()->getRoot());
$mainCls = $this->twig->getTemplateClass($relative);
$cache = $this->twig->getCache();
if ($cache) {
$key = $cache->generateKey($relative, $mainCls);
if (!$phpstorage->exists($key)) {
$unused[$key] = [
'template' => $relative,
'compiled' => $key,
];
}
} else {
throw new \Exception('There was a problem, please ensure your twig cache is enabled.');
}
}
$this->logger()->notice(dt('Found !count unused', ['!count' => count($unused)]));
return new RowsOfFields($unused);
}

/**
* Compile all Twig template(s).
*/
Expand Down
Loading