Completed
Push — master ( 64ede7...feafc2 )
by Matt
02:35
created

functions.php ➔ schema_extract()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8.006

Importance

Changes 0
Metric Value
cc 8
eloc 19
nc 7
nop 3
dl 0
loc 29
ccs 21
cts 22
cp 0.9545
crap 8.006
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
namespace League\JsonGuard;
4
5
use Sabre\Uri;
6
7
/**
8
 * @param string $json
9
 * @param bool   $assoc
10
 * @param int    $depth
11
 * @param int    $options
12
 * @return mixed
13
 * @throws \InvalidArgumentException
14
 */
15
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
16
{
17 52
    $data = \json_decode($json, $assoc, $depth, $options);
18
19 52
    if (json_last_error() !== JSON_ERROR_NONE) {
20 2
        throw new \InvalidArgumentException(sprintf('Invalid JSON: %s', json_last_error_msg()));
21
    }
22
23 50
    return $data;
24
}
25
26
/**
27
 * @param $string
28
 * @return int
29
 */
30
function strlen($string)
31
{
32 100
    if (extension_loaded('intl')) {
33 100
        return grapheme_strlen($string);
34
    }
35
36
    if (extension_loaded('mbstring')) {
37
        return mb_strlen($string, mb_detect_encoding($string));
38
    }
39
40
    return \strlen($string);
41
}
42
43
/**
44
 * Returns the string representation of a value.
45
 *
46
 * @param mixed $value
47
 * @return string
48
 */
49
function as_string($value)
50
{
51 154
    if (is_resource($value)) {
52 2
        return '<RESOURCE>';
53
    }
54
55 152
    return (string) json_encode($value);
56
}
57
58
/**
59
 * Get the properties matching $pattern from the $data.
60
 *
61
 * @param string       $pattern
62
 * @param array|object $data
63
 * @return array
64
 */
65
function properties_matching_pattern($pattern, $data)
66
{
67
    // If an object is supplied, extract an array of the property names.
68 12
    if (is_object($data)) {
69 12
        $data = array_keys(get_object_vars($data));
70 12
    }
71
72 12
    return preg_grep(delimit_pattern($pattern), $data);
73
}
74
75
/**
76
 * Delimit a regular expression pattern.
77
 *
78
 * The regular expression syntax used for JSON schema is ECMA 262, from Javascript,
79
 * and does not use delimiters.  Since the PCRE functions do, this function will
80
 * delimit a pattern and escape the delimiter if found in the pattern.
81
 *
82
 * @see http://json-schema.org/latest/json-schema-validation.html#anchor6
83
 * @see http://php.net/manual/en/regexp.reference.delimiters.php
84
 *
85
 * @param string $pattern
86
 *
87
 * @return string
88
 */
89
function delimit_pattern($pattern)
90
{
91 16
    return '/' . str_replace('/', '\\/', $pattern) . '/';
92
}
93
94
/**
95
 * Escape a JSON Pointer.
96
 *
97
 * @param  string $pointer
98
 * @return string
99
 */
100
function escape_pointer($pointer)
101
{
102 116
    $pointer = str_replace('~', '~0', $pointer);
103 116
    return str_replace('/', '~1', $pointer);
104
}
105
106
107
/**
108
 * Push a segment onto the given JSON Pointer.
109
 *
110
 * @param string $path
111
 * @param string $segment
112
 *
113
 * @return string
114
 */
115
function pointer_push($path, $segment)
116
{
117 112
    return $path . '/' . escape_pointer($segment);
118
}
119
120
/**
121
 * Determines if the value is an integer or an integer that was cast to a string
122
 * because it is larger than PHP_INT_MAX.
123
 *
124
 * @param  mixed $value
125
 * @return boolean
126
 */
127
function is_json_integer($value)
128
{
129 154
    if (is_string($value) && strlen($value) && $value[0] === '-') {
130 6
        $value = substr($value, 1);
131 6
    }
132
133 154
    return is_int($value) || (is_string($value) && ctype_digit($value) && compare($value, PHP_INT_MAX) === 1);
134
}
135
136
/**
137
 * Determines if the value is a number.  A number is a float, integer, or a number that was cast
138
 * to a string because it is larger than PHP_INT_MAX.
139
 *
140
 * @param mixed $value
141
 *
142
 * @return boolean
143
 */
144
function is_json_number($value)
145
{
146 124
    return is_float($value) || is_json_integer($value);
147
}
148
149
/**
150
 * @param string|double|int $leftOperand
151
 * @param string|double|int $rightOperand
152
 *
153
 * @return int Returns 0 if the two operands are equal, 1 if the left_operand is larger than the right_operand,
154
 * -1 otherwise.
155
 */
156
function compare($leftOperand, $rightOperand)
157
{
158 42
    return Comparator::compare($leftOperand, $rightOperand);
159
}
160
161
/**
162
 * Removes the fragment from a reference.
163
 *
164
 * @param  string $ref
165
 * @return string
166
 */
167
function strip_fragment($ref)
168
{
169 46
    $fragment = parse_url($ref, PHP_URL_FRAGMENT);
170
171 46
    return $fragment ? str_replace($fragment, '', $ref) : $ref;
172
}
173
174
/**
175
 * Determine if a reference is relative.
176
 * A reference is relative if it does not being with a prefix.
177
 *
178
 * @param string $ref
179
 *
180
 * @return bool
181
 */
182
function is_relative_ref($ref)
183
{
184 26
    return !preg_match('#^.+\:\/\/.*#', $ref);
185
}
186
187
/**
188
 * @param string $value
189
 *
190
 * @return bool
191
 */
192
function is_internal_ref($value)
193
{
194 56
    return is_string($value) && substr($value, 0, 1) === '#';
195
}
196
197
/**
198
 * Resolve the given id against the parent scope and return the resolved URI.
199
 *
200
 * @param string $id          The id to resolve.  This should be a valid relative or absolute URI.
201
 * @param string $parentScope The parent scope to resolve against.  Should be a valid URI or empty.
202
 *
203
 * @return string
204
 */
205
function resolve_uri($id, $parentScope)
206
{
207
    // If there is no parent scope, there is nothing to resolve against.
208 38
    if ($parentScope === '') {
209 14
        return $id;
210
    }
211
212 32
    return Uri\resolve($parentScope, $id);
213
}
214
215
/**
216
 * Recursively iterates over each value in the schema passing them to the callback function.
217
 * If the callback function returns true, the value is returned into the result array, keyed by a JSON Pointer.
218
 *
219
 * @param mixed    $schema
220
 * @param callable $callback
221
 * @param string   $pointer
222
 *
223
 * @return array
224
 */
225
function schema_extract($schema, callable $callback, $pointer = '')
226
{
227 166
    $matches = [];
228
229 166
    if (!is_array($schema) && !is_object($schema)) {
230 42
        return $matches;
231
    }
232
233 166
    foreach ($schema as $keyword => $value) {
234 166
        switch (true) {
235 166
            case is_object($value):
236 98
                $matches = array_merge($matches, schema_extract($value, $callback, pointer_push($pointer, $keyword)));
237 98
                break;
238 166
            case is_array($value):
239 66
                foreach ($value as $k => $v) {
240 62
                    $matches = array_merge(
241 62
                        $matches,
242 62
                        schema_extract($v, $callback, pointer_push(pointer_push($pointer, $keyword), $k))
243 62
                    );
244 66
                }
245 66
                break;
246 162
            case $callback($keyword, $value):
247 56
                $matches[$pointer] = $value;
248 56
                break;
249
        }
250 166
    }
251
252 166
    return $matches;
253
}
254