Passed
Push — master ( 91844f...3ae4be )
by Julien
07:39
created

Bootstrap   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Test Coverage

Coverage 67.27%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 128
c 1
b 0
f 0
dl 0
loc 325
ccs 74
cts 110
cp 0.6727
rs 8.8798
wmc 44

23 Methods

Rating   Name   Duplication   Size   Complexity  
A setDI() 0 9 2
A initialize() 0 2 1
A __construct() 0 11 1
A registerConfig() 0 7 2
A getDI() 0 3 1
A handleApplication() 0 4 3
A getArgs() 0 16 5
A setConfig() 0 3 1
A registerModules() 0 12 2
A getMode() 0 3 1
A setMode() 0 6 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 getConfig() 0 3 1
A handleConsole() 0 19 5
A run() 0 21 3
A getRouter() 0 3 1
A isCli() 0 3 1
A setRouter() 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 112
    public function __construct(string $mode = null)
89
    {
90 112
        $this->setMode($mode);
91 112
        $this->setEventsManager(new Events\Manager());
92 112
        $this->setDI();
93 112
        $this->initialize();
94 112
        $this->registerConfig();
95 112
        $this->registerServices();
96 112
        $this->bootServices();
97 112
        $this->registerModules();
98 112
        $this->registerRouter();
99
    }
100
    
101
    /**
102
     * Initialisation
103
     */
104 112
    public function initialize(): void
105
    {
106 112
    }
107
    
108
    /**
109
     * Set the default DI
110
     */
111 112
    public function setDI(?DiInterface $di = null): void
112
    {
113 112
        $di ??= $this->isCli()
114 17
            ? new FactoryDefault\Cli()
115 95
            : new FactoryDefault();
116
        
117 112
        $this->di = $di;
118 112
        $this->di->setShared('bootstrap', $this);
119 112
        Di::setDefault($this->di);
120
    }
121
    
122 112
    public function setMode(?string $mode = null): void
123
    {
124 112
        $this->mode = $mode ?? (
125 112
            Php::isCli()
126
            ? self::MODE_CLI
127
            : self::MODE_MVC
128
        );
129
    }
130
    
131 114
    public function getMode(): string
132
    {
133 114
        return $this->mode;
134
    }
135
    
136
    /**
137
     * Get the default DI
138
     */
139 2
    public function getDI(): DiInterface
140
    {
141 2
        return $this->di;
142
    }
143
    
144
    /**
145
     * Set the Config
146
     */
147 1
    public function setConfig(ConfigInterface $config): void
148
    {
149 1
        $this->config = $config;
150
    }
151
    
152
    /**
153
     * Get the Config
154
     */
155 2
    public function getConfig(): ConfigInterface
156
    {
157 2
        return $this->config;
158
    }
159
    
160
    /**
161
     * Set the MVC or CLI Router
162
     */
163
    public function setRouter(RouterInterface $router): void
164
    {
165
        $this->router = $router;
166
    }
167
    
168
    /**
169
     * Get the MVC or CLI Router
170
     */
171
    public function getRouter(): RouterInterface
172
    {
173
        return $this->router;
174
    }
175
    
176
    /**
177
     * Register Config
178
     */
179 112
    public function registerConfig(): void
180
    {
181 112
        if (!$this->di->has('config')) {
182 112
            $configService = new ConfigServiceProvider($this->di);
183 112
            $configService->register($this->di);
184
        }
185 112
        $this->config ??= $this->di->get('config');
186
    }
187
    
188
    /**
189
     * Register Service Providers
190
     * @throws Exception
191
     */
192 112
    public function registerServices(?array $providers = null): void
193
    {
194 112
        $providers ??= $this->config->pathToArray('providers') ?? [];
195 112
        foreach ($providers as $key => $provider) {
196 112
            if (!is_string($provider)) {
197
                throw new Exception('Service Provider `' . $key . '` class name must be a string.', 400);
198
            }
199 112
            if (!class_exists($provider)) {
200
                throw new Exception('Service Provider `' . $key . '` class  `' . $provider . '` not found.', 404);
201
            }
202 112
            if ($this->di instanceof Di) {
203 112
                $this->di->register(new $provider($this->di));
204
            }
205
        }
206
    }
207
    
208
    /**
209
     * Register Router
210
     */
211 112
    public function registerRouter(): void
212
    {
213 112
        if (!$this->di->has('router')) {
214
            $configService = new RouterServiceProvider($this->di);
215
            $configService->register($this->di);
216
        }
217 112
        $this->router ??= $this->di->get('router');
218
    }
219
    
220
    /**
221
     * Boot Service Providers
222
     */
223 112
    public function bootServices(): void
224
    {
225 112
        $this->di->get('debug');
226
    }
227
    
228
    /**
229
     * Register modules
230
     */
231 112
    public function registerModules(AbstractApplication $application = null, ?array $modules = null, ?string $defaultModule = null): void
232
    {
233 112
        $application ??= $this->isMvc()
234 95
            ? $this->di->get('application')
235 17
            : $this->di->get('console');
236 112
        assert($application instanceof AbstractApplication);
237
        
238 112
        $modules ??= $this->config->pathToArray('modules') ?? [];
239 112
        $application->registerModules($modules);
240
        
241 112
        $defaultModule ??= $this->config->path('router.defaults.module');
242 112
        $application->setDefaultModule($defaultModule);
243
    }
244
    
245
    /**
246
     * Handle cli or mvc application
247
     * @throws \Exception
248
     */
249
    public function run(): ?string
250
    {
251
        $this->fire('beforeRun');
252
        
253
        if ($this->isMvc()) {
254
            
255
            $application = $this->di->get('application');
256
            $content = $this->handleApplication($application);
257
        }
258
        elseif ($this->isCli()) {
259
            
260
            $console = $this->di->get('console');
261
            $content = $this->handleConsole($console);
262
        }
263
        else {
264
            throw new \Exception('Application or Console not found', 404);
265
        }
266
        
267
        $this->fire('afterRun', $content);
268
        
269
        return $content;
270
    }
271
    
272
    /**
273
     * Handle Console (For CLI only)
274
     */
275
    public function handleConsole(Console $console): ?string
276
    {
277
        $response = null;
278
        try {
279
            ob_start();
280
            $console->handle($this->getArgs());
281
            $response = ob_get_clean() ?: null;
282
        }
283
        catch (\Zemit\Exception $e) {
284
            new Cli\ExceptionHandler($e);
285
        }
286
        catch (\Exception $exception) {
287
            new Cli\ExceptionHandler($exception);
288
        }
289
        catch (\Throwable $throwable) {
290
            new Cli\ExceptionHandler($throwable);
291
        }
292
        
293
        return $response;
294
    }
295
    
296
    /**
297
     * Handle Application
298
     * @throws \Exception
299
     */
300
    public function handleApplication(Application $application): string
301
    {
302
        $this->response = $application->handle($_SERVER['REQUEST_URI'] ?? '/') ?: null;
303
        return $this->response ? $this->response->getContent() : '';
304
    }
305
    
306
    /**
307
     * Get & format args from the $this->args property
308
     */
309 1
    public function getArgs(): array
310
    {
311 1
        $args = [];
312
        
313 1
        if ($this->isCli()) {
314 1
            $argv = array_slice($_SERVER['argv'] ?? [], 1);
315 1
            $response = (new Docopt())->handle($this->cliDoc, ['argv' => $argv, 'optionsFirst' => true]);
316 1
            foreach ($response as $key => $value) {
317 1
                if (!is_null($value) && preg_match('/(<(.*?)>|\-\-(.*))/', $key, $match)) {
318 1
                    $key = lcfirst(Helper::camelize(Helper::uncamelize(array_pop($match))));
319 1
                    $args[$key] = $value;
320
                }
321
            }
322
        }
323
        
324 1
        return $args;
325
    }
326
    
327
    /**
328
     * Return true if the bootstrap mode is set to 'cli'
329
     */
330 114
    public function isCli(): bool
331
    {
332 114
        return $this->getMode() === self::MODE_CLI;
333
    }
334
    
335
    /**
336
     * Return true if the bootstrap mode is set to 'mvc'
337
     */
338 112
    public function isMvc(): bool
339
    {
340 112
        return $this->getMode() === self::MODE_MVC;
341
    }
342
    
343
    /**
344
     * Alias for the ->isCli() method
345
     * @deprecated
346
     */
347 2
    public function isConsole(): bool
348
    {
349 2
        return $this->isCli();
350
    }
351
    
352
    /**
353
     * Alias for the ->isMvc() method
354
     * @deprecated
355
     */
356 2
    public function isDefault(): bool
357
    {
358 2
        return $this->isMvc();
359
    }
360
}
361