mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-01 00:40:52 +00:00
7d0f151a40
a2cd7283fb
Typo fix comment php 7.3 compat on rector workflow (#3432)
147 lines
5.8 KiB
PHP
147 lines
5.8 KiB
PHP
<?php
|
|
|
|
declare (strict_types=1);
|
|
namespace Rector\Parallel\Command;
|
|
|
|
use Rector\ChangesReporting\Output\JsonOutputFormatter;
|
|
use Rector\Core\Configuration\Option;
|
|
use RectorPrefix202303\Symfony\Component\Console\Command\Command;
|
|
use RectorPrefix202303\Symfony\Component\Console\Input\InputInterface;
|
|
use RectorPrefix202303\Symplify\EasyParallel\Exception\ParallelShouldNotHappenException;
|
|
use RectorPrefix202303\Symplify\EasyParallel\Reflection\CommandFromReflectionFactory;
|
|
/**
|
|
* @see \Rector\Tests\Parallel\Command\WorkerCommandLineFactoryTest
|
|
* @todo possibly extract to symplify/easy-parallel
|
|
*/
|
|
final class WorkerCommandLineFactory
|
|
{
|
|
/**
|
|
* @var string
|
|
*/
|
|
private const OPTION_DASHES = '--';
|
|
/**
|
|
* @readonly
|
|
* @var \Symplify\EasyParallel\Reflection\CommandFromReflectionFactory
|
|
*/
|
|
private $commandFromReflectionFactory;
|
|
public function __construct()
|
|
{
|
|
$this->commandFromReflectionFactory = new CommandFromReflectionFactory();
|
|
}
|
|
/**
|
|
* @param class-string<Command> $mainCommandClass
|
|
*/
|
|
public function create(string $mainScript, string $mainCommandClass, string $workerCommandName, InputInterface $input, string $identifier, int $port) : string
|
|
{
|
|
$commandArguments = \array_slice($_SERVER['argv'], 1);
|
|
$args = \array_merge([\PHP_BINARY, $mainScript], $commandArguments);
|
|
$workerCommandArray = [];
|
|
$mainCommand = $this->commandFromReflectionFactory->create($mainCommandClass);
|
|
if ($mainCommand->getName() === null) {
|
|
$errorMessage = \sprintf('The command name for "%s" is missing', \get_class($mainCommand));
|
|
throw new ParallelShouldNotHappenException($errorMessage);
|
|
}
|
|
$mainCommandName = $mainCommand->getName();
|
|
$mainCommandNames = [$mainCommandName, $mainCommandName[0]];
|
|
foreach ($args as $arg) {
|
|
// skip command name
|
|
if (\in_array($arg, $mainCommandNames, \true)) {
|
|
break;
|
|
}
|
|
$workerCommandArray[] = \escapeshellarg((string) $arg);
|
|
}
|
|
$workerCommandArray[] = $workerCommandName;
|
|
$mainCommandOptionNames = $this->getCommandOptionNames($mainCommand);
|
|
$workerCommandOptions = $this->mirrorCommandOptions($input, $mainCommandOptionNames);
|
|
$workerCommandArray = \array_merge($workerCommandArray, $workerCommandOptions);
|
|
// for TCP local server
|
|
$workerCommandArray[] = '--port';
|
|
$workerCommandArray[] = $port;
|
|
$workerCommandArray[] = '--identifier';
|
|
$workerCommandArray[] = \escapeshellarg($identifier);
|
|
/** @var string[] $paths */
|
|
$paths = $input->getArgument(Option::SOURCE);
|
|
foreach ($paths as $path) {
|
|
$workerCommandArray[] = \escapeshellarg($path);
|
|
}
|
|
// set json output
|
|
$workerCommandArray[] = self::OPTION_DASHES . Option::OUTPUT_FORMAT;
|
|
$workerCommandArray[] = \escapeshellarg(JsonOutputFormatter::NAME);
|
|
// disable colors, breaks json_decode() otherwise
|
|
// @see https://github.com/symfony/symfony/issues/1238
|
|
$workerCommandArray[] = '--no-ansi';
|
|
if ($input->hasOption(Option::CONFIG)) {
|
|
$workerCommandArray[] = '--config';
|
|
/**
|
|
* On parallel, the command is generated with `--config` addition
|
|
* Using escapeshellarg() to ensure the --config path escaped, even when it has a space.
|
|
*
|
|
* eg:
|
|
* --config /path/e2e/parallel with space/rector.php
|
|
*
|
|
* that can cause error:
|
|
*
|
|
* File /rector-src/e2e/parallel\" was not found
|
|
*
|
|
* the escaped result is:
|
|
*
|
|
* --config '/path/e2e/parallel with space/rector.php'
|
|
*
|
|
* tested in macOS and Ubuntu (github action)
|
|
*/
|
|
$workerCommandArray[] = \escapeshellarg((string) $input->getOption(Option::CONFIG));
|
|
}
|
|
return \implode(' ', $workerCommandArray);
|
|
}
|
|
private function shouldSkipOption(InputInterface $input, string $optionName) : bool
|
|
{
|
|
if (!$input->hasOption($optionName)) {
|
|
return \true;
|
|
}
|
|
// skip output format, not relevant in parallel worker command
|
|
return $optionName === Option::OUTPUT_FORMAT;
|
|
}
|
|
/**
|
|
* @return string[]
|
|
*/
|
|
private function getCommandOptionNames(Command $command) : array
|
|
{
|
|
$inputDefinition = $command->getDefinition();
|
|
$optionNames = [];
|
|
foreach ($inputDefinition->getOptions() as $inputOption) {
|
|
$optionNames[] = $inputOption->getName();
|
|
}
|
|
return $optionNames;
|
|
}
|
|
/**
|
|
* Keeps all options that are allowed in check command options
|
|
*
|
|
* @param string[] $mainCommandOptionNames
|
|
* @return string[]
|
|
*/
|
|
private function mirrorCommandOptions(InputInterface $input, array $mainCommandOptionNames) : array
|
|
{
|
|
$workerCommandOptions = [];
|
|
foreach ($mainCommandOptionNames as $mainCommandOptionName) {
|
|
if ($this->shouldSkipOption($input, $mainCommandOptionName)) {
|
|
continue;
|
|
}
|
|
/** @var bool|string|null $optionValue */
|
|
$optionValue = $input->getOption($mainCommandOptionName);
|
|
// skip clutter
|
|
if ($optionValue === null) {
|
|
continue;
|
|
}
|
|
if (\is_bool($optionValue)) {
|
|
if ($optionValue) {
|
|
$workerCommandOptions[] = self::OPTION_DASHES . $mainCommandOptionName;
|
|
}
|
|
continue;
|
|
}
|
|
$workerCommandOptions[] = self::OPTION_DASHES . $mainCommandOptionName;
|
|
$workerCommandOptions[] = \escapeshellarg($optionValue);
|
|
}
|
|
return $workerCommandOptions;
|
|
}
|
|
}
|