JsonLocation::visit()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 16
cts 16
cp 1
rs 9.1608
c 0
b 0
f 0
cc 5
nc 5
nop 3
crap 5
1
<?php
2
namespace GuzzleHttp\Command\Guzzle\ResponseLocation;
3
4
use GuzzleHttp\Command\Guzzle\Parameter;
5
use GuzzleHttp\Command\Result;
6
use GuzzleHttp\Command\ResultInterface;
7
use Psr\Http\Message\ResponseInterface;
8
9
/**
10
 * Extracts elements from a JSON document.
11
 */
12
class JsonLocation extends AbstractLocation
13
{
14
    /** @var array The JSON document being visited */
15
    private $json = [];
16
17
    /**
18
     * Set the name of the location
19
     *
20
     * @param string $locationName
21
     */
22 3
    public function __construct($locationName = 'json')
23
    {
24 3
        parent::__construct($locationName);
25 3
    }
26
27
    /**
28
     * @param \GuzzleHttp\Command\ResultInterface  $result
29
     * @param \Psr\Http\Message\ResponseInterface  $response
30
     * @param \GuzzleHttp\Command\Guzzle\Parameter $model
31
     *
32
     * @return \GuzzleHttp\Command\ResultInterface
33
     */
34 12
    public function before(
35
        ResultInterface $result,
36
        ResponseInterface $response,
37
        Parameter $model
38
    ) {
39 12
        $body = (string) $response->getBody();
40 12
        $body = $body ?: "{}";
41 12
        $this->json = \GuzzleHttp\json_decode($body, true);
42
        // relocate named arrays, so that they have the same structure as
43
        //  arrays nested in objects and visit can work on them in the same way
44 12
        if ($model->getType() === 'array' && ($name = $model->getName())) {
0 ignored issues
show
Bug introduced by
Consider using $model->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
45 1
            $this->json = [$name => $this->json];
46 1
        }
47
48 12
        return $result;
49
    }
50
51
    /**
52
     * @param ResultInterface $result
53
     * @param ResponseInterface $response
54
     * @param Parameter $model
55
     * @return ResultInterface
56
     */
57 11
    public function after(
58
        ResultInterface $result,
59
        ResponseInterface $response,
60
        Parameter $model
61
    ) {
62
        // Handle additional, undefined properties
63 11
        $additional = $model->getAdditionalProperties();
64 11
        if (!($additional instanceof Parameter)) {
65 7
            return $result;
66
        }
67
68
        // Use the model location as the default if one is not set on additional
69 4
        $addLocation = $additional->getLocation() ?: $model->getLocation();
70 4
        if ($addLocation == $this->locationName) {
71 4
            foreach ($this->json as $prop => $val) {
72 3
                if (!isset($result[$prop])) {
73
                    // Only recurse if there is a type specified
74 3
                    $result[$prop] = $additional->getType()
75 3
                        ? $this->recurse($additional, $val)
76 2
                        : $val;
77 3
                }
78 4
            }
79 4
        }
80
81 4
        $this->json = [];
82
83 4
        return $result;
84
    }
85
86
    /**
87
     * @param ResultInterface $result
88
     * @param ResponseInterface $response
89
     * @param Parameter $param
90
     * @return Result|ResultInterface
91
     */
92 12
    public function visit(
93
        ResultInterface $result,
94
        ResponseInterface $response,
95
        Parameter $param
96
    ) {
97 12
        $name = $param->getName();
0 ignored issues
show
Bug introduced by
Consider using $param->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
98 12
        $key = $param->getWireName();
99
100
        // Check if the result should be treated as a list
101 12
        if ($param->getType() == 'array') {
102
            // Treat as javascript array
103 6
            if ($name) {
104
                // name provided, store it under a key in the array
105 5
                $subArray = isset($this->json[$key]) ? $this->json[$key] : null;
106 5
                $result[$name] = $this->recurse($param, $subArray);
107 5
            } else {
108
                // top-level `array` or an empty name
109 1
                $result = new Result(array_merge(
0 ignored issues
show
Bug Compatibility introduced by
The expression new \GuzzleHttp\Command\...$param, $this->json))); of type GuzzleHttp\Command\Result adds the type GuzzleHttp\Command\Result to the return on line 118 which is incompatible with the return type declared by the interface GuzzleHttp\Command\Guzzl...ocationInterface::visit of type GuzzleHttp\Command\ResultInterface.
Loading history...
110 1
                    $result->toArray(),
111 1
                    $this->recurse($param, $this->json)
112 1
                ));
113
            }
114 12
        } elseif (isset($this->json[$key])) {
115 7
            $result[$name] = $this->recurse($param, $this->json[$key]);
116 7
        }
117
118 12
        return $result;
119
    }
120
121
    /**
122
     * Recursively process a parameter while applying filters
123
     *
124
     * @param Parameter $param API parameter being validated
125
     * @param mixed     $value Value to process.
126
     * @return mixed|null
127
     */
128 10
    private function recurse(Parameter $param, $value)
129
    {
130 10
        if (!is_array($value)) {
131 10
            return $param->filter($value);
132
        }
133
134 9
        $result = [];
135 9
        $type = $param->getType();
136
137 9
        if ($type == 'array') {
138 6
            $items = $param->getItems();
139 6
            foreach ($value as $val) {
140 6
                $result[] = $this->recurse($items, $val);
141 6
            }
142 9
        } elseif ($type == 'object' && !isset($value[0])) {
143
            // On the above line, we ensure that the array is associative and
144
            // not numerically indexed
145 7
            if ($properties = $param->getProperties()) {
146 5
                foreach ($properties as $property) {
147 5
                    $key = $property->getWireName();
148 5
                    if (array_key_exists($key, $value)) {
149 5
                        $result[$property->getName()] = $this->recurse(
0 ignored issues
show
Bug introduced by
Consider using $property->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
150 5
                            $property,
151 5
                            $value[$key]
152 5
                        );
153
                        // Remove from the value so that AP can later be handled
154 5
                        unset($value[$key]);
155 5
                    }
156 5
                }
157 5
            }
158
            // Only check additional properties if everything wasn't already
159
            // handled
160 7
            if ($value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type null[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
161 2
                $additional = $param->getAdditionalProperties();
162 2
                if ($additional === null || $additional === true) {
163
                    // Merge the JSON under the resulting array
164
                    $result += $value;
165 2
                } elseif ($additional instanceof Parameter) {
166
                    // Process all child elements according to the given schema
167 2
                    foreach ($value as $prop => $val) {
168 2
                        $result[$prop] = $this->recurse($additional, $val);
169 2
                    }
170 2
                }
171 2
            }
172 7
        }
173
174 9
        return $param->filter($result);
175
    }
176
}
177