| Total Complexity | 45 |
| Total Lines | 445 |
| Duplicated Lines | 0 % |
| Coverage | 77.29% |
| Changes | 0 | ||
Complex classes like AddPrefixCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AddPrefixCommand, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 37 | final class AddPrefixCommand extends BaseCommand |
||
| 38 | { |
||
| 39 | private const PATH_ARG = 'paths'; |
||
| 40 | private const PREFIX_OPT = 'prefix'; |
||
| 41 | private const OUTPUT_DIR_OPT = 'output-dir'; |
||
| 42 | private const FORCE_OPT = 'force'; |
||
| 43 | private const STOP_ON_FAILURE_OPT = 'stop-on-failure'; |
||
| 44 | private const CONFIG_FILE_OPT = 'config'; |
||
| 45 | private const CONFIG_FILE_DEFAULT = 'scoper.inc.php'; |
||
| 46 | private const NO_CONFIG_OPT = 'no-config'; |
||
| 47 | |||
| 48 | private $fileSystem; |
||
| 49 | private $scoper; |
||
| 50 | private $init = false; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @inheritdoc |
||
| 54 | */ |
||
| 55 | 16 | public function __construct(Filesystem $fileSystem, Scoper $scoper) |
|
| 56 | { |
||
| 57 | 16 | parent::__construct(); |
|
| 58 | |||
| 59 | 16 | $this->fileSystem = $fileSystem; |
|
| 60 | 16 | $this->scoper = new ConfigurableScoper($scoper); |
|
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * @inheritdoc |
||
| 65 | */ |
||
| 66 | 16 | protected function configure(): void |
|
| 67 | { |
||
| 68 | 16 | parent::configure(); |
|
| 69 | |||
| 70 | $this |
||
| 71 | 16 | ->setName('add-prefix') |
|
| 72 | 16 | ->setDescription('Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.') |
|
| 73 | 16 | ->addArgument( |
|
| 74 | 16 | self::PATH_ARG, |
|
| 75 | 16 | InputArgument::IS_ARRAY, |
|
| 76 | 16 | 'The path(s) to process.' |
|
| 77 | ) |
||
| 78 | 16 | ->addOption( |
|
| 79 | 16 | self::PREFIX_OPT, |
|
| 80 | 16 | 'p', |
|
| 81 | 16 | InputOption::VALUE_REQUIRED, |
|
| 82 | 16 | 'The namespace prefix to add.' |
|
| 83 | ) |
||
| 84 | 16 | ->addOption( |
|
| 85 | 16 | self::OUTPUT_DIR_OPT, |
|
| 86 | 16 | 'o', |
|
| 87 | 16 | InputOption::VALUE_REQUIRED, |
|
| 88 | 16 | 'The output directory in which the prefixed code will be dumped.', |
|
| 89 | 16 | 'build' |
|
| 90 | ) |
||
| 91 | 16 | ->addOption( |
|
| 92 | 16 | self::FORCE_OPT, |
|
| 93 | 16 | 'f', |
|
| 94 | 16 | InputOption::VALUE_NONE, |
|
| 95 | 16 | 'Deletes any existing content in the output directory without any warning.' |
|
| 96 | ) |
||
| 97 | 16 | ->addOption( |
|
| 98 | 16 | self::STOP_ON_FAILURE_OPT, |
|
| 99 | 16 | 's', |
|
| 100 | 16 | InputOption::VALUE_NONE, |
|
| 101 | 16 | 'Stops on failure.' |
|
| 102 | ) |
||
| 103 | 16 | ->addOption( |
|
| 104 | 16 | self::CONFIG_FILE_OPT, |
|
| 105 | 16 | 'c', |
|
| 106 | 16 | InputOption::VALUE_REQUIRED, |
|
| 107 | 16 | sprintf( |
|
| 108 | 16 | 'Configuration file. Will use "%s" if found by default.', |
|
| 109 | 16 | self::CONFIG_FILE_DEFAULT |
|
| 110 | ) |
||
| 111 | ) |
||
| 112 | 16 | ->addOption( |
|
| 113 | 16 | self::NO_CONFIG_OPT, |
|
| 114 | 16 | null, |
|
| 115 | 16 | InputOption::VALUE_NONE, |
|
| 116 | 16 | 'Do not look for a configuration file.' |
|
| 117 | ) |
||
| 118 | ; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * @inheritdoc |
||
| 123 | */ |
||
| 124 | 14 | protected function execute(InputInterface $input, OutputInterface $output): int |
|
| 125 | { |
||
| 126 | 14 | $io = new SymfonyStyle($input, $output); |
|
| 127 | 14 | $io->writeln(''); |
|
| 128 | |||
| 129 | 14 | $this->changeWorkingDirectory($input); |
|
| 130 | |||
| 131 | 14 | $this->validatePrefix($input); |
|
| 132 | 14 | $this->validatePaths($input); |
|
| 133 | 14 | $this->validateOutputDir($input, $io); |
|
| 134 | |||
| 135 | 14 | $config = $this->retrieveConfig($input, $output, $io); |
|
| 136 | 12 | $output = $input->getOption(self::OUTPUT_DIR_OPT); |
|
| 137 | |||
| 138 | 12 | if ([] !== $config->getWhitelistedFiles()) { |
|
| 139 | $this->scoper = $this->scoper->withWhitelistedFiles(...$config->getWhitelistedFiles()); |
||
| 140 | } |
||
| 141 | |||
| 142 | 12 | $logger = new ScoperLogger( |
|
| 143 | 12 | $this->getApplication(), |
|
| 144 | 12 | $io |
|
| 145 | ); |
||
| 146 | |||
| 147 | 12 | $logger->outputScopingStart( |
|
| 148 | 12 | $config->getPrefix(), |
|
| 149 | 12 | $input->getArgument(self::PATH_ARG) |
|
| 150 | ); |
||
| 151 | |||
| 152 | try { |
||
| 153 | 12 | $this->scopeFiles( |
|
| 154 | 12 | $config->getPrefix(), |
|
| 155 | 12 | $config->getFilesWithContents(), |
|
| 156 | 12 | $output, |
|
| 157 | 12 | $config->getPatchers(), |
|
| 158 | 12 | $config->getWhitelist(), |
|
| 159 | 12 | $input->getOption(self::STOP_ON_FAILURE_OPT), |
|
| 160 | 12 | $logger |
|
| 161 | ); |
||
| 162 | } catch (Throwable $throwable) { |
||
| 163 | $this->fileSystem->remove($output); |
||
| 164 | |||
| 165 | $logger->outputScopingEndWithFailure(); |
||
| 166 | |||
| 167 | throw $throwable; |
||
| 168 | } |
||
| 169 | |||
| 170 | 12 | $logger->outputScopingEnd(); |
|
| 171 | |||
| 172 | 12 | return 0; |
|
| 173 | } |
||
| 174 | |||
| 175 | /** |
||
| 176 | * @var callable[] |
||
| 177 | */ |
||
| 178 | 12 | private function scopeFiles( |
|
| 230 | } |
||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * @param callable[] $patchers |
||
| 235 | */ |
||
| 236 | 12 | private function scopeFile( |
|
| 237 | string $inputFilePath, |
||
| 238 | string $inputContents, |
||
| 239 | string $outputFilePath, |
||
| 240 | string $prefix, |
||
| 241 | array $patchers, |
||
| 242 | Whitelist $whitelist, |
||
| 243 | bool $stopOnFailure, |
||
| 244 | ScoperLogger $logger |
||
| 245 | ): void { |
||
| 246 | try { |
||
| 247 | 12 | $scoppedContent = $this->scoper->scope($inputFilePath, $inputContents, $prefix, $patchers, $whitelist); |
|
| 248 | 2 | } catch (Throwable $throwable) { |
|
| 249 | 2 | $exception = new ParsingException( |
|
| 250 | 2 | sprintf( |
|
| 251 | 2 | 'Could not parse the file "%s".', |
|
| 252 | 2 | $inputFilePath |
|
| 253 | ), |
||
| 254 | 2 | 0, |
|
| 255 | 2 | $throwable |
|
| 256 | ); |
||
| 257 | |||
| 258 | 2 | if ($stopOnFailure) { |
|
| 259 | throw $exception; |
||
| 260 | } |
||
| 261 | |||
| 262 | 2 | $logger->outputWarnOfFailure($inputFilePath, $exception); |
|
| 263 | |||
| 264 | 2 | $scoppedContent = file_get_contents($inputFilePath); |
|
| 265 | } |
||
| 266 | |||
| 267 | 12 | $this->fileSystem->dumpFile($outputFilePath, $scoppedContent); |
|
| 268 | |||
| 269 | 12 | if (false === isset($exception)) { |
|
| 270 | 11 | $logger->outputSuccess($inputFilePath); |
|
| 271 | } |
||
| 272 | } |
||
| 273 | |||
| 274 | 14 | private function validatePrefix(InputInterface $input): void |
|
| 275 | { |
||
| 276 | 14 | $prefix = $input->getOption(self::PREFIX_OPT); |
|
| 277 | |||
| 278 | 14 | if (null !== $prefix && 1 === preg_match('/(?<prefix>.*?)\\\\*$/', $prefix, $matches)) { |
|
|
1 ignored issue
–
show
|
|||
| 279 | 13 | $prefix = $matches['prefix']; |
|
| 280 | } |
||
| 281 | |||
| 282 | 14 | $input->setOption(self::PREFIX_OPT, $prefix); |
|
| 283 | } |
||
| 284 | |||
| 285 | 14 | private function validatePaths(InputInterface $input): void |
|
| 286 | { |
||
| 287 | 14 | $cwd = getcwd(); |
|
| 288 | 14 | $fileSystem = $this->fileSystem; |
|
| 289 | |||
| 290 | 14 | $paths = array_map( |
|
| 291 | static function (string $path) use ($cwd, $fileSystem) { |
||
| 292 | 9 | if (false === $fileSystem->isAbsolutePath($path)) { |
|
| 293 | return $cwd.DIRECTORY_SEPARATOR.$path; |
||
| 294 | } |
||
| 295 | |||
| 296 | 9 | return $path; |
|
| 297 | 14 | }, |
|
| 298 | 14 | $input->getArgument(self::PATH_ARG) |
|
| 299 | ); |
||
| 300 | |||
| 301 | 14 | $input->setArgument(self::PATH_ARG, $paths); |
|
| 302 | } |
||
| 303 | |||
| 304 | 14 | private function validateOutputDir(InputInterface $input, OutputStyle $io): void |
|
| 363 | } |
||
| 364 | } |
||
| 365 | |||
| 366 | 14 | private function retrieveConfig(InputInterface $input, OutputInterface $output, OutputStyle $io): Configuration |
|
| 367 | { |
||
| 456 | } |
||
| 457 | |||
| 458 | 12 | private function retrievePaths(InputInterface $input, Configuration $config): Configuration |
|
| 468 | } |
||
| 469 | |||
| 470 | 4 | private function makeAbsolutePath(string $path): string |
|
| 477 | } |
||
| 478 | |||
| 479 | 1 | private function generateRandomPrefix(): string |
|
| 484 |