Completed
Push — master ( 146147...1776d4 )
by Neomerx
01:58
created

InstanceSettingsProvider   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 4
dl 0
loc 237
ccs 83
cts 83
cp 1
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A checkInstancesAreProcessed() 0 4 2
A __construct() 0 4 1
A has() 0 8 1
A get() 0 14 3
A register() 0 12 2
A getSettingsMap() 0 6 1
A getSettingsData() 0 6 1
A getAmbiguousMap() 0 6 1
A isAmbiguous() 0 6 1
A getApplicationData() 0 4 1
C processInstances() 0 49 8
A selectChildSettings() 0 16 4
A selectChildSettingsAmongTwo() 0 7 3
1
<?php namespace Limoncello\Application\Settings;
2
3
/**
4
 * Copyright 2015-2017 [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\Exceptions\AlreadyRegisteredSettingsException;
20
use Limoncello\Application\Exceptions\AmbiguousSettingsException;
21
use Limoncello\Application\Exceptions\NotRegisteredSettingsException;
22
use Limoncello\Contracts\Settings\SettingsInterface;
23
use Limoncello\Contracts\Settings\SettingsProviderInterface;
24
25
/**
26
 * @package Limoncello\Application
27
 */
28
class InstanceSettingsProvider implements SettingsProviderInterface
29
{
30
    /**
31
     * @var array
32
     */
33
    private $applicationData;
34
35
    /**
36
     * @var SettingsInterface[]
37
     */
38
    private $instances = [];
39
40
    /**
41
     * @var bool
42
     */
43
    private $isProcessed = true;
44
45
    /**
46
     * @var array
47
     */
48
    private $settingsMap = [];
49
50
    /**
51
     * @var array
52
     */
53
    private $settingsData = [];
54
55
    /**
56
     * @var array
57
     */
58
    private $ambiguousMap = [];
59
60
    /**
61
     * @param array $applicationData
62
     */
63 13
    public function __construct(array $applicationData)
64
    {
65 13
        $this->applicationData = $applicationData;
66
    }
67
68
    /**
69
     * @inheritdoc
70
     */
71 3
    public function has(string $className): bool
72
    {
73 3
        $this->checkInstancesAreProcessed();
74
75 3
        $result = array_key_exists($className, $this->getSettingsMap());
76
77 3
        return $result;
78
    }
79
80
    /**
81
     * @param string $className
82
     *
83
     * @return array
84
     */
85 3
    public function get(string $className): array
86
    {
87 3
        if ($this->has($className) === false) {
88 2
            if (array_key_exists($className, $this->ambiguousMap) === true) {
89 1
                throw new AmbiguousSettingsException($className);
90
            }
91 1
            throw new NotRegisteredSettingsException($className);
92
        }
93
94 1
        $index = $this->settingsMap[$className];
95 1
        $data  = $this->settingsData[$index];
96
97 1
        return $data;
98
    }
99
100
    /**
101
     * @param SettingsInterface $settings
102
     *
103
     * @return InstanceSettingsProvider
104
     */
105 9
    public function register(SettingsInterface $settings): InstanceSettingsProvider
106
    {
107 9
        $className = get_class($settings);
108 9
        if (array_key_exists($className, $this->instances) === true) {
109 1
            throw new AlreadyRegisteredSettingsException($className);
110
        }
111
112 9
        $this->instances[$className] = $settings;
113 9
        $this->isProcessed           = false;
114
115 9
        return $this;
116
    }
117
118
    /**
119
     * @return array
120
     */
121 9
    public function getSettingsMap(): array
122
    {
123 9
        $this->checkInstancesAreProcessed();
124
125 9
        return $this->settingsMap;
126
    }
127
128
    /**
129
     * @return array
130
     */
131 7
    public function getSettingsData(): array
132
    {
133 7
        $this->checkInstancesAreProcessed();
134
135 7
        return $this->settingsData;
136
    }
137
138
    /**
139
     * @return array
140
     */
141 7
    public function getAmbiguousMap(): array
142
    {
143 7
        $this->checkInstancesAreProcessed();
144
145 7
        return $this->ambiguousMap;
146
    }
147
148
    /**
149
     * @inheritdoc
150
     */
151 1
    public function isAmbiguous(string $className): bool
152
    {
153 1
        $result = array_key_exists($className, $this->getAmbiguousMap());
154
155 1
        return $result;
156
    }
157
158
    /**
159
     * @return array
160
     */
161 8
    protected function getApplicationData(): array
162
    {
163 8
        return $this->applicationData;
164
    }
165
166
    /**
167
     * @return void
168
     */
169 9
    private function checkInstancesAreProcessed(): void
170
    {
171 9
        $this->isProcessed === true ?: $this->processInstances();
172
    }
173
174
    /**
175
     * @return void
176
     *
177
     * @SuppressWarnings(PHPMD.ElseExpression)
178
     */
179 8
    private function processInstances(): void
180
    {
181 8
        $preliminaryMap = [];
182 8
        foreach ($this->instances as $instance) {
183 8
            $preliminaryMap[get_class($instance)][] = $instance;
184 8
            foreach (class_parents($instance) as $parentClass) {
185 7
                $preliminaryMap[$parentClass][] = $instance;
186
            }
187 8
            foreach (class_implements($instance) as $parentClass) {
188 8
                $preliminaryMap[$parentClass][] = $instance;
189
            }
190
        }
191
192 8
        $nextIndex    = 0;
193 8
        $hashMap      = []; // hash  => index
194 8
        $settingsData = []; // index => instance data
195 8
        $getIndex     = function (SettingsInterface $instance) use (&$nextIndex, &$hashMap, &$settingsData): int {
196 8
            $hash = spl_object_hash($instance);
197 8
            if (array_key_exists($hash, $hashMap) === true) {
198 8
                $index = $hashMap[$hash];
199
            } else {
200 8
                $hashMap[$hash]           = $nextIndex;
201 8
                $settingsData[$nextIndex] = $instance->get($this->getApplicationData());
0 ignored issues
show
Unused Code introduced by
The call to SettingsInterface::get() has too many arguments starting with $this->getApplicationData().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
202 8
                $index                    = $nextIndex++;
203
            }
204
205 8
            return $index;
206 8
        };
207
208 8
        $settingsMap  = []; // class => index
209 8
        $ambiguousMap = []; // class => true
210 8
        foreach ($preliminaryMap as $class => $instanceList) {
211 8
            if (count($instanceList) === 1) {
212 8
                $selected = $instanceList[0];
213
            } else {
214 7
                $selected = $this->selectChildSettings($instanceList);
215
            }
216 8
            if ($selected !== null) {
217 8
                $settingsMap[$class] = $getIndex($selected);
218
            } else {
219 8
                $ambiguousMap[$class] = true;
220
            }
221
        }
222
223 8
        $this->settingsMap  = $settingsMap;
224 8
        $this->settingsData = $settingsData;
225 8
        $this->ambiguousMap = $ambiguousMap;
226 8
        $this->isProcessed  = true;
227
    }
228
229
    /**
230
     * @param SettingsInterface[] $instanceList
231
     *
232
     * @return SettingsInterface|null
233
     */
234 7
    private function selectChildSettings(array $instanceList): ?SettingsInterface
235
    {
236 7
        $count = count($instanceList);
237 7
        assert($count > 1);
238 7
        $selected = $this->selectChildSettingsAmongTwo($instanceList[0], $instanceList[1]);
239 7
        if ($selected !== null) {
240 7
            for ($index = 2; $index < $count; ++$index) {
241 7
                $selected = $this->selectChildSettingsAmongTwo($selected, $instanceList[$index]);
242 7
                if ($selected === null) {
243 7
                    break;
244
                }
245
            }
246
        }
247
248 7
        return $selected;
249
    }
250
251
    /**
252
     * @param SettingsInterface $instance1
253
     * @param SettingsInterface $instance2
254
     *
255
     * @return SettingsInterface|null
256
     */
257 7
    private function selectChildSettingsAmongTwo(
258
        SettingsInterface $instance1,
259
        SettingsInterface $instance2
260
    ): ?SettingsInterface {
261 7
        return is_subclass_of($instance1, get_class($instance2)) === true ?
262 7
            $instance1 : (is_subclass_of($instance2, get_class($instance1)) === true ? $instance2 : null);
263
    }
264
}
265