Skip to content

Commit e3ed092

Browse files
authored
Extract child command factory into a class (#157)
1 parent ee7a38d commit e3ed092

8 files changed

+294
-101
lines changed

infection.json5

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@
6161
},
6262
"UnwrapArrayFilter": {
6363
"ignore": [
64-
"Webmozarts\\Console\\Parallelization\\ParallelExecutor::createChildCommand"
64+
"Webmozarts\\Console\\Parallelization\\Input\\ChildCommandFactory::createBaseCommand"
6565
]
6666
},
6767
"UnwrapArrayMap": {
6868
"ignore": [
69-
"Webmozarts\\Console\\Parallelization\\ParallelExecutor::createChildCommand"
69+
"Webmozarts\\Console\\Parallelization\\Input\\ChildCommandFactory::createBaseCommand"
7070
]
7171
}
7272
}

src/Input/ChildCommandFactory.php

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Webmozarts Console Parallelization package.
5+
*
6+
* (c) Webmozarts GmbH <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Webmozarts\Console\Parallelization\Input;
15+
16+
use Symfony\Component\Console\Input\InputDefinition;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Webmozart\Assert\Assert;
19+
use function array_filter;
20+
use function array_map;
21+
use function array_merge;
22+
use function array_slice;
23+
use function implode;
24+
use function Safe\getcwd;
25+
use function sprintf;
26+
27+
/**
28+
* @internal
29+
*/
30+
final class ChildCommandFactory
31+
{
32+
private string $phpExecutable;
33+
private string $scriptPath;
34+
private string $commandName;
35+
private InputDefinition $commandDefinition;
36+
37+
public function __construct(
38+
string $phpExecutable,
39+
string $scriptPath,
40+
string $commandName,
41+
InputDefinition $commandDefinition
42+
) {
43+
self::validateScriptPath($scriptPath);
44+
45+
$this->phpExecutable = $phpExecutable;
46+
$this->scriptPath = $scriptPath;
47+
$this->commandName = $commandName;
48+
$this->commandDefinition = $commandDefinition;
49+
}
50+
51+
/**
52+
* @return list<string>
53+
*/
54+
public function createChildCommand(InputInterface $input): array
55+
{
56+
return array_merge(
57+
$this->createBaseCommand($input),
58+
// Forward all the options except for "processes" to the children
59+
// this way the children can inherit the options such as env
60+
// or no-debug.
61+
InputOptionsSerializer::serialize(
62+
$this->commandDefinition,
63+
$input,
64+
['child', 'processes'],
65+
),
66+
);
67+
}
68+
69+
/**
70+
* @return list<string>
71+
*/
72+
private function createBaseCommand(
73+
InputInterface $input
74+
): array {
75+
return array_filter([
76+
$this->phpExecutable,
77+
$this->scriptPath,
78+
$this->commandName,
79+
implode(
80+
' ',
81+
// TODO: this looks suspicious: why do we need to take the first arg?
82+
// why is this not a specific arg?
83+
// why do we include optional arguments? (cf. options)
84+
// maybe has to do with the item arg but in that case it is incorrect...
85+
array_filter(
86+
array_slice(
87+
array_map('strval', $input->getArguments()),
88+
1,
89+
),
90+
),
91+
),
92+
'--child',
93+
]);
94+
}
95+
96+
private static function validateScriptPath(string $scriptPath): void
97+
{
98+
Assert::fileExists(
99+
$scriptPath,
100+
sprintf(
101+
'The script file could not be found at the path "%s" (working directory: %s)',
102+
$scriptPath,
103+
getcwd(),
104+
),
105+
);
106+
}
107+
}

src/Input/InputOptionsSerializer.php

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
use function sprintf;
2828
use function str_replace;
2929

30+
/**
31+
* @internal
32+
*/
3033
final class InputOptionsSerializer
3134
{
3235
private const ESCAPE_TOKEN_PATTERN = '/[\s\W]/';

src/ParallelExecutor.php

+5-74
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,16 @@
1414
namespace Webmozarts\Console\Parallelization;
1515

1616
use Symfony\Component\Console\Input\Input;
17-
use Symfony\Component\Console\Input\InputDefinition;
1817
use Symfony\Component\Console\Input\InputInterface;
1918
use Symfony\Component\Console\Output\OutputInterface;
2019
use Throwable;
2120
use Webmozart\Assert\Assert;
2221
use Webmozarts\Console\Parallelization\ErrorHandler\ErrorHandler;
23-
use Webmozarts\Console\Parallelization\Input\InputOptionsSerializer;
22+
use Webmozarts\Console\Parallelization\Input\ChildCommandFactory;
2423
use Webmozarts\Console\Parallelization\Input\ParallelizationInput;
2524
use Webmozarts\Console\Parallelization\Logger\Logger;
2625
use Webmozarts\Console\Parallelization\Process\ProcessLauncher;
2726
use Webmozarts\Console\Parallelization\Process\ProcessLauncherFactory;
28-
use function array_filter;
29-
use function array_map;
30-
use function array_merge;
31-
use function array_slice;
32-
use function implode;
3327
use function mb_strlen;
3428
use function sprintf;
3529

@@ -50,10 +44,6 @@ final class ParallelExecutor
5044
*/
5145
private $getItemName;
5246

53-
private string $commandName;
54-
55-
private InputDefinition $commandDefinition;
56-
5747
private ErrorHandler $errorHandler;
5848

5949
/**
@@ -93,9 +83,7 @@ final class ParallelExecutor
9383

9484
private string $progressSymbol;
9585

96-
private string $phpExecutable;
97-
98-
private string $scriptPath;
86+
private ChildCommandFactory $childCommandFactory;
9987

10088
private string $workingDirectory;
10189

@@ -133,8 +121,6 @@ public function __construct(
133121
callable $fetchItems,
134122
callable $runSingleCommand,
135123
callable $getItemName,
136-
string $commandName,
137-
InputDefinition $commandDefinition,
138124
ErrorHandler $errorHandler,
139125
$childSourceStream,
140126
int $batchSize,
@@ -144,23 +130,19 @@ public function __construct(
144130
callable $runBeforeBatch,
145131
callable $runAfterBatch,
146132
string $progressSymbol,
147-
string $phpExecutable,
148-
string $scriptPath,
133+
ChildCommandFactory $childCommandFactory,
149134
string $workingDirectory,
150135
?array $extraEnvironmentVariables,
151136
ProcessLauncherFactory $processLauncherFactory,
152137
callable $processTick
153138
) {
154139
self::validateSegmentSize($segmentSize);
155140
self::validateBatchSize($batchSize);
156-
self::validateScriptPath($scriptPath);
157141
self::validateProgressSymbol($progressSymbol);
158142

159143
$this->fetchItems = $fetchItems;
160144
$this->runSingleCommand = $runSingleCommand;
161145
$this->getItemName = $getItemName;
162-
$this->commandName = $commandName;
163-
$this->commandDefinition = $commandDefinition;
164146
$this->errorHandler = $errorHandler;
165147
$this->childSourceStream = $childSourceStream;
166148
$this->batchSize = $batchSize;
@@ -170,8 +152,7 @@ public function __construct(
170152
$this->runBeforeBatch = $runBeforeBatch;
171153
$this->runAfterBatch = $runAfterBatch;
172154
$this->progressSymbol = $progressSymbol;
173-
$this->phpExecutable = $phpExecutable;
174-
$this->scriptPath = $scriptPath;
155+
$this->childCommandFactory = $childCommandFactory;
175156
$this->workingDirectory = $workingDirectory;
176157
$this->extraEnvironmentVariables = $extraEnvironmentVariables;
177158
$this->processLauncherFactory = $processLauncherFactory;
@@ -365,20 +346,8 @@ private function createProcessLauncher(
365346
InputInterface $input,
366347
Logger $logger
367348
): ProcessLauncher {
368-
$enrichedChildCommand = array_merge(
369-
$this->createChildCommand($input),
370-
// Forward all the options except for "processes" to the children
371-
// this way the children can inherit the options such as env
372-
// or no-debug.
373-
InputOptionsSerializer::serialize(
374-
$this->commandDefinition,
375-
$input,
376-
['child', 'processes'],
377-
),
378-
);
379-
380349
return $this->processLauncherFactory->create(
381-
$enrichedChildCommand,
350+
$this->childCommandFactory->createChildCommand($input),
382351
$this->workingDirectory,
383352
$this->extraEnvironmentVariables,
384353
$numberOfProcesses,
@@ -389,32 +358,6 @@ private function createProcessLauncher(
389358
);
390359
}
391360

392-
/**
393-
* @return list<string>
394-
*/
395-
private function createChildCommand(InputInterface $input): array
396-
{
397-
return array_filter([
398-
$this->phpExecutable,
399-
$this->scriptPath,
400-
$this->commandName,
401-
implode(
402-
' ',
403-
// TODO: this looks suspicious: why do we need to take the first arg?
404-
// why is this not a specific arg?
405-
// why do we include optional arguments? (cf. options)
406-
// maybe has to do with the item arg but in that case it is incorrect...
407-
array_filter(
408-
array_slice(
409-
array_map('strval', $input->getArguments()),
410-
1,
411-
),
412-
),
413-
),
414-
'--child',
415-
]);
416-
}
417-
418361
/**
419362
* Called whenever data is received in the main process from a child process.
420363
*
@@ -459,18 +402,6 @@ private static function validateSegmentSize(int $segmentSize): void
459402
);
460403
}
461404

462-
private static function validateScriptPath(string $scriptPath): void
463-
{
464-
Assert::fileExists(
465-
$scriptPath,
466-
sprintf(
467-
'The script file could not be found at the path "%s" (working directory: %s)',
468-
$scriptPath,
469-
getcwd(),
470-
),
471-
);
472-
}
473-
474405
private static function validateProgressSymbol(string $progressSymbol): void
475406
{
476407
$symbolLength = mb_strlen($progressSymbol);

src/ParallelExecutorFactory.php

+7-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Console\Input\InputInterface;
1818
use Symfony\Component\Console\Output\OutputInterface;
1919
use Webmozarts\Console\Parallelization\ErrorHandler\ErrorHandler;
20+
use Webmozarts\Console\Parallelization\Input\ChildCommandFactory;
2021
use Webmozarts\Console\Parallelization\Process\PhpExecutableFinder;
2122
use Webmozarts\Console\Parallelization\Process\ProcessLauncherFactory;
2223
use Webmozarts\Console\Parallelization\Process\StandardSymfonyProcessFactory;
@@ -394,8 +395,6 @@ public function build(): ParallelExecutor
394395
$this->fetchItems,
395396
$this->runSingleCommand,
396397
$this->getItemName,
397-
$this->commandName,
398-
$this->commandDefinition,
399398
$this->errorHandler,
400399
$this->childSourceStream,
401400
$this->useDefaultBatchSize ? $this->segmentSize : $this->batchSize,
@@ -405,8 +404,12 @@ public function build(): ParallelExecutor
405404
$this->runBeforeBatch,
406405
$this->runAfterBatch,
407406
$this->progressSymbol,
408-
$this->phpExecutable,
409-
$this->scriptPath,
407+
new ChildCommandFactory(
408+
$this->phpExecutable,
409+
$this->scriptPath,
410+
$this->commandName,
411+
$this->commandDefinition,
412+
),
410413
$this->workingDirectory,
411414
$this->extraEnvironmentVariables,
412415
$this->processLauncherFactory,

0 commit comments

Comments
 (0)