ServeCommand::configure()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 74
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 61
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 74
rs 8.8509
ccs 0
cts 59
cp 0
crap 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace BenTools\MercurePHP\Command;
4
5
use BenTools\MercurePHP\Configuration\Configuration;
6
use BenTools\MercurePHP\Hub\HubFactory;
7
use BenTools\MercurePHP\Hub\HubFactoryInterface;
8
use Psr\Log\LoggerInterface;
9
use Psr\Log\LogLevel;
10
use React\EventLoop\Factory;
11
use React\EventLoop\LoopInterface;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Logger\ConsoleLogger;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
19
use function BenTools\MercurePHP\nullify;
20
use function BenTools\MercurePHP\without_nullish_values;
21
22
final class ServeCommand extends Command
23
{
24
    protected static $defaultName = 'mercure:serve';
25
26
    private Configuration $configuration;
27
    private HubFactoryInterface $factory;
28
    private LoopInterface $loop;
29
    private ?LoggerInterface $logger;
30
31
    public function __construct(
32
        Configuration $configuration,
33
        HubFactoryInterface $factory,
34
        LoopInterface $loop,
35
        ?LoggerInterface $logger = null
36
    ) {
37
        parent::__construct();
38
        $this->configuration = $configuration;
39
        $this->factory = $factory;
40
        $this->loop = $loop;
41
        $this->logger = $logger;
42
    }
43
44
    protected function execute(InputInterface $input, OutputInterface $output): int
45
    {
46
        $loop = $this->loop;
47
        $output = new SymfonyStyle($input, $output);
48
        $logger = $this->logger ?? new ConsoleLogger($output, [LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL]);
0 ignored issues
show
Unused Code introduced by
The assignment to $logger is dead and can be removed.
Loading history...
49
        try {
50
            $config = $this->configuration->overrideWith(without_nullish_values($input->getOptions()))->asArray();
51
            $factory = $this->factory->withConfig($config);
52
            $loop->futureTick(
53
                function () use ($config, $output) {
54
                    $this->displayConfiguration($config, $output);
55
                }
56
            );
57
58
            $hub = $factory->create();
59
            $hub->run();
60
61
            if (\SIGINT === $hub->getShutdownSignal()) {
62
                $output->newLine(2);
63
                $output->writeln('SIGINT received. 😢');
64
                $output->writeln('Goodbye! 👋');
65
66
                return 0;
67
            }
68
69
            $output->error('Server process was killed unexpectedly.');
70
71
            return 1;
72
        } catch (\Exception $e) {
73
            $output->error($e->getMessage());
74
75
            return 1;
76
        }
77
    }
78
79
    protected function configure(): void
80
    {
81
        $this->setDescription('Runs the Mercure Hub as a standalone application.');
82
        $this->addOption(
83
            'addr',
84
            null,
85
            InputOption::VALUE_OPTIONAL,
86
            'The address to listen on.',
87
        )
88
            ->addOption(
89
                'transport-url',
90
                null,
91
                InputOption::VALUE_OPTIONAL,
92
                'The DSN to transport messages.',
93
            )
94
            ->addOption(
95
                'storage-url',
96
                null,
97
                InputOption::VALUE_OPTIONAL,
98
                'The DSN to store messages.',
99
            )
100
            ->addOption(
101
                'metrics-url',
102
                null,
103
                InputOption::VALUE_OPTIONAL,
104
                'The DSN to store metrics.',
105
            )
106
            ->addOption(
107
                'cors-allowed-origins',
108
                null,
109
                InputOption::VALUE_OPTIONAL,
110
                'A list of allowed CORS origins, can be * for all.',
111
            )
112
            ->addOption(
113
                'jwt-key',
114
                null,
115
                InputOption::VALUE_OPTIONAL,
116
                'The JWT key to use for both publishers and subscribers',
117
            )
118
            ->addOption(
119
                'jwt-algorithm',
120
                null,
121
                InputOption::VALUE_OPTIONAL,
122
                'The JWT verification algorithm to use for both publishers and subscribers, e.g. HS256 (default) or RS512.',
123
            )
124
            ->addOption(
125
                'publisher-jwt-key',
126
                null,
127
                InputOption::VALUE_OPTIONAL,
128
                'Must contain the secret key to valid publishers\' JWT, can be omitted if jwt_key is set.',
129
            )
130
            ->addOption(
131
                'publisher-jwt-algorithm',
132
                null,
133
                InputOption::VALUE_OPTIONAL,
134
                'The JWT verification algorithm to use for publishers, e.g. HS256 (default) or RS512.',
135
            )
136
            ->addOption(
137
                'subscriber-jwt-key',
138
                null,
139
                InputOption::VALUE_OPTIONAL,
140
                'Must contain the secret key to valid subscribers\' JWT, can be omitted if jwt_key is set.',
141
            )
142
            ->addOption(
143
                'subscriber-jwt-algorithm',
144
                null,
145
                InputOption::VALUE_OPTIONAL,
146
                'The JWT verification algorithm to use for subscribers, e.g. HS256 (default) or RS512.',
147
            )
148
            ->addOption(
149
                'allow-anonymous',
150
                null,
151
                InputOption::VALUE_NONE,
152
                'Allows subscribers with no valid JWT to connect.',
153
            );
154
    }
155
156
    private function displayConfiguration(array $config, SymfonyStyle $output): void
157
    {
158
        if (!$output->isVeryVerbose()) {
159
            return;
160
        }
161
162
        $rows = [];
163
        foreach ($config as $key => $value) {
164
            if (null === $value) {
165
                $value = '<fg=yellow>null</>';
166
            }
167
            if (\is_bool($value)) {
168
                $value = $value ? '<fg=green>true</>' : '<fg=red>false</>';
169
            }
170
            $rows[] = [$key, $value];
171
        }
172
173
        $output->table(['Key', 'Value'], $rows);
174
    }
175
176
    private function getInputOptions(InputInterface $input): array
0 ignored issues
show
Unused Code introduced by
The method getInputOptions() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
177
    {
178
        return \array_filter(
179
            $input->getOptions(),
180
            fn($value) => null !== nullify($value) && false !== $value
181
        );
182
    }
183
}
184