MemoryConfigCollection::get()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.0729

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 6
cts 7
cp 0.8571
rs 9.4555
c 0
b 0
f 0
cc 5
nc 4
nop 3
crap 5.0729
1
<?php
2
3
namespace SilverStripe\Config\Collections;
4
5
use SilverStripe\Config\MergeStrategy\Priority;
6
use SilverStripe\Config\Middleware\MiddlewareAware;
7
use SilverStripe\Config\Transformer\TransformerInterface;
8
use Serializable;
9
10
/**
11
 * Basic mutable config collection stored in memory
12
 */
13
class MemoryConfigCollection implements MutableConfigCollectionInterface, Serializable
14
{
15
    use MiddlewareAware;
16
17
    /**
18
     * Stores a list of key/value config prior to middleware being applied
19
     *
20
     * @var array
21
     */
22
    protected $config = [];
23
24
    /**
25
     * Call cache for non-trivial config calls including middleware
26
     *
27
     * @var array
28
     */
29
    protected $callCache = [];
30
31
    /**
32
     * @var array
33
     */
34
    protected $metadata = [];
35
36
    /**
37
     * @var array
38
     */
39
    protected $history = [];
40
41
    /**
42
     * @var boolean
43
     */
44
    protected $trackMetadata = false;
45
46
    /**
47
     * ConfigCollection constructor.
48
     *
49
     * @param bool $trackMetadata
50
     */
51 31
    public function __construct($trackMetadata = false)
52
    {
53 31
        $this->trackMetadata = $trackMetadata;
54 31
    }
55
56
    /**
57
     * @return static
58
     */
59
    public static function create()
60
    {
61
        return new static();
62
    }
63
64
    /**
65
     * Trigger transformers to load into this store
66
     *
67
     * @param  TransformerInterface[] $transformers
68
     * @return $this
69
     */
70 18
    public function transform($transformers)
71
    {
72 18
        foreach ($transformers as $transformer) {
73 18
            $transformer->transform($this);
74 16
        }
75 16
        return $this;
76
    }
77
78 28
    public function set($class, $name, $data, $metadata = [])
79
    {
80 28
        $this->saveMetadata($class, $metadata);
81
82 28
        $classKey = strtolower($class);
83 28
        if ($name) {
84 6
            if (!isset($this->config[$classKey])) {
85 6
                $this->config[$classKey] = [];
86 6
            }
87 6
            $this->config[$classKey][$name] = $data;
88 6
        } else {
89 28
            $this->config[$classKey] = $data;
90
        }
91
92
        // Flush call cache
93 28
        $this->callCache = [];
94 28
        return $this;
95
    }
96
97 28
    public function get($class, $name = null, $excludeMiddleware = 0)
98
    {
99 28
        if (!is_int($excludeMiddleware) && $excludeMiddleware !== true) {
100
            throw new \InvalidArgumentException("Invalid middleware flags");
101
        }
102
103
        // Get config for complete class
104 28
        $config = $this->getClassConfig($class, $excludeMiddleware);
105
106
        // Return either name, or whole-class config
107 28
        if ($name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
108 7
            return isset($config[$name]) ? $config[$name] : null;
109
        }
110 28
        return $config;
111
    }
112
113
    /**
114
     * Retrieve config for an entire class
115
     *
116
     * @param string $class Name of class
117
     * @param int|true $excludeMiddleware Optional flag of middleware to disable.
118
     * Passing in `true` disables all middleware.
119
     * Can also pass in int flags to specify specific middlewares.
120
     * @return array
121
     */
122 28
    protected function getClassConfig($class, $excludeMiddleware = 0)
123
    {
124
        // `true` excludes all middleware, so bypass call cache
125 28
        $classKey = strtolower($class);
126 28
        if ($excludeMiddleware === true) {
127 27
            return isset($this->config[$classKey]) ? $this->config[$classKey] : [];
128
        }
129
130
        // Check cache
131 28
        if (isset($this->callCache[$classKey][$excludeMiddleware])) {
132 3
            return $this->callCache[$classKey][$excludeMiddleware];
133
        }
134
135
        // Build middleware
136 28
        $result = $this->callMiddleware(
137 28
            $class,
138 28
            $excludeMiddleware,
139
            function ($class, $excludeMiddleware) {
0 ignored issues
show
Unused Code introduced by
The parameter $excludeMiddleware is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
140 27
                return $this->getClassConfig($class, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
141
            }
142 28
        );
143
144
        // Save cache
145 28
        if (!isset($this->callCache[$classKey])) {
146 28
            $this->callCache[$classKey] = [];
147 28
        }
148 28
        $this->callCache[$classKey][$excludeMiddleware] = $result;
149 28
        return $result;
150
    }
151
152 2
    public function exists($class, $name = null, $excludeMiddleware = 0)
153
    {
154 2
        $config = $this->get($class, null, $excludeMiddleware);
155 2
        if (empty($config)) {
156 2
            return false;
157
        }
158 1
        if ($name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
159
            return array_key_exists($name, $config);
160
        }
161 1
        return true;
162
    }
163
164 1
    public function remove($class, $name = null)
165
    {
166 1
        $classKey = strtolower($class);
167 1
        if ($name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
168
            unset($this->config[$classKey][$name]);
169
        } else {
170 1
            unset($this->config[$classKey]);
171
        }
172
        // Discard call cache
173 1
        unset($this->callCache[$classKey]);
174 1
        return $this;
175
    }
176
177 1
    public function removeAll()
178
    {
179 1
        $this->config = [];
180 1
        $this->metadata = [];
181 1
        $this->history = [];
182 1
        $this->callCache = [];
183 1
    }
184
185
    /**
186
     * Get complete config (excludes middleware-applied config)
187
     *
188
     * @return array
189
     */
190
    public function getAll()
191
    {
192
        return $this->config;
193
    }
194
195
    /**
196
     * @deprecated 4.0...5.0
197
     *
198
     * Synonym for merge()
199
     *
200
     * @param string $class
201
     * @param string $name
202
     * @param mixed  $value
203
     * @return $this
204
     */
205
    public function update($class, $name, $value)
206
    {
207
        $this->merge($class, $name, $value);
208
        return $this;
209
    }
210
211
    public function merge($class, $name, $value)
212
    {
213
        // Detect mergeable config
214
        $existing = $this->get($class, $name, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
215
        if (is_array($value) && is_array($existing)) {
216
            $value = Priority::mergeArray($value, $existing);
217
        }
218
219
        // Apply
220
        $this->set($class, $name, $value);
221
        return $this;
222
    }
223
224 8
    public function getMetadata()
225
    {
226 8
        if (!$this->trackMetadata || !is_array($this->metadata)) {
227 7
            return [];
228
        }
229
230 1
        return $this->metadata;
231
    }
232
233 2
    public function getHistory()
234
    {
235 2
        if (!$this->trackMetadata || !is_array($this->history)) {
236 1
            return [];
237
        }
238
239 1
        return $this->history;
240
    }
241
242
    /**
243
     * Get list of serialized properties
244
     *
245
     * @return array
246
     */
247
    protected function getSerializedMembers()
248
    {
249
        return array_filter(array_keys(get_object_vars($this)), function ($key) {
250
            // Skip $_underscoreProp
251
            return strpos($key, '_') !== 0;
252
        });
253
    }
254
255
    public function serialize()
256
    {
257
        // Auto-serialize
258
        $data = [];
259
        foreach ($this->getSerializedMembers() as $key) {
260
            $data[$key] = $this->$key;
261
        }
262
        return serialize($data);
263
    }
264
265
    public function unserialize($serialized)
266
    {
267
        $data = unserialize($serialized);
268
        foreach ($this->getSerializedMembers() as $key) {
269
            $this->$key = isset($data[$key]) ? $data[$key] : null;
270
        }
271
    }
272
273
    public function nest()
274
    {
275
        return clone $this;
276
    }
277
278
    /**
279
     * Save metadata for the given class
280
     *
281
     * @param string $class
282
     * @param array  $metadata
283
     */
284 28
    protected function saveMetadata($class, $metadata)
285
    {
286 28
        if (!$this->trackMetadata) {
287 27
            return;
288
        }
289
290 1
        $classKey = strtolower($class);
291 1
        if (isset($this->metadata[$classKey]) && isset($this->config[$classKey])) {
292 1
            if (!isset($this->history[$classKey])) {
293 1
                $this->history[$classKey] = [];
294 1
            }
295
296 1
            array_unshift(
297 1
                $this->history[$classKey],
298
                [
299 1
                'value' => $this->config[$classKey],
300 1
                'metadata' => $this->metadata[$classKey]
301 1
                ]
302 1
            );
303 1
        }
304
305 1
        $this->metadata[$classKey] = $metadata;
306 1
    }
307
}
308