Passed
Push — master ( ac3ce9...4824e6 )
by Alexey
04:00
created

AbstractKernel   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 186
c 0
b 0
f 0
ccs 0
cts 82
cp 0
rs 10
wmc 15
lcom 1
cbo 6

13 Methods

Rating   Name   Duplication   Size   Complexity  
B boot() 0 29 2
A environment() 0 4 2
A isCli() 0 4 1
rootPath() 0 1 ?
A version() 0 4 1
A getBootstraps() 0 16 1
A invokeBootstrap() 0 6 1
registerServiceProviders() 0 1 ?
A addDefaultInflections() 0 6 1
A bindDefaultServices() 0 5 1
A ensureBootstrap() 0 8 2
A ensureServiceProvider() 0 8 2
A initServiceContainer() 0 10 1
1
<?php declare(strict_types = 1);
2
3
namespace Venta\Framework\Kernel;
4
5
use InvalidArgumentException;
6
use Psr\Log\LoggerAwareInterface;
7
use Venta\Config\ConfigProxy;
8
use Venta\Config\MutableConfig;
9
use Venta\Container\ContainerProxy;
10
use Venta\Contracts\Config\Config;
11
use Venta\Contracts\Container\Container;
12
use Venta\Contracts\Container\ContainerAware;
13
use Venta\Contracts\Container\MutableContainer;
14
use Venta\Contracts\Http\ResponseFactoryAware;
15
use Venta\Contracts\Kernel\Kernel;
16
use Venta\Contracts\ServiceProvider\ServiceProvider;
17
use Venta\Framework\Kernel\Bootstrap\ConfigurationLoading;
18
use Venta\Framework\Kernel\Bootstrap\EnvironmentDetection;
19
use Venta\Framework\Kernel\Bootstrap\ErrorHandling;
20
use Venta\Framework\Kernel\Bootstrap\Logging;
21
use Venta\ServiceProvider\AbstractServiceProvider;
22
23
/**
24
 * Class AbstractKernel
25
 *
26
 * @package Venta\Framework\Kernel
27
 */
28
abstract class AbstractKernel implements Kernel
29
{
30
    const VERSION = '0.1.0';
31
32
    /**
33
     * Service container class name.
34
     *
35
     * @var string
36
     */
37
    protected $containerClass = \Venta\Container\MutableContainer::class;
38
39
    /**
40
     * @inheritDoc
41
     */
42
    public function boot(): Container
43
    {
44
        $container = $this->initServiceContainer();
45
46
        foreach ($this->getBootstraps() as $bootstrapClass) {
47
            $this->invokeBootstrap($bootstrapClass, $container);
48
        }
49
50
        $appConfig = $container->get(Config::class)->all();
51
        $config = new MutableConfig($appConfig);
52
        $container->bind(Config::class, new ConfigProxy($config));
53
54
        array_map(function(AbstractServiceProvider $provider) use ($config, $appConfig) {
55
            $provider->boot();
56
            $config->merge($appConfig);
57
58
            return $provider;
59
        }, array_map(function ($providerClass) use ($config, $appConfig, $container) {
60
            $this->ensureServiceProvider($providerClass);
61
            /** @var AbstractServiceProvider|ServiceProvider $provider */
62
            $provider = new $providerClass($container->get(Container::class), $config);
63
            $provider->bind($container);
64
            $config->merge($appConfig);
65
66
            return $provider;
67
        }, $this->registerServiceProviders()));
68
69
        return $container->get(Container::class);
70
    }
71
72
    /**
73
     * @inheritDoc
74
     */
75
    public function environment(): string
76
    {
77
        return getenv('APP_ENV') ?: 'local';
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83
    public function isCli(): bool
84
    {
85
        return php_sapi_name() === 'cli';
86
    }
87
88
    /**
89
     * @return string
90
     */
91
    abstract public function rootPath(): string;
92
93
    /**
94
     * @inheritDoc
95
     */
96
    public function version(): string
97
    {
98
        return self::VERSION;
99
    }
100
101
    /**
102
     * Returns list of kernel bootstraps.
103
     * This is a main place to tune default kernel behavior.
104
     * Change carefully, as it may cause kernel failure.
105
     *
106
     * @return string[]
107
     */
108
    protected function getBootstraps(): array
109
    {
110
        $modules = [
111
            EnvironmentDetection::class,
112
            ConfigurationLoading::class,
113
            Logging::class,
114
            ErrorHandling::class,
115
        ];
116
117
        // Here we can add environment dependant modules.
118
        //if ($this->getEnvironment() === \Venta\Contracts\Kernel\Kernel::ENV_LOCAL) {
119
        //    $modules[] = 'KernelModule';
120
        //}
121
122
        return $modules;
123
    }
124
125
    /**
126
     * Invokes kernel bootstrap.
127
     * This is the point where specific kernel functionality defined by bootstrap is enabled.
128
     *
129
     * @param string $bootstrapClass
130
     * @param MutableContainer $container
131
     * @throws InvalidArgumentException
132
     */
133
    protected function invokeBootstrap(string $bootstrapClass, MutableContainer $container)
134
    {
135
        $this->ensureBootstrap($bootstrapClass);
136
137
        (new $bootstrapClass($container, $this))();
138
    }
139
140
    /**
141
     * Returns a list of all registered service providers.
142
     *
143
     * @return string[]
144
     */
145
    abstract protected function registerServiceProviders(): array;
146
147
    /**
148
     * Adds default service inflections.
149
     *
150
     * @param MutableContainer $container
151
     */
152
    private function addDefaultInflections(MutableContainer $container)
153
    {
154
        $container->inflect(ContainerAware::class, 'setContainer');
155
        $container->inflect(LoggerAwareInterface::class, 'setLogger');
156
        $container->inflect(ResponseFactoryAware::class, 'setResponseFactory');
157
    }
158
159
    /**
160
     * Binds default services to container.
161
     *
162
     * @param MutableContainer $container
163
     */
164
    private function bindDefaultServices(MutableContainer $container)
165
    {
166
        $container->bind(Container::class, new ContainerProxy($container));
167
        $container->bind(Kernel::class, $this);
168
    }
169
170
    /**
171
     * Ensures bootstrap class extends abstract kernel bootstrap.
172
     *
173
     * @param string $bootstrapClass
174
     * @throws InvalidArgumentException
175
     */
176
    private function ensureBootstrap(string $bootstrapClass)
177
    {
178
        if (!is_subclass_of($bootstrapClass, AbstractKernelBootstrap::class)) {
179
            throw new InvalidArgumentException(
180
                sprintf('Class "%s" must be a subclass of "%s".', $bootstrapClass, AbstractKernelBootstrap::class)
181
            );
182
        }
183
    }
184
185
    /**
186
     * Ensures service provider implements contract.
187
     *
188
     * @param string $providerClass
189
     * @throws InvalidArgumentException
190
     */
191
    private function ensureServiceProvider(string $providerClass)
192
    {
193
        if (!is_subclass_of($providerClass, AbstractServiceProvider::class)) {
194
            throw new InvalidArgumentException(
195
                sprintf('Class "%s" must be a subclass of "%s".', $providerClass, AbstractServiceProvider::class)
196
            );
197
        }
198
    }
199
200
    /**
201
     * Initializes service container.
202
     */
203
    private function initServiceContainer(): MutableContainer
204
    {
205
        /** @var MutableContainer $container */
206
        $container = new $this->containerClass;
207
208
        $this->bindDefaultServices($container);
209
        $this->addDefaultInflections($container);
210
211
        return $container;
212
    }
213
}