Passed
Push — 1.0.x ( 8353d5...3a2c37 )
by Julien
21:28
created

Bootstrap   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Test Coverage

Coverage 66.97%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 127
c 1
b 0
f 0
dl 0
loc 324
ccs 73
cts 109
cp 0.6697
rs 8.8798
wmc 44

23 Methods

Rating   Name   Duplication   Size   Complexity  
A registerConfig() 0 7 2
A getDI() 0 3 1
A setConfig() 0 3 1
A getMode() 0 3 1
A setMode() 0 5 2
A setDI() 0 9 2
A getConfig() 0 3 1
A getRouter() 0 3 1
A setRouter() 0 3 1
A initialize() 0 2 1
A __construct() 0 11 1
A handleApplication() 0 4 3
A getArgs() 0 16 5
A registerModules() 0 12 2
A isConsole() 0 3 1
A registerServices() 0 12 5
A bootServices() 0 3 1
A isMvc() 0 3 1
A isDefault() 0 3 1
A registerRouter() 0 7 2
A handleConsole() 0 19 5
A run() 0 21 3
A isCli() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Bootstrap 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 Bootstrap, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of the Zemit Framework.
5
 *
6
 * (c) Zemit Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zemit;
13
14
use Phalcon\Application\AbstractApplication;
15
use Phalcon\Di\Di;
16
use Phalcon\Di\DiInterface;
17
use Phalcon\Di\FactoryDefault;
18
use Phalcon\Events;
19
use Phalcon\Http\ResponseInterface;
20
use Zemit\Support\Helper;
21
use Zemit\Config\ConfigInterface;
22
use Zemit\Cli\Console;
23
use Zemit\Events\EventsAwareTrait;
24
use Zemit\Mvc\Application;
25
use Zemit\Provider\Config\ServiceProvider as ConfigServiceProvider;
26
use Zemit\Provider\Router\ServiceProvider as RouterServiceProvider;
27
use Zemit\Router\RouterInterface;
28
use Zemit\Support\Php;
29
use Docopt;
30
31
/**
32
 * Zemit Core's Bootstrap for the MVC Application & CLI Console mode
33
 */
34
class Bootstrap
35
{
36
    use EventsAwareTrait;
37
    
38
    public const MODE_CLI = 'cli';
39
    public const MODE_MVC = 'mvc';
40
    
41
    public const MODE_DEFAULT = self::MODE_MVC;
42
    public const MODE_CONSOLE = self::MODE_CLI;
43
    
44
    public string $mode;
45
    
46
    public ?array $args;
47
    
48
    public DiInterface $di;
49
    
50
    public ConfigInterface $config;
51
    
52
    public RouterInterface $router;
53
    
54
    public ?ResponseInterface $response;
55
    
56
    public string $cliDoc = <<<DOC
57
Zemit CLI
58
59
Usage:
60
  zemit <module> <task> [<action>] [--help | --quiet | --verbose] [--debug] [--format=<format>] [<params>...]
61
  zemit (-h | --help)
62
  zemit (-v | --version)
63
  zemit (-i | --info)
64
65
Options:
66
  -h --help               show this help message
67
  -v --version            print version number
68
  -i --info               print information
69
  -q --quiet              suppress output
70
  -V --verbose            increase verbosity
71
  -d --debug              enable debug mode
72
  --format=<format>       change output returned value format (json, xml, serialized, raw, dump)
73
74
Tasks:
75
  cache                  Wipe the cache
76
  cron                   Run the scheduled task
77
  database               Create, optimize, truncate or drop tables within the database
78
  data-life-cycle        Delete old data based on the data life cycle definitions
79
  scaffold               Generating files and folders structure
80
  test                   Return the memory usage to see if the CLI works
81
  user                   Manage the users and passwords
82
83
DOC;
84
    
85
    /**
86
     * @throws Exception
87
     */
88 23
    public function __construct(string $mode = null)
89
    {
90 23
        $this->setMode($mode);
91 23
        $this->setEventsManager(new Events\Manager());
92 23
        $this->setDI();
93 23
        $this->initialize();
94 23
        $this->registerConfig();
95 23
        $this->registerServices();
96 23
        $this->bootServices();
97 23
        $this->registerModules();
98 23
        $this->registerRouter();
99
    }
100
    
101
    /**
102
     * Initialisation
103
     */
104 23
    public function initialize(): void
105
    {
106 23
    }
107
    
108
    /**
109
     * Set the default DI
110
     */
111 23
    public function setDI(?DiInterface $di = null): void
112
    {
113 23
        $di ??= $this->isCli()
114 2
            ? new FactoryDefault\Cli()
115 21
            : new FactoryDefault();
116
        
117 23
        $this->di = $di;
118 23
        $this->di->setShared('bootstrap', $this);
119 23
        Di::setDefault($this->di);
120
    }
121
    
122 23
    public function setMode(?string $mode = null): void
123
    {
124 23
        $this->mode = $mode ?? (Php::isCli()
125
            ? self::MODE_CLI
126
            : self::MODE_MVC
127
        );
128
    }
129
    
130 23
    public function getMode(): string
131
    {
132 23
        return $this->mode;
133
    }
134
    
135
    /**
136
     * Get the default DI
137
     */
138 2
    public function getDI(): DiInterface
139
    {
140 2
        return $this->di;
141
    }
142
    
143
    /**
144
     * Set the Config
145
     */
146 1
    public function setConfig(ConfigInterface $config): void
147
    {
148 1
        $this->config = $config;
149
    }
150
    
151
    /**
152
     * Get the Config
153
     */
154 2
    public function getConfig(): ConfigInterface
155
    {
156 2
        return $this->config;
157
    }
158
    
159
    /**
160
     * Set the MVC or CLI Router
161
     */
162
    public function setRouter(RouterInterface $router): void
163
    {
164
        $this->router = $router;
165
    }
166
    
167
    /**
168
     * Get the MVC or CLI Router
169
     */
170
    public function getRouter(): RouterInterface
171
    {
172
        return $this->router;
173
    }
174
    
175
    /**
176
     * Register Config
177
     */
178 23
    public function registerConfig(): void
179
    {
180 23
        if (!$this->di->has('config')) {
181 23
            $configService = new ConfigServiceProvider($this->di);
182 23
            $configService->register($this->di);
183
        }
184 23
        $this->config ??= $this->di->get('config');
185
    }
186
    
187
    /**
188
     * Register Service Providers
189
     * @throws Exception
190
     */
191 23
    public function registerServices(?array $providers = null): void
192
    {
193 23
        $providers ??= $this->config->pathToArray('providers') ?? [];
194 23
        foreach ($providers as $key => $provider) {
195 23
            if (!is_string($provider)) {
196
                throw new Exception('Service Provider `' . $key . '` class name must be a string.', 400);
197
            }
198 23
            if (!class_exists($provider)) {
199
                throw new Exception('Service Provider `' . $key . '` class  `' . $provider . '` not found.', 404);
200
            }
201 23
            if ($this->di instanceof Di) {
202 23
                $this->di->register(new $provider($this->di));
203
            }
204
        }
205
    }
206
    
207
    /**
208
     * Register Router
209
     */
210 23
    public function registerRouter(): void
211
    {
212 23
        if (!$this->di->has('router')) {
213
            $configService = new RouterServiceProvider($this->di);
214
            $configService->register($this->di);
215
        }
216 23
        $this->router ??= $this->di->get('router');
217
    }
218
    
219
    /**
220
     * Boot Service Providers
221
     */
222 23
    public function bootServices(): void
223
    {
224 23
        $this->di->get('debug');
225
    }
226
    
227
    /**
228
     * Register modules
229
     */
230 23
    public function registerModules(AbstractApplication $application = null, ?array $modules = null, ?string $defaultModule = null): void
231
    {
232 23
        $application ??= $this->isMvc()
233 21
            ? $this->di->get('application')
234 2
            : $this->di->get('console');
235 23
        assert($application instanceof AbstractApplication);
236
        
237 23
        $modules ??= $this->config->pathToArray('modules') ?? [];
238 23
        $application->registerModules($modules);
239
        
240 23
        $defaultModule ??= $this->config->path('router.defaults.module');
241 23
        $application->setDefaultModule($defaultModule);
242
    }
243
    
244
    /**
245
     * Handle cli or mvc application
246
     * @throws \Exception
247
     */
248
    public function run(): ?string
249
    {
250
        $this->fire('beforeRun');
251
        
252
        if ($this->isMvc()) {
253
            
254
            $application = $this->di->get('application');
255
            $content = $this->handleApplication($application);
256
        }
257
        elseif ($this->isCli()) {
258
            
259
            $console = $this->di->get('console');
260
            $content = $this->handleConsole($console);
261
        }
262
        else {
263
            throw new \Exception('Application or Console not found', 404);
264
        }
265
        
266
        $this->fire('afterRun', $content);
267
        
268
        return $content;
269
    }
270
    
271
    /**
272
     * Handle Console (For CLI only)
273
     */
274
    public function handleConsole(Console $console): ?string
275
    {
276
        $response = null;
277
        try {
278
            ob_start();
279
            $console->handle($this->getArgs());
280
            $response = ob_get_clean() ?: null;
281
        }
282
        catch (\Zemit\Exception $e) {
283
            new Cli\ExceptionHandler($e);
284
        }
285
        catch (\Exception $exception) {
286
            new Cli\ExceptionHandler($exception);
287
        }
288
        catch (\Throwable $throwable) {
289
            new Cli\ExceptionHandler($throwable);
290
        }
291
        
292
        return $response;
293
    }
294
    
295
    /**
296
     * Handle Application
297
     * @throws \Exception
298
     */
299
    public function handleApplication(Application $application): string
300
    {
301
        $this->response = $application->handle($_SERVER['REQUEST_URI'] ?? '/') ?: null;
302
        return $this->response ? $this->response->getContent() : '';
303
    }
304
    
305
    /**
306
     * Get & format args from the $this->args property
307
     */
308 1
    public function getArgs(): array
309
    {
310 1
        $args = [];
311
        
312 1
        if ($this->isCli()) {
313 1
            $argv = array_slice($_SERVER['argv'] ?? [], 1);
314 1
            $response = (new Docopt())->handle($this->cliDoc, ['argv' => $argv, 'optionsFirst' => true]);
315 1
            foreach ($response as $key => $value) {
316 1
                if (!is_null($value) && preg_match('/(<(.*?)>|\-\-(.*))/', $key, $match)) {
317 1
                    $key = lcfirst(Helper::camelize(Helper::uncamelize(array_pop($match))));
318 1
                    $args[$key] = $value;
319
                }
320
            }
321
        }
322
        
323 1
        return $args;
324
    }
325
    
326
    /**
327
     * Return true if the bootstrap mode is set to 'cli'
328
     */
329 23
    public function isCli(): bool
330
    {
331 23
        return $this->getMode() === self::MODE_CLI;
332
    }
333
    
334
    /**
335
     * Return true if the bootstrap mode is set to 'mvc'
336
     */
337 23
    public function isMvc(): bool
338
    {
339 23
        return $this->getMode() === self::MODE_MVC;
340
    }
341
    
342
    /**
343
     * Alias for the ->isCli() method
344
     * @deprecated
345
     */
346 2
    public function isConsole(): bool
347
    {
348 2
        return $this->isCli();
349
    }
350
    
351
    /**
352
     * Alias for the ->isMvc() method
353
     * @deprecated
354
     */
355 2
    public function isDefault(): bool
356
    {
357 2
        return $this->isMvc();
358
    }
359
}
360