Skip to content

Commit e7accd5

Browse files
committed
De-Duplicate Composer and CLI why-block commands
1 parent 328c05a commit e7accd5

File tree

12 files changed

+460
-336
lines changed

12 files changed

+460
-336
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2021 Navarr Barnier. All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Navarr\Depends\Controller\Cli;
10+
11+
12+
use Symfony\Component\Console\Command\Command;
13+
14+
class GatherRequirementsCommandController extends Command
15+
{
16+
protected function configure()
17+
{
18+
19+
}
20+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2021 Navarr Barnier. All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Navarr\Depends\Controller\Cli;
10+
11+
use InvalidArgumentException;
12+
use Navarr\Attribute\Dependency;
13+
use Navarr\Depends\Command\WhyBlockCommand;
14+
use Navarr\Depends\Controller\Common\WhyBlockUtility;
15+
use Navarr\Depends\Controller\Di\AddDefaultDefinitions;
16+
use Navarr\Depends\ScopeDeterminer\DirectoryScopeDeterminer;
17+
use Navarr\Depends\ScopeDeterminer\PhpFileFinder;
18+
use Navarr\Depends\ScopeDeterminer\ScopeDeterminerInterface;
19+
use Psr\Container\ContainerInterface;
20+
use Symfony\Component\Console\Command\Command;
21+
use Symfony\Component\Console\Input\InputArgument;
22+
use Symfony\Component\Console\Input\InputInterface;
23+
use Symfony\Component\Console\Output\OutputInterface;
24+
25+
class WhyBlockCliCommand extends Command
26+
{
27+
private const ARGUMENT_DIR = 'directory';
28+
29+
// phpcs:disable Generic.Files.LineLength.TooLong -- Attribute support pre PHP 8
30+
#[Dependency('symfony/console', '^5', 'Command\'s setName, addArgument and addOption methods as well as InputArgument\'s constants of REQUIRED and VALUE_NONE')]
31+
#[Dependency('php-di/php-di', '^6', 'DI\ContainerBuilder::addDefinitions and the existence of the DI\autowire function')]
32+
// phpcs:enable Generic.Files.LineLength.TooLong
33+
protected function configure(): void
34+
{
35+
WhyBlockUtility::addCommongArguments($this)
36+
->addArgument('directory', InputArgument::REQUIRED, 'Directory to search in');
37+
}
38+
39+
#[Dependency('symfony/console', '^5', 'InputInterface::getOption and OutputInterface::writeln')]
40+
protected function execute(
41+
InputInterface $input,
42+
OutputInterface $output
43+
): int {
44+
$optionValuesBuilder = new WhyBlockUtility\OptionValuesBuilder();
45+
$optionValues = $optionValuesBuilder->buildFromInput($input);
46+
$directory = $input->getArgument('directory');
47+
48+
if (!is_string($directory)) {
49+
throw new InvalidArgumentException('Only one directory is allowed');
50+
}
51+
52+
$containerBuilder = AddDefaultDefinitions::execute();
53+
WhyBlockUtility::addBaseDiDefinitions($containerBuilder, $input, $output, $optionValues);
54+
$containerBuilder->addDefinitions(
55+
[
56+
ScopeDeterminerInterface::class => static function (ContainerInterface $container) use ($directory) {
57+
return new DirectoryScopeDeterminer(
58+
$container->get(PhpFileFinder::class),
59+
$directory
60+
);
61+
},
62+
]
63+
);
64+
$container = $containerBuilder->build();
65+
66+
/** @var WhyBlockCommand $command */
67+
$command = $container->get(WhyBlockCommand::class);
68+
return $command->execute(
69+
$optionValues->getPackageToSearchFor(),
70+
$optionValues->getVersionToCompareTo()
71+
);
72+
}
73+
}

src/Controller/CliApplication.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace Navarr\Depends\Controller;
1010

1111
use Navarr\Attribute\Dependency;
12+
use Navarr\Depends\Controller\Cli\WhyBlockCliCommand;
1213
use Symfony\Component\Console\Application;
1314

1415
#[Dependency('symfony/console', '^5', 'Creates a Symfony Application')]
@@ -19,7 +20,7 @@ class CliApplication
1920
public static function execute(): int
2021
{
2122
$application = new Application('DepAnno', static::VERSION);
22-
$application->add(new WhyBlockCommandController());
23+
$application->add(new WhyBlockCliCommand());
2324
return $application->run();
2425
}
2526
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2021 Navarr Barnier. All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Navarr\Depends\Controller\Common;
10+
11+
use DI\ContainerBuilder;
12+
use Navarr\Attribute\Dependency;
13+
use Navarr\Depends\Command\WhyBlockCommand\CsvOutputHandler;
14+
use Navarr\Depends\Command\WhyBlockCommand\JsonOutputHandler;
15+
use Navarr\Depends\Command\WhyBlockCommand\OutputHandlerInterface;
16+
use Navarr\Depends\Command\WhyBlockCommand\StandardOutputHandler;
17+
use Navarr\Depends\Command\WhyBlockCommand\XmlOutputHandler;
18+
use Navarr\Depends\Controller\Common\WhyBlockUtility\OptionValues;
19+
use Navarr\Depends\IssueHandler\FailOnIssueHandler;
20+
use Navarr\Depends\IssueHandler\IssueHandlerInterface;
21+
use Navarr\Depends\IssueHandler\NotifyOnIssueHandler;
22+
use Navarr\Depends\Parser\AstParser;
23+
use Navarr\Depends\Parser\LegacyParser;
24+
use Navarr\Depends\Parser\ParserInterface;
25+
use Navarr\Depends\Parser\ParserPool;
26+
use Psr\Container\ContainerInterface;
27+
use Symfony\Component\Console\Command\Command;
28+
use Symfony\Component\Console\Input\InputArgument;
29+
use Symfony\Component\Console\Input\InputInterface;
30+
use Symfony\Component\Console\Input\InputOption;
31+
use Symfony\Component\Console\Output\OutputInterface;
32+
33+
use function DI\autowire;
34+
35+
class WhyBlockUtility
36+
{
37+
public const ARGUMENT_PACKAGE = 'package';
38+
public const ARGUMENT_VERSION = 'version';
39+
40+
public const OPTION_OUTPUT_FORMAT = 'format';
41+
public const OPTION_FAIL_ON_ERROR = 'fail-on-error';
42+
public const OPTION_INCLUDE_ANNOTATIONS = 'include-legacy-annotations';
43+
44+
public const FORMAT_TEXT = 'text';
45+
public const FORMAT_CSV = 'csv';
46+
public const FORMAT_JSON = 'json';
47+
public const FORMAT_XML = 'xml';
48+
49+
public const ACCEPTABLE_FORMATS = [
50+
self::FORMAT_CSV,
51+
self::FORMAT_TEXT,
52+
self::FORMAT_JSON,
53+
self::FORMAT_XML,
54+
];
55+
56+
public const FORMAT_MAPPER = [
57+
self::FORMAT_CSV => CsvOutputHandler::class,
58+
self::FORMAT_TEXT => StandardOutputHandler::class,
59+
self::FORMAT_JSON => JsonOutputHandler::class,
60+
self::FORMAT_XML => XmlOutputHandler::class,
61+
];
62+
63+
// phpcs:ignore Generic.Files.LineLength.TooLong -- Attribute support pre PHP 8
64+
#[Dependency('symfony/console', '^5', 'Command\'s setName, addArgument and addOption methods as well as InputArgument\'s constants of REQUIRED and VALUE_NONE')]
65+
public static function addCommongArguments(
66+
Command $command
67+
): Command {
68+
return $command->setName('why-block')
69+
->addArgument(
70+
static::ARGUMENT_PACKAGE,
71+
InputArgument::REQUIRED,
72+
'Package to search dependency attributes for'
73+
)
74+
->addArgument(
75+
static::ARGUMENT_VERSION,
76+
InputArgument::REQUIRED,
77+
'Version you want to update the package to'
78+
)
79+
->addOption(
80+
static::OPTION_OUTPUT_FORMAT,
81+
['f'],
82+
InputOption::VALUE_OPTIONAL,
83+
'Format to put results in. Accepted values: text, csv, json, xml'
84+
)
85+
->addOption(
86+
static::OPTION_FAIL_ON_ERROR,
87+
['e'],
88+
InputOption::VALUE_NONE,
89+
'Immediately fail on parsing errors'
90+
)
91+
->addOption(
92+
static::OPTION_INCLUDE_ANNOTATIONS,
93+
['l'],
94+
InputOption::VALUE_NONE,
95+
'Include old @dependency/@composerDependency annotations in search'
96+
);
97+
}
98+
99+
public static function addBaseDiDefinitions(
100+
ContainerBuilder $containerBuilder,
101+
InputInterface $input,
102+
OutputInterface $output,
103+
OptionValues $optionValues
104+
): ContainerBuilder {
105+
$formatMapper = WhyBlockUtility::FORMAT_MAPPER[$optionValues->getOutputFormat()];
106+
$containerBuilder->addDefinitions(
107+
[
108+
InputInterface::class => $input,
109+
OutputInterface::class => $output,
110+
IssueHandlerInterface::class => $optionValues->shouldFailOnParseError()
111+
? FailOnIssueHandler::class
112+
: NotifyOnIssueHandler::class,
113+
ParserInterface::class => static function (ContainerInterface $container) use ($optionValues) {
114+
$parsers = [$container->get(AstParser::class)];
115+
if ($optionValues->shouldIncludeAnnotationsInSearch()) {
116+
$parsers[] = $container->get(LegacyParser::class);
117+
}
118+
return new ParserPool($parsers);
119+
},
120+
OutputHandlerInterface::class => autowire($formatMapper),
121+
]
122+
);
123+
return $containerBuilder;
124+
}
125+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2021 Navarr Barnier. All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Navarr\Depends\Controller\Common\WhyBlockUtility;
10+
11+
use JetBrains\PhpStorm\Immutable;
12+
13+
#[Immutable(Immutable::CONSTRUCTOR_WRITE_SCOPE)]
14+
class OptionValues
15+
{
16+
/** @var bool */
17+
private $failOnError;
18+
19+
/** @var string */
20+
private $format;
21+
22+
/** @var bool */
23+
private $includeLegacy;
24+
25+
/** @var string */
26+
private $package;
27+
28+
/** @var string */
29+
private $version;
30+
31+
public function __construct(
32+
string $package,
33+
string $version,
34+
string $format,
35+
bool $includeLegacy = true,
36+
bool $failOnError = false
37+
) {
38+
$this->package = $package;
39+
$this->version = $version;
40+
$this->format = $format;
41+
$this->includeLegacy = $includeLegacy;
42+
$this->failOnError = $failOnError;
43+
}
44+
45+
public function getPackageToSearchFor(): string
46+
{
47+
return $this->package;
48+
}
49+
50+
public function getVersionToCompareTo(): string
51+
{
52+
return $this->version;
53+
}
54+
55+
public function getOutputFormat(): string
56+
{
57+
return $this->format;
58+
}
59+
60+
public function shouldIncludeAnnotationsInSearch(): bool
61+
{
62+
return $this->includeLegacy;
63+
}
64+
65+
public function shouldFailOnParseError(): bool
66+
{
67+
return $this->failOnError;
68+
}
69+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2021 Navarr Barnier. All Rights Reserved.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Navarr\Depends\Controller\Common\WhyBlockUtility;
10+
11+
use InvalidArgumentException;
12+
use Navarr\Depends\Controller\Common\WhyBlockUtility;
13+
use Symfony\Component\Console\Input\InputInterface;
14+
15+
class OptionValuesBuilder
16+
{
17+
public function buildFromInput(InputInterface $input): OptionValues
18+
{
19+
$packageToSearchFor = $input->getArgument(WhyBlockUtility::ARGUMENT_PACKAGE);
20+
$versionToCompareTo = $input->getArgument(WhyBlockUtility::ARGUMENT_VERSION);
21+
$outputFormat = $input->getOption(WhyBlockUtility::OPTION_OUTPUT_FORMAT);
22+
23+
if (!is_string($packageToSearchFor)) {
24+
throw new InvalidArgumentException('Only one package is allowed');
25+
}
26+
if (!is_string($versionToCompareTo)) {
27+
throw new InvalidArgumentException('Only one version is allowed');
28+
}
29+
if (!is_string($outputFormat)) {
30+
throw new InvalidArgumentException('Only one output format is allowed');
31+
}
32+
33+
$outputFormat = strtolower($outputFormat);
34+
if (!in_array($outputFormat, WhyBlockUtility::ACCEPTABLE_FORMATS)) {
35+
$outputFormat = 'text';
36+
}
37+
38+
$failOnError = (bool)$input->getOption(WhyBlockUtility::OPTION_FAIL_ON_ERROR);
39+
$includeAnnotations = (bool)$input->getOption(WhyBlockUtility::OPTION_INCLUDE_ANNOTATIONS);
40+
41+
return new OptionValues(
42+
$packageToSearchFor,
43+
$versionToCompareTo,
44+
$outputFormat,
45+
$includeAnnotations,
46+
$failOnError
47+
);
48+
}
49+
}

0 commit comments

Comments
 (0)