2
2
3
3
namespace T3Docs \GuidesExtension \Command ;
4
4
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 ;
7
14
use Symfony \Component \Console \Command \Command ;
8
- use Symfony \Component \Console \Input \ArrayInput ;
9
15
use Symfony \Component \Console \Input \InputArgument ;
10
- use Symfony \Component \Console \Input \InputDefinition ;
11
16
use Symfony \Component \Console \Input \InputInterface ;
12
17
use Symfony \Component \Console \Input \InputOption ;
18
+ use Symfony \Component \Console \Output \ConsoleOutputInterface ;
13
19
use Symfony \Component \Console \Output \OutputInterface ;
20
+ use Symfony \Component \EventDispatcher \EventDispatcher ;
14
21
use Symfony \Component \Finder \Finder ;
15
- use Symfony \Component \Process \Process ;
16
22
use T3Docs \Typo3DocsTheme \Settings \Typo3DocsInputSettings ;
17
23
18
24
final class RunDecorator extends Command
@@ -36,29 +42,66 @@ final class RunDecorator extends Command
36
42
'README.md ' => 'md ' ,
37
43
];
38
44
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
41
57
{
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
+ );
44
87
45
- $ this ->innerCommand -> addOption (
88
+ $ this ->addOption (
46
89
'localization ' ,
47
90
null ,
48
91
InputArgument::OPTIONAL ,
49
92
'Render a specific localization (for example "de_DE", "ru_RU", ...) ' ,
50
93
);
51
94
52
95
// This option is evaluated in the PostProjectNodeCreated event in packages/typo3-docs-theme/src/EventListeners/AddThemeSettingsToProjectNode.php
53
- $ this ->innerCommand -> addOption (
96
+ $ this ->addOption (
54
97
'minimal-test ' ,
55
98
null ,
56
99
InputOption::VALUE_NONE ,
57
100
'Apply preset for minimal testing (format=singlepage) ' ,
58
101
);
59
-
60
102
}
61
103
104
+
62
105
protected function execute (InputInterface $ input , OutputInterface $ output ): int
63
106
{
64
107
$ options = [];
@@ -71,25 +114,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
71
114
}
72
115
73
116
$ arguments = $ input ->getArguments ();
117
+ $ guessedInput = [];
74
118
if ($ arguments ['input ' ] === null ) {
75
119
$ 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 ) ;
78
122
}
79
123
80
124
if (!isset ($ options ['--output ' ])) {
81
- $ options [ ' -- output '] = getcwd () . '/ ' . self ::DEFAULT_OUTPUT_DIRECTORY ;
125
+ $ input -> setOption ( ' output ', getcwd () . '/ ' . self ::DEFAULT_OUTPUT_DIRECTORY ) ;
82
126
}
83
127
84
- $ input = new ArrayInput (
85
- [
86
- ...$ arguments ,
87
- ...$ options ,
88
- ...$ guessedInput ,
89
- ],
90
- $ this ->getDefinition ()
91
- );
92
-
93
128
// Propagate all input settings to be used within events
94
129
// through the Typo3DocsInputSettings singleton.
95
130
$ this ->inputSettings ->setInput ($ input );
@@ -107,7 +142,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
107
142
$ output ->writeln (sprintf ("<info>DEBUG</info> Using parameters: \n%s " , $ readableOutput ));
108
143
}
109
144
110
- $ baseExecution = $ this ->innerCommand -> execute ($ input , $ output );
145
+ $ baseExecution = $ this ->internalRun ($ input , $ output );
111
146
112
147
// When a localization is being rendered, no other sub-localizations
113
148
// are allowed, the execution will end here.
@@ -211,95 +246,7 @@ public function renderSingleLocalization(string $availableLocalization, array $b
211
246
$ output ->writeln (sprintf ("<info>DEBUG</info> Using parameters: \n%s " , $ readableOutput ));
212
247
}
213
248
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 );
303
250
}
304
251
305
252
/** @return array<string, string> */
@@ -363,4 +310,53 @@ private function guessInput(string $inputBaseDirectory, OutputInterface $output,
363
310
364
311
return [];
365
312
}
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
+ }
366
362
}
0 commit comments