Completed
Branch FET/reg-form-builder/main (a66e69)
by
unknown
09:49 queued 19s
created

JsonDataHandler::resetErrors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\services\json;
4
5
use stdClass;
6
7
/**
8
 * Class JsonDataHandler
9
 * A class for handling serialization to and from JSON and tracking any errors that occur during that process
10
 *
11
 * @author  Brent Christensen
12
 * @package EventEspresso\core\services\json
13
 * @since   $VID:$
14
 */
15
class JsonDataHandler
16
{
17
    const DATA_TYPE_ARRAY     = 'array';
18
19
    const DATA_TYPE_OBJECT    = 'object';
20
21
    const DATA_TYPE_USE_FLAGS = 'flags';
22
23
    const NO_ERROR_MSG        = 'No error';
24
25
    /**
26
     * @var string
27
     */
28
    private $data_type;
29
30
    /**
31
     * @var array|stdClass
32
     */
33
    private $decoded_data;
34
35
    /**
36
     * JSON_BIGINT_AS_STRING,
37
     * JSON_INVALID_UTF8_IGNORE,
38
     * JSON_INVALID_UTF8_SUBSTITUTE,
39
     * JSON_OBJECT_AS_ARRAY,
40
     * JSON_THROW_ON_ERROR
41
     *
42
     * @var int
43
     */
44
    private $decode_flags;
45
46
    /**
47
     * @var int
48
     */
49
    private $depth;
50
51
    /**
52
     * @var string
53
     */
54
    private $encoded_data;
55
56
    /**
57
     * JSON_FORCE_OBJECT,
58
     * JSON_HEX_QUOT,
59
     * JSON_HEX_TAG,
60
     * JSON_HEX_AMP,
61
     * JSON_HEX_APOS,
62
     * JSON_INVALID_UTF8_IGNORE,
63
     * JSON_INVALID_UTF8_SUBSTITUTE,
64
     * JSON_NUMERIC_CHECK,
65
     * JSON_PARTIAL_OUTPUT_ON_ERROR,
66
     * JSON_PRESERVE_ZERO_FRACTION,
67
     * JSON_PRETTY_PRINT,
68
     * JSON_UNESCAPED_LINE_TERMINATORS,
69
     * JSON_UNESCAPED_SLASHES,
70
     * JSON_UNESCAPED_UNICODE,
71
     * JSON_THROW_ON_ERROR.
72
     *
73
     * @var int
74
     */
75
    private $encode_flags;
76
77
    /**
78
     * @var int
79
     */
80
    private $last_error_code = JSON_ERROR_NONE;
81
82
    /**
83
     * @var string
84
     */
85
    private $last_error_msg = JsonDataHandler::NO_ERROR_MSG;
86
87
88
    /**
89
     * JsonDataHandler constructor.
90
     */
91
    public function __construct()
92
    {
93
        if (! defined('JSON_INVALID_UTF8_IGNORE')) {
94
            define('JSON_INVALID_UTF8_IGNORE', 1048576);
95
        }
96
        if (! defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
97
            define('JSON_INVALID_UTF8_SUBSTITUTE', 2097152);
98
        }
99
        if (! defined('JSON_THROW_ON_ERROR')) {
100
            define('JSON_THROW_ON_ERROR', 4194304);
101
        }
102
    }
103
104
105
    /**
106
     * set $data_type, $decode_flags, $encode_flags, and depth all in one shot
107
     *
108
     * @param string $data_type
109
     * @param int    $decode_flags
110
     * @param int    $encode_flags
111
     * @param int    $depth
112
     */
113
    public function configure(
114
        string $data_type = JsonDataHandler::DATA_TYPE_USE_FLAGS,
115
        int $decode_flags = 0,
116
        int $encode_flags = 0,
117
        int $depth = 512
118
    ) {
119
        $this->setDataType($data_type);
120
        $this->setDecodeFlags($decode_flags);
121
        $this->setDepth($depth);
122
        $this->setEncodeFlags($encode_flags);
123
    }
124
125
126
    /**
127
     * @param string $data_type
128
     */
129
    public function setDataType(string $data_type): void
130
    {
131
        $this->data_type = $data_type === JsonDataHandler::DATA_TYPE_ARRAY
132
                           || $data_type === JsonDataHandler::DATA_TYPE_OBJECT
133
                           || $data_type === JsonDataHandler::DATA_TYPE_USE_FLAGS
134
            ? $data_type
135
            : JsonDataHandler::DATA_TYPE_USE_FLAGS;
136
    }
137
138
139
    /**
140
     * One or more Bitmask values:
141
     * JSON_BIGINT_AS_STRING,
142
     * JSON_INVALID_UTF8_IGNORE,        PHP >= 7.2
143
     * JSON_INVALID_UTF8_SUBSTITUTE,    PHP >= 7.2
144
     * JSON_OBJECT_AS_ARRAY,
145
     * JSON_THROW_ON_ERROR              PHP >= 7.3
146
     *
147
     * pass multiple values separated with |
148
     * ex: JSON_BIGINT_AS_STRING | JSON_INVALID_UTF8_IGNORE | JSON_OBJECT_AS_ARRAY
149
     *
150
     * @param int $decode_flags
151
     */
152
    public function setDecodeFlags(int $decode_flags): void
153
    {
154
        $this->decode_flags = $decode_flags === JSON_BIGINT_AS_STRING
155
                              || $decode_flags === JSON_OBJECT_AS_ARRAY
156
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_invalid_utf8_ignoreFound
157
                              || $decode_flags === JSON_INVALID_UTF8_IGNORE
158
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_invalid_utf8_substituteFound
159
                              || $decode_flags === JSON_INVALID_UTF8_SUBSTITUTE
160
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_throw_on_errorFound
161
                              || $decode_flags === JSON_THROW_ON_ERROR
162
            ? $decode_flags
163
            : 0;
164
    }
165
166
167
    /**
168
     * @param int $depth
169
     */
170
    public function setDepth(int $depth): void
171
    {
172
        $depth       = absint($depth);
173
        $this->depth = $depth ?: 512;
174
    }
175
176
177
    /**
178
     * One or more Bitmask values:
179
     * JSON_FORCE_OBJECT,
180
     * JSON_HEX_QUOT,
181
     * JSON_HEX_TAG,
182
     * JSON_HEX_AMP,
183
     * JSON_HEX_APOS,
184
     * JSON_INVALID_UTF8_IGNORE,        PHP >= 7.2
185
     * JSON_INVALID_UTF8_SUBSTITUTE,    PHP >= 7.2
186
     * JSON_NUMERIC_CHECK,
187
     * JSON_PARTIAL_OUTPUT_ON_ERROR,
188
     * JSON_PRESERVE_ZERO_FRACTION,
189
     * JSON_PRETTY_PRINT,
190
     * JSON_UNESCAPED_LINE_TERMINATORS,
191
     * JSON_UNESCAPED_SLASHES,
192
     * JSON_UNESCAPED_UNICODE,
193
     * JSON_THROW_ON_ERROR.             PHP >= 7.3
194
     *
195
     * pass multiple values separated with |
196
     * ex: JSON_FORCE_OBJECT | JSON_INVALID_UTF8_IGNORE | JSON_THROW_ON_ERROR
197
     *
198
     * @param int $encode_flags
199
     */
200
    public function setEncodeFlags(int $encode_flags): void
201
    {
202
        $this->encode_flags = $encode_flags === JSON_FORCE_OBJECT
203
                              || $encode_flags === JSON_HEX_QUOT
204
                              || $encode_flags === JSON_HEX_TAG
205
                              || $encode_flags === JSON_HEX_AMP
206
                              || $encode_flags === JSON_HEX_APOS
207
                              || $encode_flags === JSON_NUMERIC_CHECK
208
                              || $encode_flags === JSON_PARTIAL_OUTPUT_ON_ERROR
209
                              || $encode_flags === JSON_PRESERVE_ZERO_FRACTION
210
                              || $encode_flags === JSON_PRETTY_PRINT
211
                              || $encode_flags === JSON_UNESCAPED_LINE_TERMINATORS
212
                              || $encode_flags === JSON_UNESCAPED_SLASHES
213
                              || $encode_flags === JSON_UNESCAPED_UNICODE
214
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_invalid_utf8_ignoreFound
215
                              || $encode_flags === JSON_INVALID_UTF8_IGNORE
216
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_invalid_utf8_substituteFound
217
                              || $encode_flags === JSON_INVALID_UTF8_SUBSTITUTE
218
                              // phpcs:ignore PHPCompatibility.Constants.NewConstants.json_throw_on_errorFound
219
                              || $encode_flags === JSON_THROW_ON_ERROR
220
            ? $encode_flags
221
            : 0;
222
    }
223
224
225
    /**
226
     * @return bool|null
227
     */
228
    private function asAssociative(): ?bool
229
    {
230
        switch ($this->data_type) {
231
            case JsonDataHandler::DATA_TYPE_ARRAY:
232
                return true;
233
            case JsonDataHandler::DATA_TYPE_OBJECT:
234
                return false;
235
            case JsonDataHandler::DATA_TYPE_USE_FLAGS:
236
                return null;
237
        }
238
        return null;
239
    }
240
241
242
    /**
243
     * @param array|string $json
244
     * @return array|mixed|stdClass
245
     */
246
    public function decodeJson($json)
247
    {
248
        $this->resetErrors();
249
        if ($this->isJson($json)) {
250
            $this->decoded_data    = json_decode($json, $this->asAssociative(), $this->depth, $this->decode_flags);
251
            $this->last_error_code = json_last_error();
252
            $this->last_error_msg  = json_last_error_msg();
253
        } else {
254
            $this->decoded_data    = $json;
255
            $this->last_error_code = JSON_ERROR_NONE;
256
            $this->last_error_msg  = JsonDataHandler::NO_ERROR_MSG;
257
        }
258
        return $this->decoded_data;
259
    }
260
261
262
    /**
263
     * @param $data
264
     * @return string
265
     */
266
    public function encodeData($data): string
267
    {
268
        $this->resetErrors();
269
        if ($this->isJson($data)) {
270
            $this->encoded_data = $data;
271
            $this->last_error_code = JSON_ERROR_NONE;
272
            $this->last_error_msg  = JsonDataHandler::NO_ERROR_MSG;
273
        } else {
274
            $this->encoded_data = json_encode($data, $this->encode_flags, $this->depth);
275
            $this->last_error_code = json_last_error();
276
            $this->last_error_msg  = json_last_error_msg();
277
        }
278
        return $this->encoded_data ?: '{}';
279
    }
280
281
282
    /**
283
     * @return array|stdClass
284
     */
285
    public function getDecodedData()
286
    {
287
        return $this->decoded_data;
288
    }
289
290
291
    /**
292
     * @return string
293
     */
294
    public function getEncodedData(): string
295
    {
296
        return $this->encoded_data;
297
    }
298
299
300
    /**
301
     * @param bool $reset
302
     * @return int
303
     */
304
    public function getLastErrorCode(bool $reset = false): int
305
    {
306
        $last_error = $this->last_error_code;
307
        if ($reset) {
308
            $this->resetErrors();
309
        }
310
        return $last_error;
311
    }
312
313
314
    /**
315
     * @param bool $reset
316
     * @return string
317
     */
318
    public function getLastErrorMessage(bool $reset = false): string
319
    {
320
        $last_error = $this->last_error_msg;
321
        if ($reset) {
322
            $this->resetErrors();
323
        }
324
        return $last_error;
325
    }
326
327
328
    /**
329
     * @param array|string $maybe_json
330
     * @return bool
331
     */
332
    public function isJson($maybe_json): bool
333
    {
334
        if (! is_string($maybe_json)) {
335
            return false;
336
        }
337
        $decoded = json_decode($maybe_json, $this->asAssociative(), $this->depth, $this->decode_flags);
338
        return json_last_error() === JSON_ERROR_NONE && ! ($decoded === null && ! empty($maybe_json));
339
    }
340
341
342
    /**
343
     * @since $VID:$
344
     */
345
    public function resetErrors()
346
    {
347
        $this->last_error_code = JSON_ERROR_NONE;
348
        $this->last_error_msg  = JsonDataHandler::NO_ERROR_MSG;
349
    }
350
}
351