Context   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 350
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 51
lcom 1
cbo 5
dl 0
loc 350
rs 7.92
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getAvailableConfigs() 0 8 2
A addAvailableConfig() 0 10 3
A setAvailableConfigs() 0 6 1
A loadConfigsFromPath() 0 20 5
A __construct() 0 6 1
A dispatchMetaEvent() 0 4 1
A getConfigurationForValue() 0 21 5
A getMetaForValue() 0 4 1
A isSupported() 0 16 4
A addNewConfig() 0 19 3
A setCurrentPage() 0 6 1
A getCurrentPage() 0 4 1
A registerMeta() 0 15 3
A getRegisteredMeta() 0 4 1
A isPreviewMode() 0 4 1
A setPreviewMode() 0 4 1
A offsetSet() 0 8 2
A offsetExists() 0 4 1
A offsetUnset() 0 6 1
A offsetGet() 0 8 3
B temporaryUnset() 0 24 6
A restoreTemporaryUnset() 0 13 3
A reset() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like Context often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Context, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Templates System.
5
 *
6
 * Copyright 2015 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2015 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Component\TemplatesSystem\Gimme\Context;
16
17
use Doctrine\Common\Cache\Cache;
18
use SWP\Component\TemplatesSystem\Gimme\Event\MetaEvent;
19
use SWP\Component\TemplatesSystem\Gimme\Meta\Meta;
20
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Yaml\Parser;
23
24
class Context implements \ArrayAccess
25
{
26
    const META_EVENT_NAME = 'swp.templates_system.meta.load';
27
28
    /**
29
     * Array with current page information's.
30
     *
31
     * @var Meta
32
     */
33
    protected $currentPage;
34
35
    /**
36
     * Array will all registered meta types.
37
     *
38
     * @var Meta[]
39
     */
40
    protected $registeredMeta = [];
41
42
    /**
43
     * Array with available meta configs.
44
     *
45
     * @var array
46
     */
47
    protected $availableConfigs = [];
48
49
    protected $dispatcher;
50
51
    protected $metadataCache;
52
53
    /**
54
     * @var string
55
     */
56
    protected $configsPath;
57
58
    /**
59
     * @var array
60
     */
61
    private $temporaryMeta = [];
62
63
    /**
64
     * @var bool
65
     */
66
    private $previewMode = false;
67
68
    /**
69
     * @var array
70
     */
71
    private $supportedCache = [];
72
73
    /**
74
     * @var array
75
     */
76
    private $configurationCache = [];
77
78
    public function __construct(EventDispatcherInterface $dispatcher, Cache $metadataCache, $configsPath = null)
79
    {
80
        $this->metadataCache = $metadataCache;
81
        $this->configsPath = $configsPath;
82
        $this->dispatcher = $dispatcher;
83
    }
84
85
    public function dispatchMetaEvent(MetaEvent $event): void
86
    {
87
        $this->dispatcher->dispatch(self::META_EVENT_NAME, $event);
88
    }
89
90
    public function getAvailableConfigs(): array
91
    {
92
        if (0 === count($this->availableConfigs)) {
93
            $this->loadConfigsFromPath($this->configsPath);
94
        }
95
96
        return $this->availableConfigs;
97
    }
98
99
    /**
100
     * @param array $configuration
101
     *
102
     * @return bool
103
     */
104
    public function addAvailableConfig(array $configuration)
105
    {
106
        if (isset($configuration['class']) && !isset($this->availableConfigs[$configuration['class']])) {
107
            $this->availableConfigs[$configuration['class']] = $configuration;
108
109
            return true;
110
        }
111
112
        return false;
113
    }
114
115
    /**
116
     * @param array $availableConfigs
117
     *
118
     * @return Context
119
     */
120
    public function setAvailableConfigs(array $availableConfigs)
121
    {
122
        $this->availableConfigs = $availableConfigs;
123
124
        return $this;
125
    }
126
127
    /**
128
     * @param string $configsPath
129
     */
130
    public function loadConfigsFromPath($configsPath)
131
    {
132
        if (file_exists($configsPath)) {
133
            if (!$this->metadataCache->contains('metadata_config_files')) {
134
                $finder = new Finder();
135
                $finder->in($configsPath)->files()->name('*.{yaml,yml}');
136
                $files = [];
137
                foreach ($finder as $file) {
138
                    $files[] = $file->getRealPath();
139
                    $this->addNewConfig($file->getRealPath());
140
                }
141
142
                $this->metadataCache->save('metadata_config_files', $files);
143
            } else {
144
                foreach ($this->metadataCache->fetch('metadata_config_files') as $file) {
145
                    $this->addNewConfig($file);
146
                }
147
            }
148
        }
149
    }
150
151
    public function getConfigurationForValue($value): array
152
    {
153
        if (false === is_object($value)) {
154
            throw new \Exception('Context supports configuration loading only for objects');
155
        }
156
157
        $objectClassName = get_class($value);
158
        if (array_key_exists($objectClassName, $this->configurationCache)) {
159
            return $this->configurationCache[$objectClassName];
160
        }
161
162
        foreach ($this->getAvailableConfigs() as $class => $configuration) {
163
            if ($value instanceof $class) {
164
                $this->configurationCache[$objectClassName] = $configuration;
165
166
                return $configuration;
167
            }
168
        }
169
170
        return [];
171
    }
172
173
    public function getMetaForValue($value): Meta
174
    {
175
        return new Meta($this, $value, $this->getConfigurationForValue($value));
176
    }
177
178
    public function isSupported($value)
179
    {
180
        if (!is_object($value)) {
181
            return false;
182
        }
183
184
        $objectClassName = get_class($value);
185
        if (array_key_exists($objectClassName, $this->supportedCache)) {
186
            return $this->supportedCache[$objectClassName];
187
        }
188
189
        $result = count($this->getConfigurationForValue($value)) > 0 ? true : false;
190
        $this->supportedCache[$objectClassName] = $result;
191
192
        return $result;
193
    }
194
195
    public function addNewConfig(string $filePath)
196
    {
197
        $cacheKey = md5($filePath);
198
        if (!$this->metadataCache->contains($cacheKey)) {
199
            if (!is_readable($filePath)) {
200
                throw new \InvalidArgumentException('Configuration file is not readable for parser');
201
            }
202
            $parser = new Parser();
203
            $configuration = $parser->parse(file_get_contents($filePath));
204
            $this->metadataCache->save($cacheKey, $configuration);
205
        } else {
206
            $configuration = $this->metadataCache->fetch($cacheKey);
207
        }
208
209
        $this->addAvailableConfig($configuration);
210
        $this->supportedCache = [];
211
212
        return $configuration;
213
    }
214
215
    public function setCurrentPage(Meta $currentPage): self
216
    {
217
        $this->currentPage = $currentPage;
218
219
        return $this;
220
    }
221
222
    /**
223
     * Get current context page information's.
224
     *
225
     * @return Meta
226
     */
227
    public function getCurrentPage()
228
    {
229
        return $this->currentPage;
230
    }
231
232
    public function registerMeta(Meta $meta = null)
233
    {
234
        $configuration = $meta->getConfiguration();
0 ignored issues
show
Bug introduced by
It seems like $meta is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
235
        $name = $configuration['name'];
236
        if (!array_key_exists($name, $this->registeredMeta)) {
237
            $this->registeredMeta[$name] = $configuration;
238
            if (null !== $meta) {
239
                $this[$name] = $meta;
240
            }
241
242
            return true;
243
        }
244
245
        return false;
246
    }
247
248
    /**
249
     * @return Meta[]
250
     */
251
    public function getRegisteredMeta()
252
    {
253
        return $this->registeredMeta;
254
    }
255
256
    /**
257
     * @return bool
258
     */
259
    public function isPreviewMode(): bool
260
    {
261
        return $this->previewMode;
262
    }
263
264
    /**
265
     * @param bool $previewMode
266
     */
267
    public function setPreviewMode(bool $previewMode)
268
    {
269
        $this->previewMode = $previewMode;
270
    }
271
272
    /**
273
     * {@inheritdoc}
274
     */
275
    public function offsetSet($name, $meta)
276
    {
277
        if (array_key_exists($name, $this->registeredMeta)) {
278
            $this->$name = $meta;
279
        }
280
281
        return true;
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     */
287
    public function offsetExists($name)
288
    {
289
        return isset($this->$name);
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295
    public function offsetUnset($name)
296
    {
297
        unset($this->$name);
298
299
        return true;
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function offsetGet($name)
306
    {
307
        if (array_key_exists($name, $this->registeredMeta) && isset($this->$name)) {
308
            return $this->$name;
309
        }
310
311
        return false;
312
    }
313
314
    /**
315
     * @param array $keys
316
     *
317
     * @return string
318
     */
319
    public function temporaryUnset(array $keys)
320
    {
321
        $metas = [];
322
        $keysId = md5(serialize($keys));
323
324
        if (0 === count($keys)) {
325
            foreach ($this->registeredMeta as $key => $configuration) {
326
                if (isset($this[$key])) {
327
                    $metas[$key] = $this[$key];
328
                    unset($this[$key]);
329
                }
330
            }
331
        }
332
333
        foreach ($keys as $key) {
334
            if (array_key_exists($key, $this->registeredMeta)) {
335
                $metas[$key] = $this[$key];
336
                unset($this[$key]);
337
            }
338
        }
339
        $this->temporaryMeta[$keysId] = $metas;
340
341
        return $keysId;
342
    }
343
344
    /**
345
     * @param string $id
346
     *
347
     * @return null|true
348
     */
349
    public function restoreTemporaryUnset($id)
350
    {
351
        $metas = $this->temporaryMeta[$id];
352
        if (!is_array($metas)) {
353
            return;
354
        }
355
356
        foreach ($metas as $key => $value) {
357
            $this[$key] = $value;
358
        }
359
360
        return true;
361
    }
362
363
    /**
364
     *  Resets context data.
365
     */
366
    public function reset()
367
    {
368
        $this->currentPage = null;
369
        $this->registeredMeta = [];
370
        $this->availableConfigs = [];
371
        $this->previewMode = false;
372
    }
373
}
374