Completed
Push — master ( 4e5b68...f757a2 )
by Freek
02:00
created

Valuestore::push()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 26
rs 8.439
cc 5
eloc 13
nc 10
nop 2
1
<?php
2
3
namespace Spatie\Valuestore;
4
5
use ArrayAccess;
6
7
class Valuestore implements ArrayAccess
8
{
9
    /** @var string */
10
    protected $fileName;
11
12
    /**
13
     * @param string $fileName
14
     *
15
     * @return $this
16
     */
17
    public static function make(string $fileName)
18
    {
19
        return (new static())->setFileName($fileName);
20
    }
21
22
    protected function __construct()
23
    {
24
    }
25
26
    /**
27
     * Set the filename where all values will be stored.
28
     *
29
     * @param string $fileName
30
     *
31
     * @return $this
32
     */
33
    protected function setFileName(string $fileName)
34
    {
35
        $this->fileName = $fileName;
36
37
        return $this;
38
    }
39
40
    /**
41
     * Put a value in the store.
42
     *
43
     * @param string|array    $name
44
     * @param string|int|null $value
45
     *
46
     * @return $this
47
     */
48
    public function put($name, $value = null)
49
    {
50
        $newValues = $name;
51
52
        if (!is_array($name)) {
53
            $newValues = [$name => $value];
54
        }
55
56
        $newContent = array_merge($this->all(), $newValues);
57
58
        $this->setContent($newContent);
59
60
        return $this;
61
    }
62
63
    /**
64
     * Push a new value into an array.
65
     *
66
     * @param string $name
67
     * @param $pushValue
68
     *
69
     * @return $this
70
     */
71
    public function push(string $name, $pushValue)
72
    {
73
        if (!is_array($pushValue)) {
74
            $pushValue = [$pushValue];
75
        }
76
77
        if (!$this->has($name)) {
78
            $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...
79
80
            return $this;
81
        }
82
83
        $oldValue = $this->get($name);
84
85
        if (!is_array($oldValue)) {
86
            $oldValue = [$oldValue];
87
        }
88
89
        if (is_array($oldValue)) {
90
            $newValue = array_merge($oldValue, $pushValue);
91
        }
92
93
        $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...
94
95
        return $this;
96
    }
97
98
    /**
99
     * Get a value from the store.
100
     *
101
     * @param string $name
102
     * @param $default
103
     *
104
     * @return null|string
105
     */
106
    public function get(string $name, $default = null)
107
    {
108
        if (!array_key_exists($name, $this->all())) {
109
            return $default;
110
        }
111
112
        return $this->all()[$name];
113
    }
114
115
    /*
116
     * Determine if the store has a value for the given name.
117
     */
118
    public function has(string $name) : bool
119
    {
120
        return array_key_exists($name, $this->all());
121
    }
122
123
    /**
124
     * Get all values from the store.
125
     *
126
     * @param string $startingWith
127
     *
128
     * @return array
129
     */
130
    public function all(string $startingWith = '') : array
131
    {
132
        if (!file_exists($this->fileName)) {
133
            return [];
134
        }
135
136
        $values = json_decode(file_get_contents($this->fileName), true);
137
138
        if ($startingWith !== '') {
139
            return $this->filterKeysStartingWith($this->all(), $startingWith);
140
        }
141
142
        return $values;
143
    }
144
145
    /**
146
     * Forget a value from the store.
147
     *
148
     * @param string $key
149
     *
150
     * @return $this
151
     */
152
    public function forget(string $key)
153
    {
154
        $newContent = $this->all();
155
156
        unset($newContent[$key]);
157
158
        $this->setContent($newContent);
159
160
        return $this;
161
    }
162
163
    /**
164
     * Flush all values from the store.
165
     *
166
     * @param string $startingWith
167
     *
168
     * @return $this
169
     */
170
    public function flush(string $startingWith = '')
171
    {
172
        $newContent = [];
173
174
        if ($startingWith !== '') {
175
            $newContent = $this->filterKeysNotStartingWith($this->all(), $startingWith);
176
        }
177
178
        return $this->setContent($newContent);
179
    }
180
181
    /**
182
     * Get and forget a value from the store.
183
     *
184
     * @param string $name
185
     *
186
     * @return null|string
187
     */
188
    public function pull(string $name)
189
    {
190
        $value = $this->get($name);
191
192
        $this->forget($name);
193
194
        return $value;
195
    }
196
197
    /**
198
     * Increment a value from the store.
199
     *
200
     * @param string $name
201
     * @param int    $by
202
     *
203
     * @return int|null|string
204
     */
205
    public function increment(string $name, int $by = 1)
206
    {
207
        $currentValue = $this->get($name) ?? 0;
208
209
        $newValue = $currentValue + $by;
210
211
        $this->put($name, $newValue);
212
213
        return $newValue;
214
    }
215
216
    /**
217
     * Decrement a value from the store.
218
     *
219
     * @param string $name
220
     * @param int    $by
221
     *
222
     * @return int|null|string
223
     */
224
    public function decrement(string $name, int $by = 1)
225
    {
226
        return $this->increment($name, $by * -1);
227
    }
228
229
    protected function filterKeysStartingWith(array $values, string $startsWith) : array
230
    {
231
        return array_filter($values, function ($key) use ($startsWith) {
232
233
            return $this->startsWith($key, $startsWith);
234
235
        }, ARRAY_FILTER_USE_KEY);
236
    }
237
238
    protected function filterKeysNotStartingWith(array $values, string $startsWith) : array
239
    {
240
        return array_filter($values, function ($key) use ($startsWith) {
241
242
            return !$this->startsWith($key, $startsWith);
243
244
        }, ARRAY_FILTER_USE_KEY);
245
    }
246
247
    protected function startsWith(string $haystack, string $needle) : bool
248
    {
249
        return substr($haystack, 0, strlen($needle)) === $needle;
250
    }
251
252
    /**
253
     * @param array $values
254
     *
255
     * @return $this
256
     */
257
    protected function setContent(array $values)
258
    {
259
        file_put_contents($this->fileName, json_encode($values));
260
261
        return $this;
262
    }
263
264
    /**
265
     * Whether a offset exists.
266
     *
267
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
268
     *
269
     * @param mixed $offset <p>
270
     *                      An offset to check for.
271
     *                      </p>
272
     *
273
     * @return bool true on success or false on failure.
274
     *              </p>
275
     *              <p>
276
     *              The return value will be casted to boolean if non-boolean was returned.
277
     *
278
     * @since 5.0.0
279
     */
280
    public function offsetExists($offset)
281
    {
282
        return $this->has($offset);
283
    }
284
285
    /**
286
     * Offset to retrieve.
287
     *
288
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
289
     *
290
     * @param mixed $offset <p>
291
     *                      The offset to retrieve.
292
     *                      </p>
293
     *
294
     * @return mixed Can return all value types.
295
     *
296
     * @since 5.0.0
297
     */
298
    public function offsetGet($offset)
299
    {
300
        return $this->get($offset);
301
    }
302
303
    /**
304
     * Offset to set.
305
     *
306
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
307
     *
308
     * @param mixed $offset <p>
309
     *                      The offset to assign the value to.
310
     *                      </p>
311
     * @param mixed $value  <p>
312
     *                      The value to set.
313
     *                      </p>
314
     *
315
     * @since 5.0.0
316
     */
317
    public function offsetSet($offset, $value)
318
    {
319
        $this->put($offset, $value);
320
    }
321
322
    /**
323
     * Offset to unset.
324
     *
325
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
326
     *
327
     * @param mixed $offset <p>
328
     *                      The offset to unset.
329
     *                      </p>
330
     *
331
     * @since 5.0.0
332
     */
333
    public function offsetUnset($offset)
334
    {
335
        $this->forget($offset);
336
    }
337
}
338