VersionStore   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 276
Duplicated Lines 18.48 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 2
dl 51
loc 276
rs 9.3999
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setChildInvalidationConstraint() 0 6 1
A fetch() 0 8 2
A contains() 0 4 1
A containsPath() 0 4 1
A containsKey() 0 4 1
B update() 26 26 5
B register() 25 25 5
B invalidateChildren() 0 17 5
A getSegments() 0 16 2
A createKey() 0 4 1
A saveRecord() 0 4 1
A fetchRecord() 0 4 1
A createRecord() 0 4 1
A updateVersion() 0 4 1
A addChild() 0 4 1
A createKeyFromSegments() 0 4 1
A createPathFromKey() 0 4 1
A createKeyFromPath() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/*
3
 * This file is part of the KleijnWeb\RestETagBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace KleijnWeb\RestETagBundle\Version;
10
11
use Psr\SimpleCache\CacheInterface;
12
use Symfony\Component\HttpFoundation\Request;
13
14
/**
15
 * @author John Kleijn <[email protected]>
16
 */
17
class VersionStore
18
{
19
    const KEY_VERSION = 1;
20
    const KEY_CHILDREN = 2;
21
22
    /**
23
     * @var CacheInterface
24
     */
25
    private $cache;
26
27
    /**
28
     * @var string
29
     */
30
    private $childInvalidationConstraint;
31
32
    /**
33
     * @param CacheInterface  $cache
34
     * @param string $childInvalidationConstraint
35
     */
36
    public function __construct(CacheInterface $cache, $childInvalidationConstraint = '')
37
    {
38
        $this->cache = $cache;
39
        $this->childInvalidationConstraint = $childInvalidationConstraint;
40
    }
41
42
    /**
43
     * @param string $childInvalidationConstraint
44
     *
45
     * @return $this
46
     */
47
    public function setChildInvalidationConstraint($childInvalidationConstraint)
48
    {
49
        $this->childInvalidationConstraint = $childInvalidationConstraint;
50
51
        return $this;
52
    }
53
54
    /**
55
     * @param Request $request
56
     *
57
     * @return string
58
     */
59
    public function fetch(Request $request)
60
    {
61
        if (!$record = $this->fetchRecord($this->createKey($request))) {
62
            return '';
63
        }
64
65
        return $record[self::KEY_VERSION];
66
    }
67
68
    /**
69
     * @param Request $request
70
     *
71
     * @return bool
72
     */
73
    public function contains(Request $request)
74
    {
75
        return $this->containsKey($this->createKey($request));
76
    }
77
78
    /**
79
     * @param string $path
80
     *
81
     * @return bool
82
     */
83
    public function containsPath($path)
84
    {
85
        return $this->containsKey($this->createKeyFromPath($path));
86
    }
87
88
    /**
89
     * @param string $key
90
     *
91
     * @return bool
92
     */
93
    public function containsKey($key)
94
    {
95
        return $this->cache->has($key);
96
    }
97
98
    /**
99
     * @param Request $request
100
     * @param string  $version
101
     *
102
     * @return mixed
103
     */
104 View Code Duplication
    public function update(Request $request, $version)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
    {
106
        $segments = $this->getSegments($request);
107
        $paths = [];
108
        $path = '';
109
        foreach ($segments as $segment) {
110
            $path .= "#$segment";
111
            $paths[] = $path;
112
        }
113
114
        foreach ($paths as $i => $path) {
115
            $record = $this->fetchRecord($path);
116
            if ($record) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $record of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
117
                $this->invalidateChildren($record, $version);
118
            } else {
119
                $record = $this->createRecord();
120
            }
121
            $this->updateVersion($record, $version);
122
            if (isset($paths[$i + 1])) {
123
                $this->addChild($record, $paths[$i + 1]);
124
            }
125
            $this->cache->set($path, $record);
126
        }
127
128
        return $version;
129
    }
130
131
    /**
132
     * @param Request $request
133
     * @param string  $version
134
     *
135
     * @return mixed
136
     */
137 View Code Duplication
    public function register(Request $request, $version)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
    {
139
        $segments = $this->getSegments($request);
140
        $paths = [];
141
        $path = '';
142
        foreach ($segments as $segment) {
143
            $path .= "#$segment";
144
            $paths[] = $path;
145
        }
146
147
        foreach ($paths as $i => $path) {
148
            $record = $this->fetchRecord($path);
149
            if (!$record) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $record of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
150
                $record = $this->createRecord($version);
151
            }
152
            if (isset($paths[$i + 1])) {
153
                $this->addChild($record, $paths[$i + 1]);
154
            }
155
            $this->saveRecord($path, $record);
156
        }
157
        $record = $this->createRecord($version);
158
        $this->cache->set($this->createKeyFromSegments($segments), $record);
159
160
        return $version;
161
    }
162
163
    /**
164
     * @param array  $record
165
     * @param string $version
166
     */
167
    private function invalidateChildren(array $record, $version)
168
    {
169
        foreach ($record[self::KEY_CHILDREN] as $child) {
170
            if ($this->childInvalidationConstraint !== ''
171
                && preg_match("/$this->childInvalidationConstraint/", $this->createPathFromKey($child))
172
            ) {
173
                // Stop recursive invalidation if it matches
174
                return;
175
            }
176
            $record = $this->fetchRecord($child);
177
            $this->updateVersion($record, $version);
178
            $this->saveRecord($child, $record);
179
            if ($record) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $record of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
180
                $this->invalidateChildren($record, $version);
181
            }
182
        }
183
    }
184
185
    /**
186
     * @param Request $request
187
     *
188
     * @return array
189
     */
190
    private function getSegments(Request $request)
191
    {
192
        $key = $request->getPathInfo();
193
        $segments = explode('/', ltrim($key, '/'));
194
        if ($query = $request->getQueryString()) {
195
            $segments[] = '?' . $query;
196
        }
197
198
        array_walk($segments, function (&$value) {
199
            $value = preg_replace('/[^[:print:]]/', '_', $value);
200
        });
201
202
        return array_filter($segments, function ($value) {
203
            return $value !== '';
204
        });
205
    }
206
207
    /**
208
     * @param Request $request
209
     *
210
     * @return string
211
     */
212
    private function createKey(Request $request)
213
    {
214
        return $this->createKeyFromSegments($this->getSegments($request));
215
    }
216
217
    /**
218
     * @param string $key
219
     * @param array  $record
220
     */
221
    private function saveRecord($key, array $record)
222
    {
223
        $this->cache->set($key, $record);
224
    }
225
226
    /**
227
     * @param string $key
228
     *
229
     * @return array
230
     */
231
    private function fetchRecord($key)
232
    {
233
        return $this->cache->get($key);
234
    }
235
236
    /**
237
     * @param array  $children
238
     * @param string $version
239
     *
240
     * @return array
241
     */
242
    private function createRecord($version = '', $children = [])
243
    {
244
        return [self::KEY_VERSION => $version, self::KEY_CHILDREN => $children];
245
    }
246
247
    /**
248
     * @param array  $record
249
     * @param string $version
250
     */
251
    private function updateVersion(array &$record, $version)
252
    {
253
        $record[self::KEY_VERSION] = $version;
254
    }
255
256
    /**
257
     * @param array  $record
258
     * @param string $key
259
     */
260
    private function addChild(array &$record, $key)
261
    {
262
        $record[self::KEY_CHILDREN][] = $key;
263
    }
264
265
    /**
266
     * @param array $segments
267
     *
268
     * @return string
269
     */
270
    private function createKeyFromSegments(array $segments)
271
    {
272
        return '#' . implode('#', $segments);
273
    }
274
275
    /**
276
     * @param string $key
277
     * @return string
278
     */
279
    private function createPathFromKey(string $key)
280
    {
281
        return strtr($key, '#', '/');
282
    }
283
284
    /**
285
     * @param string $path
286
     * @return string
287
     */
288
    private function createKeyFromPath(string $path)
289
    {
290
        return strtr($path, '/', '#');
291
    }
292
}
293