Test Failed
Push — master ( cba865...0d5b8f )
by Konstantins
03:10
created

AbstractKernel::createConfigurationBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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