Completed
Push — master ( d8465d...85c396 )
by Stefano
03:17
created

JsonLocation   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 98.72%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 2
dl 0
loc 165
ccs 77
cts 78
cp 0.9872
rs 9.8
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A before() 0 16 4
C after() 0 28 7
B visit() 0 28 5
C recurse() 0 48 14
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 9
    public function before(
35
        ResultInterface $result,
36
        ResponseInterface $response,
37
        Parameter $model
38
    ) {
39 9
        $body = (string) $response->getBody();
40 9
        $body = $body ?: "{}";
41 9
        $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 9
        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 9
        return $result;
49
    }
50
51
    /**
52
     * @param ResultInterface $result
53
     * @param ResponseInterface $response
54
     * @param Parameter $model
55
     * @return ResultInterface
56
     */
57 8
    public function after(
58
        ResultInterface $result,
59
        ResponseInterface $response,
60
        Parameter $model
61
    ) {
62
        // Handle additional, undefined properties
63 8
        $additional = $model->getAdditionalProperties();
64 8
        if (!($additional instanceof Parameter)) {
65 4
            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 9
    public function visit(
93
        ResultInterface $result,
94
        ResponseInterface $response,
95
        Parameter $param
96
    ) {
97 9
        $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 9
        $key = $param->getWireName();
99
100
        // Check if the result should be treated as a list
101 9
        if ($param->getType() == 'array') {
102
            // Treat as javascript array
103 3
            if ($name) {
104
                // name provided, store it under a key in the array
105 2
                $subArray = isset($this->json[$name]) ? $this->json[$name] : null;
106 2
                $result[$name] = $this->recurse($param, $subArray);
107 2
            } 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 9
        } elseif (isset($this->json[$key])) {
115 5
            $result[$name] = $this->recurse($param, $this->json[$key]);
116 5
        }
117
118 9
        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 7
    private function recurse(Parameter $param, $value)
129
    {
130 7
        if (!is_array($value)) {
131 7
            return $param->filter($value);
132
        }
133
134 6
        $result = [];
135 6
        $type = $param->getType();
136
137 6
        if ($type == 'array') {
138 3
            $items = $param->getItems();
139 3
            foreach ($value as $val) {
140 3
                $result[] = $this->recurse($items, $val);
141 3
            }
142 6
        } elseif ($type == 'object' && !isset($value[0])) {
143
            // On the above line, we ensure that the array is associative and
144
            // not numerically indexed
145 5
            if ($properties = $param->getProperties()) {
146 3
                foreach ($properties as $property) {
147 3
                    $key = $property->getWireName();
148 3
                    if (array_key_exists($key, $value)) {
149 3
                        $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 3
                            $property,
151 3
                            $value[$key]
152 3
                        );
153
                        // Remove from the value so that AP can later be handled
154 3
                        unset($value[$key]);
155 3
                    }
156 3
                }
157 3
            }
158
            // Only check additional properties if everything wasn't already
159
            // handled
160 5
            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 5
        }
173
174 6
        return $param->filter($result);
175
    }
176
}
177