Passed
Push — master ( 81e780...d41685 )
by Mariano
03:19 queued 27s
created

createPhiremockPathIfNotExists()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 7
rs 10
1
<?php
2
/**
3
 * This file is part of Phiremock.
4
 *
5
 * Phiremock is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Lesser General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * Phiremock is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with Phiremock.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
namespace Mcustiel\Phiremock\Server\Cli\Commands;
20
21
use Mcustiel\Phiremock\Server\Factory\Factory;
22
use Mcustiel\Phiremock\Server\Http\ServerInterface;
23
use Mcustiel\Phiremock\Server\Utils\Config\Config;
24
use Mcustiel\Phiremock\Server\Utils\Config\ConfigBuilder;
25
use Mcustiel\Phiremock\Server\Utils\Config\Directory;
26
use Psr\Log\LoggerInterface;
27
use Symfony\Component\Console\Command\Command;
28
use Symfony\Component\Console\Input\InputInterface;
29
use Symfony\Component\Console\Input\InputOption;
30
use Symfony\Component\Console\Output\OutputInterface;
31
32
class PhiremockServerCommand extends Command
33
{
34
    const IP_HELP_MESSAGE = 'IP address of the interface where Phiremock must list for connections.';
35
    const DEFAULT_IP = '0.0.0.0';
36
    const PORT_HELP_MESSAGE = 'Port where Phiremock must list for connections.';
37
    const DEFAULT_PORT = 8086;
38
    const EXPECTATIONS_DIR_HELP_MESSAGE = 'Directory in which to search for expectation definition files.';
39
    const DEFAULT_EXPECTATIONS_DIR = '[USER_HOME_PATH]/.phiremock/expectations';
40
    const DEBUG_HELP_MESSAGE = 'Sets debug mode.';
41
    const CONFIG_PATH_HELP_MESSAGE = 'Directory in which to search for configuration files. Default: current directory.';
42
43
    /** @var Factory */
44
    private $factory;
45
    /** @var LoggerInterface */
46
    private $logger;
47
48
    public function __construct(Factory $factory)
49
    {
50
        $this->factory = $factory;
51
        parent::__construct('run');
52
    }
53
54
    protected function configure(): void
55
    {
56
        $this->setDescription('Runs Phiremock server')
57
            ->setHelp('This is the main command to run Phiremock as a HTTP server.')
58
            ->addOption(
59
                'ip',
60
                'i',
61
                InputOption::VALUE_REQUIRED,
62
                self::IP_HELP_MESSAGE
63
            )
64
            ->addOption(
65
                'port',
66
                'p',
67
                InputOption::VALUE_REQUIRED,
68
                self::PORT_HELP_MESSAGE
69
            )
70
            ->addOption(
71
                'expectations-dir',
72
                'e',
73
                InputOption::VALUE_REQUIRED,
74
                self::EXPECTATIONS_DIR_HELP_MESSAGE
75
            )
76
            ->addOption(
77
                'debug',
78
                'd',
79
                InputOption::VALUE_NONE,
80
                sprintf(self::DEBUG_HELP_MESSAGE)
81
            )
82
            ->addOption(
83
                'config-path',
84
                'c',
85
                InputOption::VALUE_REQUIRED,
86
                sprintf(self::CONFIG_PATH_HELP_MESSAGE)
87
            );
88
    }
89
90
    protected function execute(InputInterface $input, OutputInterface $output): int
91
    {
92
        $this->createPhiremockPathIfNotExists();
93
94
        $configPath = new Directory($input->getOption('config-path') ?? getcwd());
95
        $cliConfig = [];
96
        if ($input->getOption('ip')) {
97
            $cliConfig['ip'] = $input->getOption('ip');
98
        }
99
        if ($input->getOption('port')) {
100
            $cliConfig['port'] = $input->getOption('port');
101
        }
102
        if ($input->getOption('debug')) {
103
            $cliConfig['debug'] = true;
104
        }
105
        if ($input->getOption('expectations-dir')) {
106
            $cliConfig['expectations-dir'] = $this->factory
107
                ->createFileSystemService()
108
                ->getRealPath($input->getOption('expectations-dir'));
0 ignored issues
show
Bug introduced by
It seems like $input->getOption('expectations-dir') can also be of type string[]; however, parameter $path of Mcustiel\Phiremock\Commo...leSystem::getRealPath() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
                ->getRealPath(/** @scrutinizer ignore-type */ $input->getOption('expectations-dir'));
Loading history...
109
        }
110
111
        $config = (new ConfigBuilder($configPath))->build($cliConfig);
112
113
        $this->initializeLogger($config);
114
        $this->processFileExpectations($config);
115
        $this->startHttpServer($config);
116
117
        return 0;
118
    }
119
120
    private function createPhiremockPathIfNotExists()
121
    {
122
        $defaultExpectationsPath = ConfigBuilder::getDefaultExpectationsDir();
123
        if (!$defaultExpectationsPath->exists()) {
124
            $defaultExpectationsPath->create();
125
        } elseif (!$defaultExpectationsPath->isDirectory()) {
126
            throw new \Exception('Expectations path must be a directory');
127
        }
128
    }
129
130
    private function startHttpServer(Config $config): void
131
    {
132
        $httpServer = $this->factory->createHttpServer();
133
        $this->setUpHandlers($httpServer);
134
        $httpServer->listen($config->getInterfaceIp(), $config->getPort());
135
    }
136
137
    private function initializeLogger(Config $config): void
138
    {
139
        \define('IS_DEBUG_MODE', $config->isDebugMode());
140
141
        $this->logger = $this->factory->createLogger();
142
        $this->logger->info(
143
            sprintf(
144
                '[%s] Starting Phiremock%s...',
145
                date('Y-m-d H:i:s'),
146
                (IS_DEBUG_MODE ? ' in debug mode' : '')
147
            )
148
        );
149
    }
150
151
    private function processFileExpectations(Config $config): void
152
    {
153
        $expectationsDir = $config->getExpectationsPath()->asString();
154
        $this->logger->debug("Phiremock's expectation dir is set to: {$expectationsDir}");
155
        $this->factory
156
            ->createFileExpectationsLoader()
157
            ->loadExpectationsFromDirectory($expectationsDir);
158
    }
159
160
    private function setUpHandlers(ServerInterface $server): void
161
    {
162
        $handleTermination = function ($signal = 0) use ($server) {
0 ignored issues
show
Unused Code introduced by
The parameter $signal is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

162
        $handleTermination = function (/** @scrutinizer ignore-unused */ $signal = 0) use ($server) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
163
            $this->logger->info('Stopping Phiremock...');
164
            $server->shutdown();
165
            $this->logger->info('Bye bye');
166
        };
167
168
        $this->logger->debug('Registering shutdown function');
169
        register_shutdown_function($handleTermination);
170
171
        if (\function_exists('pcntl_signal')) {
172
            $this->logger->debug('PCNTL present: Installing signal handlers');
173
            pcntl_signal(SIGTERM, $handleTermination);
174
            pcntl_signal(SIGABRT, $handleTermination);
175
            pcntl_signal(SIGINT, $handleTermination);
176
        }
177
178
        $errorHandler = function ($severity, $message, $file, $line, $context = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

178
        $errorHandler = function ($severity, $message, $file, $line, /** @scrutinizer ignore-unused */ $context = null) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
179
            $errorInformation = sprintf('%s:%s (%s)', $file, $line, $message);
180
            if ($this->isError($severity)) {
181
                $this->logger->error($errorInformation);
182
                throw new \ErrorException($message, 0, $severity, $file, $line);
183
            }
184
            $this->logger->warning($errorInformation);
185
186
            return false;
187
        };
188
        set_error_handler($errorHandler);
189
    }
190
191
    private function isError(int $severity): bool
192
    {
193
        return \in_array(
194
            $severity,
195
            [
196
                E_COMPILE_ERROR,
197
                E_CORE_ERROR,
198
                E_USER_ERROR,
199
                E_PARSE,
200
                E_ERROR,
201
            ],
202
            true
203
        );
204
    }
205
}
206