Completed
Pull Request — master (#168)
by
unknown
01:57
created

JsonLocation::visit()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 5.0164

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 21
cts 23
cp 0.913
rs 8.9848
c 0
b 0
f 0
cc 5
nc 8
nop 3
crap 5.0164
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 18
    public function before(
35
        ResultInterface $result,
36
        ResponseInterface $response,
37
        Parameter $model
38
    ) {
39 18
        $body = (string) $response->getBody();
40 18
        $body = $body ?: "{}";
41 18
        $this->json = \GuzzleHttp\json_decode($body, true);
0 ignored issues
show
Documentation Bug introduced by
It seems like \GuzzleHttp\json_decode($body, true) of type * is incompatible with the declared type array of property $json.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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 18
        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 18
        return $result;
49
    }
50
51
    /**
52
     * @param ResultInterface $result
53
     * @param ResponseInterface $response
54
     * @param Parameter $model
55
     * @return ResultInterface
56
     */
57 17
    public function after(
58
        ResultInterface $result,
59
        ResponseInterface $response,
60
        Parameter $model
61
    ) {
62
        // Handle additional, undefined properties
63 17
        $additional = $model->getAdditionalProperties();
64 17
        if (!($additional instanceof Parameter)) {
65 13
            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 18
    public function visit(
93
        ResultInterface $result,
94
        ResponseInterface $response,
95
        Parameter $param
96
    ) {
97 18
        $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 18
        $key = $param->getWireName();
99
100 18
        $wholeValue = $this->json;
101
102 18
        if (array_key_exists($key, $wholeValue)) {
103 15
            $haveNestedElement = true;
104 15
            $nestedElement     = $wholeValue[$key];
105 15
        } else {
106 4
            $haveNestedElement = false;
107 4
            $nestedElement     = null;
108
        }
109
110 18
        $valueType = $param->determineType($wholeValue);
111
112
        // Check if the result should be treated as a list
113 18
        if ($valueType === 'array') {
114
            // Treat as javascript array
115 1
            if (!empty($name)) {
116
                // name provided, store it under a key in the array
117
                $result[$name] = $this->recurse($param, $nestedElement);
118
            } else {
119
                // top-level `array` or an empty name
120 1
                $result = new Result(array_merge(
0 ignored issues
show
Bug Compatibility introduced by
The expression new \GuzzleHttp\Command\...$param, $wholeValue))); of type GuzzleHttp\Command\Result adds the type GuzzleHttp\Command\Result to the return on line 129 which is incompatible with the return type declared by the interface GuzzleHttp\Command\Guzzl...ocationInterface::visit of type GuzzleHttp\Command\ResultInterface.
Loading history...
121 1
                    $result->toArray(),
122 1
                    $this->recurse($param, $wholeValue)
123 1
                ));
124
            }
125 18
        } elseif ($haveNestedElement) {
126 15
            $result[$name] = $this->recurse($param, $nestedElement);
127 15
        }
128
129 18
        return $result;
130
    }
131
132
    /**
133
     * Recursively process a parameter while applying filters
134
     *
135
     * @param Parameter $param API parameter being validated
136
     * @param mixed     $value Value to process.
137
     * @return mixed|null
138
     */
139 16
    private function recurse(Parameter $param, $value)
140
    {
141 16
        if (!is_array($value)) {
142 14
            return $param->filter($value);
143
        }
144
145 13
        $result = [];
146 13
        $type = $param->determineType($value);
147
148 13
        if ($type == 'array') {
149 8
            $items = $param->getItems();
150 8
            foreach ($value as $val) {
151 8
                $result[] = $this->recurse($items, $val);
152 8
            }
153 13
        } elseif ($type == 'object' && !isset($value[0])) {
154
            // On the above line, we ensure that the array is associative and
155
            // not numerically indexed
156 9
            if ($properties = $param->getProperties()) {
157 5
                foreach ($properties as $property) {
158 5
                    $key = $property->getWireName();
159 5
                    if (array_key_exists($key, $value)) {
160 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...
161 5
                            $property,
162 5
                            $value[$key]
163 5
                        );
164
                        // Remove from the value so that AP can later be handled
165 5
                        unset($value[$key]);
166 5
                    }
167 5
                }
168 5
            }
169
            // Only check additional properties if everything wasn't already
170
            // handled
171 9
            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...
172 4
                $additional = $param->getAdditionalProperties();
173 4
                if ($additional === null || $additional === true) {
174
                    // Merge the JSON under the resulting array
175 2
                    $result += $value;
176 4
                } elseif ($additional instanceof Parameter) {
177
                    // Process all child elements according to the given schema
178 2
                    foreach ($value as $prop => $val) {
179 2
                        $result[$prop] = $this->recurse($additional, $val);
180 2
                    }
181 2
                }
182 4
            }
183 9
        }
184
185 13
        return $param->filter($result);
186
    }
187
}
188