Skip to content

Commit ca9d783

Browse files
committed
[TASK] Overwrite run command
Other than what we did before we do now a full overwrite of the run command. This allows us to remove a number of less optimal constructions in the command. The change itself is empowered by the changes in phpdocumentor/guides-cli 1.9 which now provides a number of helper classes to simplify self build run commands.
1 parent c6c698e commit ca9d783

File tree

6 files changed

+132
-129
lines changed

6 files changed

+132
-129
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"league/flysystem": "^1.1.10",
1212
"phpdocumentor/filesystem": "^1.7",
1313
"phpdocumentor/guides": "^1.7",
14-
"phpdocumentor/guides-cli": "^1.8.1",
14+
"phpdocumentor/guides-cli": "^1.9.0",
1515
"phpdocumentor/guides-graphs": "^1.0",
1616
"phpdocumentor/guides-markdown": "^1.7",
1717
"phpdocumentor/guides-restructured-text": "^1.7",

composer.lock

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/typo3-guides-extension/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
}
1010
},
1111
"require": {
12-
"phpdocumentor/guides-cli": "^1.8.1",
12+
"phpdocumentor/guides-cli": "^1.9.0",
1313
"symfony/clock": "^6.4",
1414
"t3docs/guides-php-domain": "^1.0"
1515
}

packages/typo3-guides-extension/resources/config/typo3-guides.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818
$container->services()
1919
->defaults()
2020
->autowire()
21-
->set(RunDecorator::class)
22-
->decorate(
23-
Run::class,
24-
)->args([service('.inner')])
21+
->set(Run::class, RunDecorator::class)
22+
->public()
23+
->tag('phpdoc.guides.cli.command')
2524
->set(\T3Docs\GuidesExtension\Renderer\SinglePageRenderer::class)
2625
->tag(
2726
'phpdoc.renderer.typerenderer',

packages/typo3-guides-extension/src/Command/RunDecorator.php

Lines changed: 110 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@
22

33
namespace T3Docs\GuidesExtension\Command;
44

5-
use phpDocumentor\Guides\Cli\Command\Run;
6-
use Symfony\Component\Console\Application;
5+
use League\Tactician\CommandBus;
6+
use Monolog\Handler\ErrorLogHandler;
7+
use Monolog\Handler\StreamHandler;
8+
use Monolog\Logger;
9+
use phpDocumentor\Guides\Cli\Command\ProgressBarSubscriber;
10+
use phpDocumentor\Guides\Cli\Command\SettingsBuilder;
11+
use phpDocumentor\Guides\Cli\Internal\RunCommand;
12+
use phpDocumentor\Guides\Cli\Logger\SpyProcessor;
13+
use Psr\Log\LogLevel;
714
use Symfony\Component\Console\Command\Command;
8-
use Symfony\Component\Console\Input\ArrayInput;
915
use Symfony\Component\Console\Input\InputArgument;
10-
use Symfony\Component\Console\Input\InputDefinition;
1116
use Symfony\Component\Console\Input\InputInterface;
1217
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
1319
use Symfony\Component\Console\Output\OutputInterface;
20+
use Symfony\Component\EventDispatcher\EventDispatcher;
1421
use Symfony\Component\Finder\Finder;
15-
use Symfony\Component\Process\Process;
1622
use T3Docs\Typo3DocsTheme\Settings\Typo3DocsInputSettings;
1723

1824
final class RunDecorator extends Command
@@ -36,29 +42,66 @@ final class RunDecorator extends Command
3642
'README.md' => 'md',
3743
];
3844

39-
private Run $innerCommand;
40-
public function __construct(Run $innerCommand, private readonly Typo3DocsInputSettings $inputSettings)
45+
public function __construct(
46+
private readonly Typo3DocsInputSettings $inputSettings,
47+
private readonly SettingsBuilder $settingsBuilder,
48+
private readonly CommandBus $commandBus,
49+
private readonly EventDispatcher $eventDispatcher,
50+
private readonly Logger $logger,
51+
private readonly ProgressBarSubscriber $progressBarSubscriber,
52+
) {
53+
parent::__construct('run');
54+
}
55+
56+
protected function configure(): void
4157
{
42-
parent::__construct($innerCommand->getName());
43-
$this->innerCommand = $innerCommand;
58+
$this->settingsBuilder->configureCommand($this);
59+
60+
$this->addOption(
61+
'log-path',
62+
null,
63+
InputOption::VALUE_REQUIRED,
64+
'Write rendering log to this path',
65+
);
66+
$this->addOption(
67+
'fail-on-log',
68+
null,
69+
InputOption::VALUE_NONE,
70+
'If set, returns a non-zero exit code as soon as any warnings/errors occur',
71+
);
72+
73+
$this->addOption(
74+
'fail-on-error',
75+
null,
76+
InputOption::VALUE_NONE,
77+
'If set, returns a non-zero exit code as soon as any errors occur',
78+
);
79+
80+
$this->addOption(
81+
'progress',
82+
null,
83+
InputOption::VALUE_NEGATABLE,
84+
'Whether to show a progress bar',
85+
true,
86+
);
4487

45-
$this->innerCommand->addOption(
88+
$this->addOption(
4689
'localization',
4790
null,
4891
InputArgument::OPTIONAL,
4992
'Render a specific localization (for example "de_DE", "ru_RU", ...)',
5093
);
5194

5295
// This option is evaluated in the PostProjectNodeCreated event in packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php
53-
$this->innerCommand->addOption(
96+
$this->addOption(
5497
'minimal-test',
5598
null,
5699
InputOption::VALUE_NONE,
57100
'Apply preset for minimal testing (format=singlepage)',
58101
);
59-
60102
}
61103

104+
62105
protected function execute(InputInterface $input, OutputInterface $output): int
63106
{
64107
$options = [];
@@ -71,25 +114,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
71114
}
72115

73116
$arguments = $input->getArguments();
117+
$guessedInput = [];
74118
if ($arguments['input'] === null) {
75119
$guessedInput = $this->guessInput(self::DEFAULT_INPUT_DIRECTORY, $output, false);
76-
} else {
77-
$guessedInput = [];
120+
$input->setArgument('input', $guessedInput['input']);
121+
$input->setOption('input-format', $guessedInput['--input-format'] ?? null);
78122
}
79123

80124
if (!isset($options['--output'])) {
81-
$options['--output'] = getcwd() . '/' . self::DEFAULT_OUTPUT_DIRECTORY;
125+
$input->setOption('output', getcwd() . '/' . self::DEFAULT_OUTPUT_DIRECTORY);
82126
}
83127

84-
$input = new ArrayInput(
85-
[
86-
...$arguments,
87-
...$options,
88-
...$guessedInput,
89-
],
90-
$this->getDefinition()
91-
);
92-
93128
// Propagate all input settings to be used within events
94129
// through the Typo3DocsInputSettings singleton.
95130
$this->inputSettings->setInput($input);
@@ -107,7 +142,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
107142
$output->writeln(sprintf("<info>DEBUG</info> Using parameters:\n%s", $readableOutput));
108143
}
109144

110-
$baseExecution = $this->innerCommand->execute($input, $output);
145+
$baseExecution = $this->internalRun($input, $output);
111146

112147
// When a localization is being rendered, no other sub-localizations
113148
// are allowed, the execution will end here.
@@ -211,95 +246,7 @@ public function renderSingleLocalization(string $availableLocalization, array $b
211246
$output->writeln(sprintf("<info>DEBUG</info> Using parameters:\n%s", $readableOutput));
212247
}
213248

214-
$processArguments = array_merge(['env', 'php', $_SERVER['PHP_SELF']], $this->retrieveLocalizationArgumentsFromCurrentArguments($input));
215-
216-
$process = new Process($processArguments);
217-
$output->writeln(sprintf('<info>SUB-PROCESS:</info> %s', $process->getCommandLine()));
218-
$hasErrors = false;
219-
$result = $process->run(function ($type, $buffer) use ($output, &$hasErrors): void {
220-
if ($type === Process::ERR) {
221-
$output->write('<error>' . $buffer . '</error>');
222-
$hasErrors = true;
223-
} else {
224-
$output->write($buffer);
225-
}
226-
});
227-
228-
if ($hasErrors) {
229-
return Command::FAILURE;
230-
}
231-
232-
return Command::SUCCESS;
233-
}
234-
235-
/** @return mixed[] */
236-
public function retrieveLocalizationArgumentsFromCurrentArguments(InputInterface $input): array
237-
{
238-
$arguments = $input->getArguments();
239-
$options = $input->getOptions();
240-
241-
$shellCommands = [];
242-
foreach ($options as $option => $value) {
243-
if (is_bool($value) && $value) {
244-
$shellCommands[] = "--$option";
245-
} elseif (is_string($value)) {
246-
$shellCommands[] = "--$option=" . $value;
247-
}
248-
}
249-
250-
// Localizations are rendered as a sub-process. There the progress bar
251-
// disturbs the output that is returned. We only want normal and error output then.
252-
$shellCommands[] = '--no-progress';
253-
254-
foreach ($arguments as $argument) {
255-
if (is_string($argument)) {
256-
$shellCommands[] = $argument;
257-
}
258-
}
259-
260-
return $shellCommands;
261-
}
262-
263-
public function getDescription(): string
264-
{
265-
return $this->innerCommand->getDescription();
266-
}
267-
268-
public function getHelp(): string
269-
{
270-
return $this->innerCommand->getHelp();
271-
}
272-
273-
public function setApplication(Application $application = null): void
274-
{
275-
parent::setApplication($application);
276-
$this->innerCommand->setApplication($application);
277-
}
278-
279-
/** @return mixed[] */
280-
public function getUsages(): array
281-
{
282-
return $this->innerCommand->getUsages();
283-
}
284-
285-
public function getNativeDefinition(): InputDefinition
286-
{
287-
return $this->innerCommand->getNativeDefinition();
288-
}
289-
290-
public function getSynopsis(bool $short = false): string
291-
{
292-
return $this->innerCommand->getSynopsis($short);
293-
}
294-
295-
public function getDefinition(): InputDefinition
296-
{
297-
return $this->innerCommand->getDefinition();
298-
}
299-
300-
public function mergeApplicationDefinition(bool $mergeArgs = true): void
301-
{
302-
$this->innerCommand->mergeApplicationDefinition($mergeArgs);
249+
return $this->internalRun($input, $output);
303250
}
304251

305252
/** @return array<string, string> */
@@ -363,4 +310,53 @@ private function guessInput(string $inputBaseDirectory, OutputInterface $output,
363310

364311
return [];
365312
}
313+
314+
private function internalRun(InputInterface $input, OutputInterface $output): int
315+
{
316+
$this->settingsBuilder->overrideWithInput($input);
317+
$projectNode = $this->settingsBuilder->createProjectNode();
318+
$settings = $this->settingsBuilder->getSettings();
319+
320+
$logPath = $settings->getLogPath();
321+
if ($logPath === 'php://stder') {
322+
$this->logger->setHandlers([new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::WARNING)]);
323+
} else {
324+
$this->logger->setHandlers([new StreamHandler($logPath . '/warning.log', Logger::WARNING), new StreamHandler($logPath . '/error.log', Logger::ERROR)]);
325+
}
326+
327+
if ($settings->isFailOnError()) {
328+
$spyProcessor = new SpyProcessor($settings->getFailOnError() ?? LogLevel::WARNING);
329+
$this->logger->pushProcessor($spyProcessor);
330+
}
331+
332+
if ($output instanceof ConsoleOutputInterface && $settings->isShowProgressBar()) {
333+
$this->progressBarSubscriber->subscribe($output, $this->eventDispatcher);
334+
}
335+
336+
$documents = $this->commandBus->handle(
337+
new RunCommand($settings, $projectNode, $input),
338+
);
339+
340+
$outputFormats = $settings->getOutputFormats();
341+
$outputDir = $settings->getOutput();
342+
if ($output->isQuiet() === false) {
343+
$lastFormat = '';
344+
345+
if (count($outputFormats) > 1) {
346+
$lastFormat = (count($outputFormats) > 2 ? ',' : '') . ' and ' . strtoupper((string) array_pop($outputFormats));
347+
}
348+
349+
$formatsText = strtoupper(implode(', ', $outputFormats)) . $lastFormat;
350+
351+
$output->writeln(
352+
'Successfully placed ' . (is_countable($documents) ? count($documents) : 0) . ' rendered ' . $formatsText . ' files into ' . $outputDir,
353+
);
354+
}
355+
356+
if ($settings->isFailOnError() && $spyProcessor->hasBeenCalled()) {
357+
return Command::FAILURE;
358+
}
359+
360+
return Command::SUCCESS;
361+
}
366362
}

0 commit comments

Comments
 (0)