Completed
Pull Request — master (#20)
by
unknown
01:30
created

functions.php ➔ determineMediaType()   C

Complexity

Conditions 14
Paths 75

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 14

Importance

Changes 0
Metric Value
cc 14
eloc 25
nc 75
nop 1
dl 0
loc 41
ccs 27
cts 27
cp 1
crap 14
rs 5.0864
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace League\JsonReference;
4
5
use Sabre\Uri;
6
7
/**
8
 * @param object|array $json
9
 *
10
 * @return Pointer
11
 */
12
function pointer(&$json)
13
{
14 26
    return new Pointer($json);
15
}
16
17
/**
18
 * Escape a JSON Pointer.
19
 *
20
 * @param  string $pointer
21
 * @return string
22
 */
23
function escape_pointer($pointer)
24
{
25
    return str_replace(['~', '/'], ['~0', '~1'], $pointer);
26
}
27
28
29
/**
30
 * Push a segment onto the given JSON Pointer.
31
 *
32
 * @param string   $pointer
33
 * @param string[] $segments
34
 *
35
 * @return string
36
 *
37
 */
38
function pointer_push($pointer, ...$segments)
39
{
40 52
    $segments = str_replace(['~', '/'], ['~0', '~1'], $segments);
41 52
    return ($pointer !== '/' ? $pointer : '') . '/' . implode('/', $segments);
42
}
43
44
/**
45
 * Removes the fragment from a reference.
46
 *
47
 * @param  string $ref
48
 * @return string
49
 */
50
function strip_fragment($ref)
51
{
52 48
    $fragment = Uri\parse($ref)['fragment'];
53
54 48
    return $fragment ? str_replace('#'.$fragment, '#', $ref) : $ref;
55
}
56
57
/**
58
 * Check if the reference contains a fragment and resolve
59
 * the pointer.  Otherwise returns the original schema.
60
 *
61
 * @param  string $ref
62
 * @param  object $schema
63
 *
64
 * @return object
65
 */
66
function resolve_fragment($ref, $schema)
67
{
68 46
    $fragment = Uri\parse($ref)['fragment'];
69
70 46
    if (!is_internal_ref($ref) && is_string($fragment)) {
71 6
        return (new Pointer($schema))->get($fragment);
72
    }
73
74 42
    return $schema;
75
}
76
77
/**
78
 * @param string $keyword
79
 * @param mixed  $value
80
 *
81
 * @return bool
82
 */
83
function is_ref($keyword, $value)
84
{
85 52
    return $keyword === '$ref' && is_string($value);
86
}
87
88
/**
89
 * Determine if a reference is relative.
90
 * A reference is relative if it does not being with a prefix.
91
 *
92
 * @param string $ref
93
 *
94
 * @return bool
95
 */
96
function is_relative_ref($ref)
97
{
98 50
    return !preg_match('#^.+\:\/\/.*#', $ref);
99
}
100
101
/**
102
 * @param string $value
103
 *
104
 * @return bool
105
 */
106
function is_internal_ref($value)
107
{
108 64
    return is_string($value) && substr($value, 0, 1) === '#';
109
}
110
111
/**
112
 * Parse an external reference returning the prefix and path.
113
 *
114
 * @param string $ref
115
 *
116
 * @return array
117
 *
118
 * @throws \InvalidArgumentException
119
 */
120
function parse_external_ref($ref)
121
{
122 50
    if (is_relative_ref($ref)) {
123 2
        throw new \InvalidArgumentException(
124 2
            sprintf(
125
                'The path  "%s" was expected to be an external reference but is missing a prefix.  ' .
126 2
                'The schema path should start with a prefix i.e. "file://".',
127 1
                $ref
128 1
            )
129 1
        );
130
    }
131
132 48
    list($prefix, $path) = explode('://', $ref, 2);
133 48
    $path = rtrim(strip_fragment($path), '#');
134
135 48
    return [$prefix, $path];
136
}
137
138
/**
139
 * Resolve the given id against the parent scope and return the resolved URI.
140
 *
141
 * @param string $id          The id to resolve.  This should be a valid relative or absolute URI.
142
 * @param string $parentScope The parent scope to resolve against.  Should be a valid URI or empty.
143
 *
144
 * @return string
145
 */
146
function resolve_uri($id, $parentScope)
147
{
148
    // If there is no parent scope, there is nothing to resolve against.
149 40
    if ($parentScope === '') {
150 10
        return $id;
151
    }
152
153 32
    return Uri\resolve($parentScope, $id);
154
}
155
156
/**
157
 * Recursively iterates over each value in the schema passing them to the callback function.
158
 * If the callback function returns true the value is returned into the result array, keyed by a JSON Pointer.
159
 *
160
 * @param mixed    $schema
161
 * @param callable $callback
162
 * @param string   $pointer
163
 *
164
 * @return array
165
 */
166
function schema_extract($schema, callable $callback, $pointer = '')
167
{
168 54
    $matches = [];
169
170 54
    if ($schema instanceof Reference || (!is_array($schema) && !is_object($schema))) {
171 18
        return $matches;
172
    }
173
174 54
    foreach ($schema as $keyword => $value) {
175 27
        switch (true) {
176 54
            case is_object($value):
177 48
                $matches = array_merge($matches, schema_extract($value, $callback, pointer_push($pointer, $keyword)));
178 48
                break;
179 54
            case is_array($value):
180 22
                foreach ($value as $k => $v) {
181 22
                    if ($callback($k, $v)) {
182
                        $matches[pointer_push($pointer, $keyword)] = $v;
183
                    } else {
184 22
                        $matches = array_merge(
185 22
                            $matches,
186 22
                            schema_extract($v, $callback, pointer_push($pointer, $keyword, $k))
187 11
                        );
188
                    }
189 11
                }
190 22
                break;
191 54
            case $callback($keyword, $value):
192 52
                $matches[$pointer] = $value;
193 53
                break;
194
        }
195 27
    }
196
197 54
    return $matches;
198
}
199
200
/**
201
 * @param object $schema
202
 * @param object $resolvedRef
203
 * @param string $path
204
 *
205
 * @return object
206
 */
207
function merge_ref($schema, $resolvedRef, $path = '')
208
{
209 50
    if ($path === '') {
210 6
        pointer($schema)->remove('$ref');
211 6
        foreach ($resolvedRef as $prop => $value) {
212 4
            pointer($schema)->set($prop, $value);
213 2
        }
214 4
        return $schema;
215
    }
216
217 44
    $pointer = new Pointer($schema);
218 44
    if ($pointer->has($path)) {
219 44
        $pointer->set($path, $resolvedRef);
220 22
    }
221 44
    return $schema;
222
}
223
224
/**
225
 * Parses Content-Type header and returns an array with type, subtype, suffix and parameters
226
 *
227
 * @param string $contentType
228
 *
229
 * @return array
230
 */
231
function parseContentTypeHeader($contentType)
232
{
233 42
    preg_match('%
234
        ^
235
            (?<type>[^\/;+]+)
236
            (
237
                \/
238
                (?<subtype>[^;+]*)
239
                (?<has_suffix>\+(?<suffix>[^;]*))?
240
                ;?
241
                (?<parameter>.*)?
242
            )
243
        $
244 42
        %x', $contentType, $matches);
245
246
    $result = [
247 42
        'type' => strtolower($matches['type']),
248 42
        'subtype' => strtolower($matches['subtype']),
249 42
        'suffix' => $matches['has_suffix'] ? strtolower($matches['suffix']) : null,
250
        'parameter' => null
251 21
    ];
252
253 42
    if ($matches['parameter']) {
254 4
        $result['parameter'] = [];
255
        
256 4
        preg_match_all('/\s*([^=;]+)\s*(=([^;]*))?;?/', $matches['parameter'], $parameters, PREG_SET_ORDER);
257
258 4
        foreach ($parameters as $parameter) {
0 ignored issues
show
Bug introduced by
The expression $parameters of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
259 4
            $result['parameter'][strtolower($parameter[1])] = isset($parameter[3]) ? $parameter[3] : '';
260 2
        }
261 2
    }
262
263 42
    return $result;
264
}
265
266
/**
267
 * Determine the file type based on the given context (http-headers, uri)
268
 *
269
 * @param mixed $context
270
 *
271
 * @return string
272
 */
273
function determineMediaType($context)
274
{
275 86
    if (isset($context['headers']) && $context['headers']) {
276 16
        if (isset($context['headers']['Content-Type'])) {
277 16
            $context['Content-Type'] = $context['headers']['Content-Type'];
278 8
        }
279 8
    }
280
281 86
    $type = null;
282
283 86
    if (isset($context['Content-Type']) && $context['Content-Type']) {
284 32
        $contentType = parseContentTypeHeader($context['Content-Type']);
285
286 32
        if (isset($contentType['suffix'])) {
287 4
            return '+'.$contentType['suffix'];
288
        } else {
289 28
            $type = $contentType['type'].'/'.$contentType['subtype'];
290
        }
291 14
    }
292
293 82
    $extension = null;
294
295 82
    if (isset($context['uri']) && $context['uri']) {
296 78
        $path = Uri\parse($context['uri'])['path'];
297 78
        $info = pathinfo($path);
298
299 78
        if (isset($info['extension'])) {
300 72
            $extension = $info['extension'];
301 36
        }
302 39
    }
303
304 82
    if($type === 'application/octet-stream' && $extension) {
305 14
        return $extension;
306 69
    } else if($type) { 
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
307 15
        return $type;
308 54
    } else if($extension) {
309 46
        return $extension;
310
    } else {
311 8
        return null;
312
    }
313
}
314