Completed
Push — develop ( 62c23a...4f6165 )
by Neomerx
05:22
created

Application::createInstanceSettingsProvider()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 8
cts 8
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php namespace Limoncello\Application\Packages\Application;
2
3
/**
4
 * Copyright 2015-2018 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Limoncello\Application\CoreSettings\CoreData;
20
use Limoncello\Application\Settings\CacheSettingsProvider;
21
use Limoncello\Application\Settings\FileSettingsProvider;
22
use Limoncello\Application\Settings\InstanceSettingsProvider;
23
use Limoncello\Container\Container;
24
use Limoncello\Contracts\Application\ApplicationConfigurationInterface as A;
25
use Limoncello\Contracts\Application\CacheSettingsProviderInterface;
26
use Limoncello\Contracts\Container\ContainerInterface as LimoncelloContainerInterface;
27
use Limoncello\Contracts\Core\SapiInterface;
28
use Limoncello\Contracts\Provider\ProvidesSettingsInterface;
29
use Limoncello\Contracts\Routing\RouterInterface;
30
use Limoncello\Contracts\Settings\SettingsProviderInterface;
31
use Limoncello\Core\Application\Sapi;
32
use Limoncello\Core\Reflection\ClassIsTrait;
33
use ReflectionException;
34
use Zend\HttpHandlerRunner\Emitter\SapiEmitter;
35
36
/**
37
 * @package Limoncello\Application
38
 *
39
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
40
 */
41
class Application extends \Limoncello\Core\Application\Application
42
{
43
    use ClassIsTrait;
44
45
    /**
46
     * @var string
47
     */
48
    private $settingsPath;
49
50
    /**
51
     * @var callable|string|null
52
     */
53
    private $settingCacheMethod;
54
55
    /**
56
     * @var CacheSettingsProviderInterface|null
57
     */
58
    private $cacheSettingsProvider = null;
59
60
    /**
61
     * @param string                     $settingsPath
62
     * @param string|array|callable|null $settingCacheMethod
63
     * @param SapiInterface|null         $sapi
64
     */
65 2
    public function __construct(string $settingsPath, $settingCacheMethod = null, SapiInterface $sapi = null)
66
    {
67
        // The reason we do not use `callable` for the input parameter is that at the moment
68
        // of calling the callable might not exist. Therefore when created it will pass
69
        // `is_callable` check and will be used for getting the cached data.
70 2
        assert(is_null($settingCacheMethod) || is_string($settingCacheMethod) || is_array($settingCacheMethod));
71
72 2
        $this->settingsPath       = $settingsPath;
73 2
        $this->settingCacheMethod = $settingCacheMethod;
74
75 2
        $this->setSapi($sapi ?? new Sapi(new SapiEmitter()));
76
    }
77
78
    /**
79
     * @inheritdoc
80
     *
81
     * @throws ReflectionException
82
     */
83 2
    protected function getCoreData(): array
84
    {
85 2
        return $this->getCacheSettingsProvider()->getCoreData();
86
    }
87
88
    /**
89
     * Get container from application. If `method` and `path` are specified the container will be configured
90
     * not only with global container configurators but with route's one as well.
91
     *
92
     * @param string|null $method
93
     * @param string|null $path
94
     *
95
     * @return LimoncelloContainerInterface
96
     *
97
     * @throws ReflectionException
98
     *
99
     * @SuppressWarnings(PHPMD.StaticAccess)
100
     */
101 2
    public function createContainer(string $method = null, string $path = null): LimoncelloContainerInterface
102
    {
103 2
        $container = $this->createContainerInstance();
104
105 2
        $routeConfigurators = [];
106 2
        $coreData           = $this->getCoreData();
107 2
        if (empty($method) === false && empty($path) === false) {
108 2
            list(, , , , , $routeConfigurators) = $this->initRouter($coreData)->match($method, $path);
109
        }
110
111
        // configure container
112 2
        assert(!empty($coreData));
113 2
        $globalConfigurators = CoreData::getGlobalConfiguratorsFromData($coreData);
114 2
        $this->configureContainer($container, $globalConfigurators, $routeConfigurators);
115
116 2
        return $container;
117
    }
118
119
    /**
120
     * @return LimoncelloContainerInterface
121
     *
122
     * @throws ReflectionException
123
     */
124 2
    protected function createContainerInstance(): LimoncelloContainerInterface
125
    {
126 2
        $container = new Container();
127
128 2
        $settingsProvider = $this->getCacheSettingsProvider();
129 2
        $container->offsetSet(SettingsProviderInterface::class, $settingsProvider);
130 2
        $container->offsetSet(CacheSettingsProviderInterface::class, $settingsProvider);
131 2
        $container->offsetSet(RouterInterface::class, $this->getRouter());
132
133 2
        return $container;
134
    }
135
136
    /**
137
     * @return string
138
     */
139 1
    protected function getSettingsPath(): string
140
    {
141 1
        return $this->settingsPath;
142
    }
143
144
    /**
145
     * @return callable|string|null
146
     */
147 2
    protected function getSettingCacheMethod()
148
    {
149 2
        return $this->settingCacheMethod;
150
    }
151
152
    /**
153
     * @return CacheSettingsProviderInterface
154
     *
155
     * @SuppressWarnings(PHPMD.ElseExpression)
156
     *
157
     * @throws ReflectionException
158
     */
159 2
    private function getCacheSettingsProvider(): CacheSettingsProviderInterface
160
    {
161 2
        if ($this->cacheSettingsProvider === null) {
162 2
            $provider = new CacheSettingsProvider();
163
164 2
            if (is_callable($method = $this->getSettingCacheMethod()) === true) {
165 1
                $data = call_user_func($method);
166 1
                $provider->unserialize($data);
167
            } else {
168 1
                $appConfig = $this->createApplicationConfiguration();
169 1
                $appData   = $appConfig->get();
170 1
                $coreData  = new CoreData(
171 1
                    $appData[A::KEY_ROUTES_PATH],
172 1
                    $appData[A::KEY_CONTAINER_CONFIGURATORS_PATH],
173 1
                    $appData[A::KEY_PROVIDER_CLASSES]
174
                );
175
176 1
                $provider->setInstanceSettings($appConfig, $coreData, $this->createInstanceSettingsProvider($appData));
177
            }
178
179 2
            $this->cacheSettingsProvider = $provider;
180
        }
181
182 2
        return $this->cacheSettingsProvider;
183
    }
184
185
    /**
186
     * @return A
187
     *
188
     * @throws ReflectionException
189
     */
190 1
    private function createApplicationConfiguration(): A
191
    {
192 1
        $classes = iterator_to_array(static::selectClasses($this->getSettingsPath(), A::class));
193 1
        assert(
194 1
            count($classes) > 0,
195 1
            'Settings path must contain a class implementing ApplicationConfigurationInterface.'
196
        );
197 1
        assert(
198 1
            count($classes) === 1,
199 1
            'Settings path must contain only one class implementing ApplicationConfigurationInterface.'
200
        );
201 1
        $class    = reset($classes);
202 1
        $instance = new $class();
203 1
        assert($instance instanceof A);
204
205 1
        return $instance;
206
    }
207
208
    /**
209
     * @param array $applicationData
210
     *
211
     * @return InstanceSettingsProvider
212
     *
213
     * @throws ReflectionException
214
     */
215 1
    private function createInstanceSettingsProvider(array $applicationData): InstanceSettingsProvider
216
    {
217
        // Load all settings from path specified
218 1
        $provider = (new FileSettingsProvider($applicationData))->load($this->getSettingsPath());
219
220
        // Application settings have a list of providers which might have additional settings to load
221 1
        $providerClasses = $applicationData[A::KEY_PROVIDER_CLASSES];
222 1
        $selectedClasses = $this->selectClassImplements($providerClasses, ProvidesSettingsInterface::class);
223 1
        foreach ($selectedClasses as $providerClass) {
224
            /** @var ProvidesSettingsInterface $providerClass */
225 1
            foreach ($providerClass::getSettings() as $setting) {
226 1
                $provider->register($setting);
227
            }
228
        }
229
230 1
        return $provider;
231
    }
232
}
233