1
|
|
|
<?php namespace Arcanedev\Json; |
2
|
|
|
|
3
|
|
|
use Arcanedev\Json\Contracts\Json as JsonContract; |
4
|
|
|
use Illuminate\Contracts\Support\Arrayable; |
5
|
|
|
use Illuminate\Contracts\Support\Jsonable; |
6
|
|
|
use Illuminate\Filesystem\Filesystem; |
7
|
|
|
use Illuminate\Support\Collection; |
8
|
|
|
use JsonSerializable; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Class Json |
12
|
|
|
* |
13
|
|
|
* @package Arcanedev\Support |
14
|
|
|
* @author ARCANEDEV <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
class Json implements JsonContract, Arrayable, Jsonable, JsonSerializable |
17
|
|
|
{ |
18
|
|
|
/* ------------------------------------------------------------------------------------------------ |
19
|
|
|
| Properties |
20
|
|
|
| ------------------------------------------------------------------------------------------------ |
21
|
|
|
*/ |
22
|
|
|
/** |
23
|
|
|
* The file path. |
24
|
|
|
* |
25
|
|
|
* @var string |
26
|
|
|
*/ |
27
|
|
|
protected $path; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* The laravel filesystem instance. |
31
|
|
|
* |
32
|
|
|
* @var \Illuminate\Filesystem\Filesystem |
33
|
|
|
*/ |
34
|
|
|
protected $filesystem; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The attributes collection. |
38
|
|
|
* |
39
|
|
|
* @var \Illuminate\Support\Collection |
40
|
|
|
*/ |
41
|
|
|
protected $attributes; |
42
|
|
|
|
43
|
|
|
/* ------------------------------------------------------------------------------------------------ |
44
|
|
|
| Constructor |
45
|
|
|
| ------------------------------------------------------------------------------------------------ |
46
|
|
|
*/ |
47
|
|
|
/** |
48
|
|
|
* Construct the Json instance. |
49
|
|
|
* |
50
|
|
|
* @param string $path |
51
|
|
|
* @param \Illuminate\Filesystem\Filesystem $filesystem |
52
|
|
|
*/ |
53
|
54 |
|
public function __construct($path, Filesystem $filesystem = null) |
54
|
|
|
{ |
55
|
54 |
|
$this->setFilesystem($filesystem ?: new Filesystem); |
56
|
54 |
|
$this->loadFile($path); |
57
|
54 |
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Load the json file. |
61
|
|
|
* |
62
|
|
|
* @param string $path |
63
|
|
|
* |
64
|
|
|
* @return self |
65
|
|
|
*/ |
66
|
54 |
|
public function loadFile($path) |
67
|
|
|
{ |
68
|
54 |
|
$content = '{}'; |
69
|
|
|
|
70
|
54 |
|
if ( ! is_null($path)) { |
71
|
54 |
|
$this->setPath($path); |
72
|
|
|
|
73
|
54 |
|
$content = $this->filesystem->get($this->getPath()); |
74
|
18 |
|
} |
75
|
|
|
|
76
|
54 |
|
return $this->loadContent($content); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Load the json content. |
81
|
|
|
* |
82
|
|
|
* @param string $content |
83
|
|
|
* |
84
|
|
|
* @return self |
85
|
|
|
*/ |
86
|
54 |
|
public function loadContent($content) |
87
|
|
|
{ |
88
|
54 |
|
$this->attributes = Collection::make( |
89
|
54 |
|
$this->decode($content) |
90
|
18 |
|
); |
91
|
|
|
|
92
|
54 |
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/* ------------------------------------------------------------------------------------------------ |
96
|
|
|
| Getters & Setters |
97
|
|
|
| ------------------------------------------------------------------------------------------------ |
98
|
|
|
*/ |
99
|
|
|
/** |
100
|
|
|
* Set the filesystem instance. |
101
|
|
|
* |
102
|
|
|
* @param \Illuminate\Filesystem\Filesystem $filesystem |
103
|
|
|
* |
104
|
|
|
* @return self |
105
|
|
|
*/ |
106
|
54 |
|
public function setFilesystem(Filesystem $filesystem) |
107
|
|
|
{ |
108
|
54 |
|
$this->filesystem = $filesystem; |
109
|
|
|
|
110
|
54 |
|
return $this; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Get path. |
115
|
|
|
* |
116
|
|
|
* @return string |
117
|
|
|
*/ |
118
|
54 |
|
public function getPath() |
119
|
|
|
{ |
120
|
54 |
|
return $this->path; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Set path. |
125
|
|
|
* |
126
|
|
|
* @param mixed $path |
127
|
|
|
* |
128
|
|
|
* @return self |
129
|
|
|
*/ |
130
|
54 |
|
public function setPath($path) |
131
|
|
|
{ |
132
|
54 |
|
$this->path = (string) $path; |
133
|
|
|
|
134
|
54 |
|
return $this; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Get file contents as Collection. |
139
|
|
|
* |
140
|
|
|
* @return \Illuminate\Support\Collection |
141
|
|
|
*/ |
142
|
12 |
|
public function attributes() |
143
|
|
|
{ |
144
|
12 |
|
return $this->attributes; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/* ------------------------------------------------------------------------------------------------ |
148
|
|
|
| Main Functions |
149
|
|
|
| ------------------------------------------------------------------------------------------------ |
150
|
|
|
*/ |
151
|
|
|
/** |
152
|
|
|
* Decode ths json content. |
153
|
|
|
* |
154
|
|
|
* @param string $content |
155
|
|
|
* @param bool $assoc |
156
|
|
|
* @param int $depth |
157
|
|
|
* @param int $options |
158
|
|
|
* |
159
|
|
|
* @return array |
160
|
|
|
*/ |
161
|
54 |
|
public static function decode($content, $assoc = true, $options = 0, $depth = 512) |
162
|
|
|
{ |
163
|
54 |
|
$decoded = json_decode($content, $assoc, $depth, $options); |
164
|
|
|
|
165
|
|
|
// Check if json decode has errors |
166
|
54 |
|
if (($lastError = json_last_error()) !== JSON_ERROR_NONE) { |
167
|
15 |
|
JsonErrorHandler::handleDecodeErrors($lastError, $options, [ |
168
|
15 |
|
'depth' => $depth, |
169
|
15 |
|
'last_error_msg' => json_last_error_msg(), |
170
|
15 |
|
'last_error_number' => $lastError, |
171
|
5 |
|
]); |
172
|
|
|
} |
173
|
|
|
|
174
|
54 |
|
return $decoded; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Encode to json content. |
179
|
|
|
* |
180
|
|
|
* @param mixed $content |
181
|
|
|
* @param int $options |
182
|
|
|
* @param int $depth |
183
|
|
|
* |
184
|
|
|
* @return string |
185
|
|
|
*/ |
186
|
21 |
|
public function encode($content, $options = 0, $depth = 512) |
187
|
|
|
{ |
188
|
21 |
|
$encoded = json_encode($content, $options, $depth); |
189
|
|
|
|
190
|
|
|
// Check if json encode has errors |
191
|
21 |
|
if (($lastError = json_last_error()) !== JSON_ERROR_NONE) { |
192
|
12 |
|
JsonErrorHandler::handleEncodeErrors($lastError, $options, [ |
193
|
12 |
|
'depth' => $depth, |
194
|
12 |
|
'last_error_msg' => json_last_error_msg(), |
195
|
12 |
|
'last_error_number' => $lastError, |
196
|
4 |
|
]); |
197
|
|
|
} |
198
|
|
|
|
199
|
9 |
|
return $encoded; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Make new instance. |
204
|
|
|
* |
205
|
|
|
* @param string $path |
206
|
|
|
* @param \Illuminate\Filesystem\Filesystem $filesystem |
207
|
|
|
* |
208
|
|
|
* @return static |
209
|
|
|
*/ |
210
|
9 |
|
public static function make($path = null, Filesystem $filesystem = null) |
211
|
|
|
{ |
212
|
9 |
|
return new static($path, $filesystem); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Load json content. |
217
|
|
|
* |
218
|
|
|
* @param string $content |
219
|
|
|
* |
220
|
|
|
* @return static |
221
|
|
|
*/ |
222
|
3 |
|
public static function fromContent($content) |
223
|
|
|
{ |
224
|
3 |
|
return static::make()->loadContent($content); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Get the specified attribute from json file. |
229
|
|
|
* |
230
|
|
|
* @param string $key |
231
|
|
|
* @param mixed|null $default |
232
|
|
|
* |
233
|
|
|
* @return mixed |
234
|
|
|
*/ |
235
|
3 |
|
public function get($key, $default = null) |
236
|
|
|
{ |
237
|
3 |
|
return $this->attributes->get($key, $default); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Set a specific key & value. |
242
|
|
|
* |
243
|
|
|
* @param string $key |
244
|
|
|
* @param mixed $value |
245
|
|
|
* |
246
|
|
|
* @return self |
247
|
|
|
*/ |
248
|
3 |
|
public function set($key, $value) |
249
|
|
|
{ |
250
|
3 |
|
$this->attributes->put($key, $value); |
251
|
|
|
|
252
|
3 |
|
return $this; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Merge json attributes with a given array items. |
257
|
|
|
* |
258
|
|
|
* @param array $items |
259
|
|
|
* |
260
|
|
|
* @return self |
261
|
|
|
*/ |
262
|
3 |
|
public function merge(array $items) |
263
|
|
|
{ |
264
|
3 |
|
$this->attributes = $this->attributes->merge($items); |
265
|
|
|
|
266
|
3 |
|
return $this; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Update json contents from array items. |
271
|
|
|
* |
272
|
|
|
* @param array $items |
273
|
|
|
* @param string $path |
274
|
|
|
* |
275
|
|
|
* @return bool |
276
|
|
|
*/ |
277
|
3 |
|
public function update(array $items, $path = null) |
278
|
|
|
{ |
279
|
3 |
|
return $this->merge($items)->save($path); |
|
|
|
|
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Save the current attributes array to the file storage. |
284
|
|
|
* |
285
|
|
|
* @param string|null $path |
286
|
|
|
* |
287
|
|
|
* @return bool |
288
|
|
|
*/ |
289
|
6 |
|
public function save($path = null) |
290
|
|
|
{ |
291
|
6 |
|
return $this->filesystem->put($path ?: $this->getPath(), $this->toJsonPretty()); |
|
|
|
|
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Handle call to __call method. |
296
|
|
|
* |
297
|
|
|
* @param string $method |
298
|
|
|
* @param array $arguments |
299
|
|
|
* |
300
|
|
|
* @return mixed |
301
|
|
|
*/ |
302
|
3 |
|
public function __call($method, $arguments = []) |
303
|
|
|
{ |
304
|
3 |
|
return method_exists($this, $method) |
305
|
1 |
|
? call_user_func_array([$this, $method], $arguments) |
306
|
3 |
|
: $this->attributes->get($method); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Handle magic method __get. |
311
|
|
|
* |
312
|
|
|
* @param string $key |
313
|
|
|
* |
314
|
|
|
* @return mixed |
315
|
|
|
*/ |
316
|
3 |
|
public function __get($key) |
317
|
|
|
{ |
318
|
3 |
|
return $this->get($key); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* Handle call to __toString method. |
323
|
|
|
* |
324
|
|
|
* @return string |
325
|
|
|
*/ |
326
|
6 |
|
public function __toString() |
327
|
|
|
{ |
328
|
6 |
|
return $this->toJsonPretty(); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* Convert the given array data to pretty json. |
333
|
|
|
* |
334
|
|
|
* @param array $data |
335
|
|
|
* |
336
|
|
|
* @return string |
337
|
|
|
*/ |
338
|
9 |
|
public function toJsonPretty(array $data = null) |
339
|
|
|
{ |
340
|
9 |
|
return static::encode($data ?: $this->attributes, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Convert to array. |
345
|
|
|
* |
346
|
|
|
* @return array |
347
|
|
|
*/ |
348
|
9 |
|
public function toArray() |
349
|
|
|
{ |
350
|
9 |
|
return $this->attributes()->toArray(); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Convert the object to its JSON representation. |
355
|
|
|
* |
356
|
|
|
* @param int $options |
357
|
|
|
* |
358
|
|
|
* @return string |
359
|
|
|
*/ |
360
|
3 |
|
public function toJson($options = 0) |
361
|
|
|
{ |
362
|
3 |
|
return json_encode($this->toArray(), $options); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Allow to encode the json object to json file. |
367
|
|
|
* |
368
|
|
|
* @return array |
369
|
|
|
*/ |
370
|
3 |
|
public function jsonSerialize() |
371
|
|
|
{ |
372
|
3 |
|
return $this->toArray(); |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.