| Total Complexity | 49 |
| Total Lines | 335 |
| Duplicated Lines | 0 % |
| Changes | 12 | ||
| Bugs | 3 | Features | 0 |
Complex classes like Serve 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 Serve, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 34 | class Serve extends AbstractCommand |
||
| 35 | { |
||
| 36 | /** @var boolean */ |
||
| 37 | protected $watcherEnabled; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * {@inheritdoc} |
||
| 41 | */ |
||
| 42 | protected function configure() |
||
| 43 | { |
||
| 44 | $this |
||
| 45 | ->setName('serve') |
||
| 46 | ->setDescription('Starts the built-in server') |
||
| 47 | ->setDefinition([ |
||
| 48 | new InputArgument('path', InputArgument::OPTIONAL, 'Use the given path as working directory'), |
||
| 49 | new InputOption('open', 'o', InputOption::VALUE_NONE, 'Open web browser automatically'), |
||
| 50 | new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Server host', 'localhost'), |
||
| 51 | new InputOption('port', null, InputOption::VALUE_REQUIRED, 'Server port', '8000'), |
||
| 52 | new InputOption('watch', 'w', InputOption::VALUE_NEGATABLE, 'Enable (or disable --no-watch) changes watcher (enabled by default)', true), |
||
| 53 | new InputOption('drafts', 'd', InputOption::VALUE_NONE, 'Include drafts'), |
||
| 54 | new InputOption('optimize', null, InputOption::VALUE_NEGATABLE, 'Enable (or disable --no-optimize) optimization of generated files'), |
||
| 55 | new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Set the path to extra config files (comma-separated)'), |
||
| 56 | new InputOption('clear-cache', null, InputOption::VALUE_OPTIONAL, 'Clear cache before build (optional cache key as regular expression)', false), |
||
| 57 | new InputOption('page', 'p', InputOption::VALUE_REQUIRED, 'Build a specific page'), |
||
| 58 | new InputOption('no-ignore-vcs', null, InputOption::VALUE_NONE, 'Changes watcher must not ignore VCS directories'), |
||
| 59 | new InputOption('metrics', 'm', InputOption::VALUE_NONE, 'Show build metrics (duration and memory) of each step'), |
||
| 60 | new InputOption('timeout', null, InputOption::VALUE_REQUIRED, 'Sets the process timeout (max. runtime) in seconds', 7200), // default is 2 hours |
||
| 61 | ]) |
||
| 62 | ->setHelp( |
||
| 63 | <<<'EOF' |
||
| 64 | The <info>%command.name%</> command starts the live-reloading-built-in web server. |
||
| 65 | |||
| 66 | <info>%command.full_name%</> |
||
| 67 | <info>%command.full_name% path/to/the/working/directory</> |
||
| 68 | <info>%command.full_name% --open</> |
||
| 69 | <info>%command.full_name% --drafts</> |
||
| 70 | <info>%command.full_name% --no-watch</> |
||
| 71 | |||
| 72 | You can use a custom host and port by using the <info>--host</info> and <info>--port</info> options: |
||
| 73 | |||
| 74 | <info>%command.full_name% --host=127.0.0.1 --port=8080</> |
||
| 75 | |||
| 76 | To build the website with an extra configuration file, you can use the <info>--config</info> option. |
||
| 77 | This is useful during local development to <comment>override some settings</comment> without modifying the main configuration: |
||
| 78 | |||
| 79 | <info>%command.full_name% --config=config/dev.yml</> |
||
| 80 | |||
| 81 | To start the server with changes watcher <comment>not ignoring VCS</comment> directories, run: |
||
| 82 | |||
| 83 | <info>%command.full_name% --no-ignore-vcs</> |
||
| 84 | |||
| 85 | To define the process <comment>timeout</comment> (in seconds), run: |
||
| 86 | |||
| 87 | <info>%command.full_name% --timeout=7200</> |
||
| 88 | EOF |
||
| 89 | ); |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * {@inheritdoc} |
||
| 94 | * |
||
| 95 | * @throws RuntimeException |
||
| 96 | */ |
||
| 97 | protected function execute(InputInterface $input, OutputInterface $output) |
||
| 270 | } |
||
| 271 | |||
| 272 | /** |
||
| 273 | * Build success actions. |
||
| 274 | */ |
||
| 275 | private function buildSuccessActions(OutputInterface $output): void |
||
| 276 | { |
||
| 277 | // writes `changes.flag` file |
||
| 278 | if ($this->watcherEnabled) { |
||
| 279 | Util\File::getFS()->dumpFile(Util::joinFile($this->getPath(), self::TMP_DIR, 'changes.flag'), time()); |
||
| 280 | } |
||
| 281 | // writes `headers.ini` file |
||
| 282 | $headers = $this->getBuilder()->getConfig()->get('server.headers'); |
||
| 283 | if (is_iterable($headers)) { |
||
| 284 | $output->writeln('Writing headers file...'); |
||
| 285 | Util\File::getFS()->remove(Util::joinFile($this->getPath(), self::TMP_DIR, 'headers.ini')); |
||
| 286 | foreach ($headers as $entry) { |
||
| 287 | Util\File::getFS()->appendToFile(Util::joinFile($this->getPath(), self::TMP_DIR, 'headers.ini'), "[{$entry['path']}]\n"); |
||
| 288 | foreach ($entry['headers'] ?? [] as $header) { |
||
| 289 | Util\File::getFS()->appendToFile(Util::joinFile($this->getPath(), self::TMP_DIR, 'headers.ini'), "{$header['key']} = \"{$header['value']}\"\n"); |
||
| 290 | } |
||
| 291 | } |
||
| 292 | } |
||
| 293 | } |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Sets up the watcher. |
||
| 297 | */ |
||
| 298 | private function setupWatcher(bool $noignorevcs = false): ResourceWatcher |
||
| 308 | } |
||
| 309 | |||
| 310 | /** |
||
| 311 | * Prepares server's files. |
||
| 312 | * |
||
| 313 | * @throws RuntimeException |
||
| 314 | */ |
||
| 315 | private function setUpServer(string $host, string $port): void |
||
| 352 | } |
||
| 353 | } |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Removes temporary directory. |
||
| 357 | * |
||
| 358 | * @throws RuntimeException |
||
| 359 | */ |
||
| 360 | public function tearDownServer(): void |
||
| 372 |