Application   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 17
dl 0
loc 194
ccs 63
cts 63
cp 1
rs 10
c 0
b 0
f 0
lcom 1
cbo 11

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 3
A getCoreData() 0 4 1
A createContainer() 0 17 3
A createContainerInstance() 0 11 1
A getSettingsPath() 0 4 1
A getSettingCacheMethod() 0 4 1
A getCacheSettingsProvider() 0 25 3
A createApplicationConfiguration() 0 18 1
A createInstanceSettingsProvider() 0 17 3
1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Application\Packages\Application;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Limoncello\Application\CoreSettings\CoreData;
22
use Limoncello\Application\Settings\CacheSettingsProvider;
23
use Limoncello\Application\Settings\FileSettingsProvider;
24
use Limoncello\Application\Settings\InstanceSettingsProvider;
25
use Limoncello\Common\Reflection\ClassIsTrait;
26
use Limoncello\Container\Container;
27
use Limoncello\Contracts\Application\ApplicationConfigurationInterface as A;
28
use Limoncello\Contracts\Application\CacheSettingsProviderInterface;
29
use Limoncello\Contracts\Container\ContainerInterface as LimoncelloContainerInterface;
30
use Limoncello\Contracts\Core\SapiInterface;
31
use Limoncello\Contracts\Provider\ProvidesSettingsInterface;
32
use Limoncello\Contracts\Routing\RouterInterface;
33
use Limoncello\Contracts\Settings\SettingsProviderInterface;
34
use Limoncello\Core\Application\Sapi;
35
use ReflectionException;
36
use Zend\HttpHandlerRunner\Emitter\SapiEmitter;
37
use function assert;
38
use function call_user_func;
39
use function count;
40
use function is_array;
41
use function is_callable;
42
use function is_null;
43
use function is_string;
44
use function iterator_to_array;
45
use function reset;
46
47
/**
48
 * @package Limoncello\Application
49
 *
50
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
51
 * @SuppressWarnings(PHPMD.LongVariable)
52
 */
53
class Application extends \Limoncello\Core\Application\Application
54
{
55
    use ClassIsTrait;
56
57
    /**
58
     * @var string
59
     */
60
    private $settingsPath;
61
62
    /**
63
     * @var callable|string|null
64
     */
65 2
    private $settingCacheMethod;
66
67
    /**
68
     * @var CacheSettingsProviderInterface|null
69
     */
70 2
    private $cacheSettingsProvider = null;
71
72 2
    /**
73 2
     * @param string                     $settingsPath
74
     * @param string|array|callable|null $settingCacheMethod
75 2
     * @param SapiInterface|null         $sapi
76
     */
77
    public function __construct(string $settingsPath, $settingCacheMethod = null, SapiInterface $sapi = null)
78
    {
79
        // The reason we do not use `callable` for the input parameter is that at the moment
80
        // of calling the callable might not exist. Therefore when created it will pass
81
        // `is_callable` check and will be used for getting the cached data.
82
        assert(is_null($settingCacheMethod) || is_string($settingCacheMethod) || is_array($settingCacheMethod));
83 2
84
        $this->settingsPath       = $settingsPath;
85 2
        $this->settingCacheMethod = $settingCacheMethod;
86
87
        $this->setSapi($sapi ?? new Sapi(new SapiEmitter()));
88
    }
89
90
    /**
91
     * @inheritdoc
92
     *
93
     * @throws ReflectionException
94
     */
95
    protected function getCoreData(): array
96
    {
97
        return $this->getCacheSettingsProvider()->getCoreData();
98
    }
99
100
    /**
101 2
     * Get container from application. If `method` and `path` are specified the container will be configured
102
     * not only with global container configurators but with route's one as well.
103 2
     *
104
     * @param string|null $method
105 2
     * @param string|null $path
106 2
     *
107 2
     * @return LimoncelloContainerInterface
108 2
     *
109
     * @throws ReflectionException
110
     *
111
     * @SuppressWarnings(PHPMD.StaticAccess)
112 2
     */
113 2
    public function createContainer(string $method = null, string $path = null): LimoncelloContainerInterface
114 2
    {
115
        $container = $this->createContainerInstance();
116 2
117
        $routeConfigurators = [];
118
        $coreData           = $this->getCoreData();
119
        if (empty($method) === false && empty($path) === false) {
120
            list(, , , , , $routeConfigurators) = $this->initRouter($coreData)->match($method, $path);
121
        }
122
123
        // configure container
124 2
        assert(!empty($coreData));
125
        $globalConfigurators = CoreData::getGlobalConfiguratorsFromData($coreData);
126 2
        $this->configureContainer($container, $globalConfigurators, $routeConfigurators);
127
128 2
        return $container;
129 2
    }
130 2
131 2
    /**
132
     * @return LimoncelloContainerInterface
133 2
     *
134
     * @throws ReflectionException
135
     */
136
    protected function createContainerInstance(): LimoncelloContainerInterface
137
    {
138
        $container = new Container();
139 1
140
        $settingsProvider = $this->getCacheSettingsProvider();
141 1
        $container->offsetSet(SettingsProviderInterface::class, $settingsProvider);
142
        $container->offsetSet(CacheSettingsProviderInterface::class, $settingsProvider);
143
        $container->offsetSet(RouterInterface::class, $this->getRouter());
144
145
        return $container;
146
    }
147 2
148
    /**
149 2
     * @return string
150
     */
151
    protected function getSettingsPath(): string
152
    {
153
        return $this->settingsPath;
154
    }
155
156
    /**
157
     * @return callable|string|null
158
     */
159 2
    protected function getSettingCacheMethod()
160
    {
161 2
        return $this->settingCacheMethod;
162 2
    }
163
164 2
    /**
165 1
     * @return CacheSettingsProviderInterface
166 1
     *
167
     * @throws ReflectionException
168 1
     *
169 1
     * @SuppressWarnings(PHPMD.ElseExpression)
170 1
     * @SuppressWarnings(PHPMD.IfStatementAssignment)
171 1
     */
172 1
    private function getCacheSettingsProvider(): CacheSettingsProviderInterface
173 1
    {
174
        if ($this->cacheSettingsProvider === null) {
175
            $provider = new CacheSettingsProvider();
176 1
177
            if (is_callable($method = $this->getSettingCacheMethod()) === true) {
178
                $data = call_user_func($method);
179 2
                $provider->unserialize($data);
0 ignored issues
show
Unused Code introduced by
The call to the method Limoncello\Application\S...Provider::unserialize() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
180
            } else {
181
                $appConfig = $this->createApplicationConfiguration();
182 2
                $appData   = $appConfig->get();
183
                $coreData  = new CoreData(
184
                    $appData[A::KEY_ROUTES_PATH],
185
                    $appData[A::KEY_CONTAINER_CONFIGURATORS_PATH],
186
                    $appData[A::KEY_PROVIDER_CLASSES]
187
                );
188
189
                $provider->setInstanceSettings($appConfig, $coreData, $this->createInstanceSettingsProvider($appData));
190 1
            }
191
192
            $this->cacheSettingsProvider = $provider;
193 1
        }
194 1
195 1
        return $this->cacheSettingsProvider;
196 1
    }
197
198 1
    /**
199 1
     * @return A
200 1
     *
201
     * @throws ReflectionException
202 1
     */
203 1
    private function createApplicationConfiguration(): A
204 1
    {
205
        /** @noinspection PhpParamsInspection */
206 1
        $classes = iterator_to_array(static::selectClasses($this->getSettingsPath(), A::class));
207
        assert(
208
            count($classes) > 0,
209
            'Settings path must contain a class implementing ApplicationConfigurationInterface.'
210
        );
211
        assert(
212
            count($classes) === 1,
213
            'Settings path must contain only one class implementing ApplicationConfigurationInterface.'
214
        );
215
        $class    = reset($classes);
216 1
        $instance = new $class();
217
        assert($instance instanceof A);
218
219 1
        return $instance;
220
    }
221
222 1
    /**
223 1
     * @param array $applicationData
224 1
     *
225
     * @return InstanceSettingsProvider
226 1
     *
227 1
     * @throws ReflectionException
228
     */
229
    private function createInstanceSettingsProvider(array $applicationData): InstanceSettingsProvider
230
    {
231 1
        // Load all settings from path specified
232
        $provider = (new FileSettingsProvider($applicationData))->load($this->getSettingsPath());
233
234
        // Application settings have a list of providers which might have additional settings to load
235
        $providerClasses = $applicationData[A::KEY_PROVIDER_CLASSES];
236
        $selectedClasses = $this->selectClassImplements($providerClasses, ProvidesSettingsInterface::class);
237
        foreach ($selectedClasses as $providerClass) {
238
            /** @var ProvidesSettingsInterface $providerClass */
239
            foreach ($providerClass::getSettings() as $setting) {
240
                $provider->register($setting);
241
            }
242
        }
243
244
        return $provider;
245
    }
246
}
247