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 |