Json   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 216
rs 10
c 3
b 0
f 0
wmc 25
lcom 1
cbo 6

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B validate() 0 55 9
A getEncodedJSON() 0 9 2
A getDecodedJSON() 0 10 3
C validateAndConvertData() 0 41 7
A decodeJSON() 0 11 3
1
<?php namespace CMPayments\Json;
2
3
use CMPayments\Cache\Cache;
4
use CMPayments\JsonLint\Exceptions\JsonLintException;
5
use CMPayments\JsonLint\Exceptions\ParseException;
6
use CMPayments\JsonLint\JsonLinter;
7
use CMPayments\SchemaValidator\BaseValidator;
8
use CMPayments\Json\Exceptions\JsonException;
9
use CMPayments\SchemaValidator\Exceptions\ValidateSchemaException;
10
use CMPayments\SchemaValidator\SchemaValidator;
11
12
/**
13
 * Class Json
14
 *
15
 * @package CMPayments\SchemaValidator
16
 * @Author  Boy Wijnmaalen <[email protected]>
17
 */
18
class Json
19
{
20
    const SCHEMA   = 'Schema';
21
    const INPUT    = 'Input';
22
    const ERRORS   = 'errors';
23
    const WARNINGS = 'warnings';
24
25
    /**
26
     * @var null|object|string
27
     */
28
    private $input = null;
29
30
    /**
31
     * @var null|object|string
32
     */
33
    private $schema = null;
34
35
    /**
36
     * @var bool
37
     */
38
    private $isValid = false;
39
40
    /**
41
     * @var null|string
42
     */
43
    private $encodedJSON;
44
45
    /**
46
     * @var bool
47
     */
48
    private $validatedInput = null;
49
50
    /**
51
     * Json constructor.
52
     *
53
     * @param string $input
54
     */
55
    public function __construct($input)
56
    {
57
        $this->input = $input;
58
    }
59
60
    /**
61
     * Validates $this->input and optionally against $this->schema as well
62
     *
63
     * @param null|string $schema
64
     * @param array       $passthru Stores the error(s) that might occur during validation
65
     * @param array       $options
66
     *
67
     * @return bool
68
     * @throws JsonException|ParseException|JsonLintException|ValidateSchemaException|bool
69
     */
70
    public function validate($schema = null, &$passthru = [], $options = [])
71
    {
72
        $passthru = [
73
            self::ERRORS   => [],
74
            self::WARNINGS => []
75
        ];
76
77
        try {
78
            $cache = new Cache($options, $passthru);
79
80
            if (!is_null($schema)) {
81
82
                // validate $schema
83
                $this->schema = $this->validateAndConvertData($schema, self::SCHEMA, $cache);
84
            }
85
86
            // validate $this->input
87
            if (empty($this->validatedInput)) {
88
89
                $this->validatedInput = $this->validateAndConvertData($this->input, self::INPUT, $cache);
90
            }
91
92
            if (!is_null($this->schema)) {
93
94
                // validate $input against $this->schema
95
                $validator = new SchemaValidator($this->validatedInput, $this->schema, $cache);
96
97
                // check if there are errors, if so store them
98
                if (!$validator->isValid()) {
99
100
                    foreach ($validator->getErrors() as $error) {
101
102
                        $passthru[self::ERRORS][] = $error;
103
                    }
104
105
                    return $this->isValid = false;
106
                }
107
108
                // if $this->schema->type is other than object, decode it again but this time with $assoc = true
109
                $this->input = ($this->schema->type === BaseValidator::OBJECT) ? $this->validatedInput : $this->decodeJSON($this->input, self::INPUT, true);
110
            }
111
        } catch (ValidateSchemaException $e) {
112
113
            // re throw this one
114
            throw $e;
115
        } catch (\Exception $e) {
116
117
            // convert Exception to array
118
            $passthru[self::ERRORS][] = convert_exception_to_array($e);
119
120
            return $this->isValid = false;
121
        }
122
123
        return $this->isValid = true;
124
    }
125
126
    /**
127
     *  Returns the encoded JSON
128
     *
129
     * @return string
130
     */
131
    public function getEncodedJSON()
132
    {
133
        if (empty($this->encodedJSON)) {
134
135
            $this->encodedJSON = json_encode($this->input);
136
        }
137
138
        return $this->encodedJSON;
139
    }
140
141
    /**
142
     *  Returns the decoded JSON
143
     *
144
     * @return mixed|null|string
145
     * @throws JsonException
146
     */
147
    public function getDecodedJSON()
148
    {
149
        // cannot decode was not found valid by $this->validate() (or when $this->validate was never called)
150
        if (!$this->isValid) {
151
152
            return null;
153
        }
154
155
        return (is_string($this->input)) ? $this->validatedInput : $this->input;
156
    }
157
158
    /**
159
     * Validates if a string is valid JSON and converts it back to an object
160
     *
161
     * @param string|null|object $data
162
     * @param string             $type
163
     * @param Cache              $cache
164
     *
165
     * @return mixed
166
     * @throws JsonException
167
     * @throws ParseException|null
168
     * @throws ValidateSchemaException
169
     */
170
    private function validateAndConvertData($data, $type, Cache $cache)
171
    {
172
        // check type $data
173
        if (!is_string($data)) {
174
175
            throw new JsonException(JsonException::ERROR_INPUT_IS_NOT_OF_TYPE_STRING, [$type, gettype($data)]);
176
        } elseif (empty($data)) {
177
178
            throw new JsonException(JsonException::ERROR_INPUT_IS_OF_TYPE_STRING_BUT_EMPTY, [$type]);
179
        }
180
181
        if ($type === self::SCHEMA) {
182
183
            // calculate and set filename
184
            $cache->setFilename(md5($data) . '.php');
185
186
            // if cache file exits it means that this Schema has been correctly validated before
187
            if (file_exists($cache->getAbsoluteFilePath())) {
188
189
                return $this->decodeJSON($data, $type);
190
            }
191
        }
192
193
        // check if $data variable is valid JSON
194
        if (($result = (new JsonLinter())->lint($data)) instanceof JsonLintException) {
195
196
            // if the current $data
197
            if ($type === self::SCHEMA) {
198
199
                $result = new ValidateSchemaException(
200
                    ValidateSchemaException::ERROR_SCHEMA_IS_NOT_VALID_JSON,
201
                    [$data],
202
                    $result->getMessage()
203
                );
204
            }
205
206
            throw $result;
207
        }
208
209
        return $this->decodeJSON($data, $type);
210
    }
211
212
    /**
213
     * Does the actual decoding of a JSON string
214
     *
215
     * @param      $data
216
     * @param      $type
217
     * @param bool $assoc
218
     *
219
     * @return mixed
220
     * @throws JsonException
221
     */
222
    private function decodeJSON($data, $type, $assoc = false)
223
    {
224
        $result = json_decode($data, $assoc);
225
226
        if (empty($result) && (json_last_error() !== JSON_ERROR_NONE)) {
227
228
            throw new JsonException(JsonException::ERROR_INPUT_IS_NOT_VALID_JSON, [$type, $data]);
229
        }
230
231
        return $result;
232
    }
233
}