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) |
|
|
|
|
73
|
|
|
{ |
74
|
|
|
if (! is_array($pushValue)) { |
75
|
|
|
$pushValue = [$pushValue]; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
if (! $this->has($name)) { |
79
|
|
|
$this->put($name, $pushValue); |
|
|
|
|
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); |
|
|
|
|
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) |
|
|
|
|
108
|
|
|
{ |
109
|
|
|
if (! is_array($prependValue)) { |
110
|
|
|
$prependValue = [$prependValue]; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
if (! $this->has($name)) { |
114
|
|
|
$this->put($name, $prependValue); |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|
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.