Completed
Push — master ( 699208...147e57 )
by Freek
02:39
created

Valuestore::prepend()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 26
Code Lines 13

Duplication

Lines 26
Ratio 100 %

Importance

Changes 0
Metric Value
dl 26
loc 26
rs 8.439
c 0
b 0
f 0
cc 5
eloc 13
nc 10
nop 2
1
<?php
2
3
namespace Spatie\Valuestore;
4
5
use ArrayAccess;
6
use Countable;
7
8
class Valuestore implements ArrayAccess, Countable
9
{
10
    /** @var string */
11
    protected $fileName;
12
13
    /**
14
     * @param string $fileName
15
     *
16
     * @return $this
17
     */
18
    public static function make(string $fileName)
19
    {
20
        return (new static())->setFileName($fileName);
21
    }
22
23
    protected function __construct()
24
    {
25
    }
26
27
    /**
28
     * Set the filename where all values will be stored.
29
     *
30
     * @param string $fileName
31
     *
32
     * @return $this
33
     */
34
    protected function setFileName(string $fileName)
35
    {
36
        $this->fileName = $fileName;
37
38
        return $this;
39
    }
40
41
    /**
42
     * Put a value in the store.
43
     *
44
     * @param string|array    $name
45
     * @param string|int|null $value
46
     *
47
     * @return $this
48
     */
49
    public function put($name, $value = null)
50
    {
51
        $newValues = $name;
52
53
        if (! is_array($name)) {
54
            $newValues = [$name => $value];
55
        }
56
57
        $newContent = array_merge($this->all(), $newValues);
58
59
        $this->setContent($newContent);
60
61
        return $this;
62
    }
63
64
    /**
65
     * Push a new value into an array.
66
     *
67
     * @param string $name
68
     * @param $pushValue
69
     *
70
     * @return $this
71
     */
72 View Code Duplication
    public function push(string $name, $pushValue)
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...
73
    {
74
        if (! is_array($pushValue)) {
75
            $pushValue = [$pushValue];
76
        }
77
78
        if (! $this->has($name)) {
79
            $this->put($name, $pushValue);
0 ignored issues
show
Documentation introduced by
$pushValue is of type array, but the function expects a string|integer|null.

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...
80
81
            return $this;
82
        }
83
84
        $oldValue = $this->get($name);
85
86
        if (! is_array($oldValue)) {
87
            $oldValue = [$oldValue];
88
        }
89
90
        if (is_array($oldValue)) {
91
            $newValue = array_merge($oldValue, $pushValue);
92
        }
93
94
        $this->put($name, $newValue);
0 ignored issues
show
Bug introduced by
The variable $newValue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Documentation introduced by
$newValue is of type array, but the function expects a string|integer|null.

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...
95
96
        return $this;
97
    }
98
99
    /**
100
     * Prepend a new value in an array.
101
     *
102
     * @param string $name
103
     * @param $prependValue
104
     *
105
     * @return $this
106
     */
107 View Code Duplication
    public function prepend(string $name, $prependValue)
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...
108
    {
109
        if (! is_array($prependValue)) {
110
            $prependValue = [$prependValue];
111
        }
112
113
        if (! $this->has($name)) {
114
            $this->put($name, $prependValue);
0 ignored issues
show
Documentation introduced by
$prependValue is of type array, but the function expects a string|integer|null.

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...
115
116
            return $this;
117
        }
118
119
        $oldValue = $this->get($name);
120
121
        if (! is_array($oldValue)) {
122
            $oldValue = [$oldValue];
123
        }
124
125
        if (is_array($oldValue)) {
126
            $newValue = array_merge($prependValue, $oldValue);
127
        }
128
129
        $this->put($name, $newValue);
0 ignored issues
show
Bug introduced by
The variable $newValue does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Documentation introduced by
$newValue is of type array, but the function expects a string|integer|null.

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...
130
131
        return $this;
132
    }
133
134
    /**
135
     * Get a value from the store.
136
     *
137
     * @param string $name
138
     * @param $default
139
     *
140
     * @return null|string
141
     */
142
    public function get(string $name, $default = null)
143
    {
144
        if (! array_key_exists($name, $this->all())) {
145
            return $default;
146
        }
147
148
        return $this->all()[$name];
149
    }
150
151
    /*
152
     * Determine if the store has a value for the given name.
153
     */
154
    public function has(string $name) : bool
155
    {
156
        return array_key_exists($name, $this->all());
157
    }
158
159
    /**
160
     * Get all values from the store.
161
     *
162
     * @return array
163
     */
164
    public function all() : array
165
    {
166
        if (! file_exists($this->fileName)) {
167
            return [];
168
        }
169
170
        return json_decode(file_get_contents($this->fileName), true);
171
    }
172
173
    /**
174
     * Get all keys starting with a given string from the store.
175
     *
176
     * @param string $startingWith
177
     *
178
     * @return array
179
     */
180
    public function allStartingWith(string $startingWith = '') : array
181
    {
182
        $values = $this->all();
183
184
        if ($startingWith === '') {
185
            return $values;
186
        }
187
188
        return $this->filterKeysStartingWith($values, $startingWith);
189
    }
190
191
    /**
192
     * Forget a value from the store.
193
     *
194
     * @param string $key
195
     *
196
     * @return $this
197
     */
198
    public function forget(string $key)
199
    {
200
        $newContent = $this->all();
201
202
        unset($newContent[$key]);
203
204
        $this->setContent($newContent);
205
206
        return $this;
207
    }
208
209
    /**
210
     * Flush all values from the store.
211
     *
212
     * @return $this
213
     */
214
    public function flush()
215
    {
216
        return $this->setContent([]);
217
    }
218
219
    /**
220
     * Flush all values which keys start with a given string.
221
     *
222
     * @param string $startingWith
223
     *
224
     * @return $this
225
     */
226
    public function flushStartingWith(string $startingWith = '')
227
    {
228
        $newContent = [];
229
230
        if ($startingWith !== '') {
231
            $newContent = $this->filterKeysNotStartingWith($this->all(), $startingWith);
232
        }
233
234
        return $this->setContent($newContent);
235
    }
236
237
    /**
238
     * Get and forget a value from the store.
239
     *
240
     * @param string $name
241
     *
242
     * @return null|string
243
     */
244
    public function pull(string $name)
245
    {
246
        $value = $this->get($name);
247
248
        $this->forget($name);
249
250
        return $value;
251
    }
252
253
    /**
254
     * Increment a value from the store.
255
     *
256
     * @param string $name
257
     * @param int    $by
258
     *
259
     * @return int|null|string
260
     */
261
    public function increment(string $name, int $by = 1)
262
    {
263
        $currentValue = $this->get($name) ?? 0;
264
265
        $newValue = $currentValue + $by;
266
267
        $this->put($name, $newValue);
268
269
        return $newValue;
270
    }
271
272
    /**
273
     * Decrement a value from the store.
274
     *
275
     * @param string $name
276
     * @param int    $by
277
     *
278
     * @return int|null|string
279
     */
280
    public function decrement(string $name, int $by = 1)
281
    {
282
        return $this->increment($name, $by * -1);
283
    }
284
285
    /**
286
     * Whether a offset exists.
287
     *
288
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
289
     *
290
     * @param mixed $offset
291
     *
292
     * @return bool
293
     */
294
    public function offsetExists($offset)
295
    {
296
        return $this->has($offset);
297
    }
298
299
    /**
300
     * Offset to retrieve.
301
     *
302
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
303
     *
304
     * @param mixed $offset
305
     *
306
     * @return mixed
307
     */
308
    public function offsetGet($offset)
309
    {
310
        return $this->get($offset);
311
    }
312
313
    /**
314
     * Offset to set.
315
     *
316
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
317
     *
318
     * @param mixed $offset
319
     * @param mixed $value
320
     */
321
    public function offsetSet($offset, $value)
322
    {
323
        $this->put($offset, $value);
324
    }
325
326
    /**
327
     * Offset to unset.
328
     *
329
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
330
     *
331
     * @param mixed $offset
332
     */
333
    public function offsetUnset($offset)
334
    {
335
        $this->forget($offset);
336
    }
337
338
    /**
339
     * Count elements.
340
     *
341
     * @link http://php.net/manual/en/countable.count.php
342
     *
343
     * @return int
344
     */
345
    public function count()
346
    {
347
        return count($this->all());
348
    }
349
350
    protected function filterKeysStartingWith(array $values, string $startsWith) : array
351
    {
352
        return array_filter($values, function ($key) use ($startsWith) {
353
            return $this->startsWith($key, $startsWith);
354
        }, ARRAY_FILTER_USE_KEY);
355
    }
356
357
    protected function filterKeysNotStartingWith(array $values, string $startsWith) : array
358
    {
359
        return array_filter($values, function ($key) use ($startsWith) {
360
            return ! $this->startsWith($key, $startsWith);
361
        }, ARRAY_FILTER_USE_KEY);
362
    }
363
364
    protected function startsWith(string $haystack, string $needle) : bool
365
    {
366
        return substr($haystack, 0, strlen($needle)) === $needle;
367
    }
368
369
    /**
370
     * @param array $values
371
     *
372
     * @return $this
373
     */
374
    protected function setContent(array $values)
375
    {
376
        file_put_contents($this->fileName, json_encode($values));
377
378
        if (! count($values)) {
379
            unlink($this->fileName);
380
        }
381
382
        return $this;
383
    }
384
}
385