GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 27531d...b2df35 )
by Jamie
03:59
created

AbstractResource::populateFromResponse()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 11
ccs 8
cts 8
cp 1
rs 9.4286
cc 3
eloc 6
nc 3
nop 1
crap 3
1
<?php
2
3
namespace OpenStack\Common\Resource;
4
5
use OpenStack\Common\Api\Operator;
6
use OpenStack\Common\Transport\Utils;
7
use Psr\Http\Message\ResponseInterface;
8
9
/**
10
 * Represents a top-level abstraction of a remote API resource. Usually a resource represents a discrete
11
 * entity such as a Server, Container, Load Balancer. Apart from a representation of state, a resource can
12
 * also execute RESTFul operations on itself (updating, deleting, listing) or on other models.
13
 *
14
 * @package OpenStack\Common\Resource
15
 */
16
abstract class AbstractResource extends Operator implements ResourceInterface
17
{
18
    const DEFAULT_MARKER_KEY = 'id';
19
20
    /**
21
     * The JSON key that indicates how the API nests singular resources. For example, when
22
     * performing a GET, it could respond with ``{"server": {"id": "12345"}}``. In this case,
23
     * "server" is the resource key, since the essential state of the server is nested inside.
24
     *
25
     * @var string
26
     */
27
    protected $resourceKey;
28
29
    /**
30
     * The key that indicates how the API nests resource collections. For example, when
31
     * performing a GET, it could respond with ``{"servers": [{}, {}]}``. In this case, "servers"
32
     * is the resources key, since the array of servers is nested inside.
33
     *
34
     * @var string
35
     */
36
    protected $resourcesKey;
37
38
    /**
39
     * Indicates which attribute of the current resource should be used for pagination markers.
40
     *
41
     * @var string
42
     */
43
    protected $markerKey;
44
45
    /**
46
     * An array of aliases that will be checked when the resource is being populated. For example,
47
     *
48
     * 'FOO_BAR' => 'fooBar'
49
     *
50
     * will extract FOO_BAR from the response, and save it as 'fooBar' in the resource.
51
     *
52
     * @var array
53
     */
54
    protected $aliases = [];
55
56
    /**
57
     * Populates the current resource from a response object.
58
     *
59
     * @param ResponseInterface $response
60
     *
61
     * @return $this|ResourceInterface
62
     */
63 66
    public function populateFromResponse(ResponseInterface $response)
64
    {
65 66
        if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) {
66 54
            $json = Utils::jsonDecode($response);
67 54
            if (!empty($json)) {
68 54
                $this->populateFromArray(Utils::flattenJson($json, $this->resourceKey));
69 54
            }
70 54
        }
71
72 66
        return $this;
73
    }
74
75
    /**
76
     * Populates the current resource from a data array.
77
     *
78
     * @param array $array
79
     *
80
     * @return mixed|void
81
     */
82 116
    public function populateFromArray(array $array)
83
    {
84 116
        $reflClass = new \ReflectionClass($this);
85
86 116
        foreach ($array as $key => $val) {
87 116
            $propertyName = isset($this->aliases[$key]) ? $this->aliases[$key] : $key;
88
89 116
            if (property_exists($this, $propertyName)) {
90 114
                if ($type = $this->extractTypeFromDocBlock($reflClass, $propertyName)) {
91 111
                    $val = $this->parseDocBlockValue($type, $val);
92 111
                }
93
94 114
                $this->$propertyName = $val;
95 114
            }
96 116
        }
97 116
    }
98
99 111
    private function parseDocBlockValue($type, $val)
100
    {
101 111
        if (strpos($type, '[]') === 0 && is_array($val)) {
102 6
            $array = [];
103 6
            foreach ($val as $subVal) {
104 6
                $array[] = $this->model($this->normalizeModelClass(substr($type, 2)), $subVal);
105 6
            }
106 6
            $val = $array;
107 111
        } elseif (strcasecmp($type, '\datetimeimmutable') === 0) {
108 20
            $val = new \DateTimeImmutable($val);
109 110
        } elseif ($this->isNotNativeType($type)) {
110 11
            $val = $this->model($this->normalizeModelClass($type), $val);
111 11
        }
112
113 111
        return $val;
114
    }
115
116 107
    private function isNotNativeType($type)
117
    {
118 107
        return !in_array($type, [
119 107
            'string', 'bool', 'boolean', 'null', 'array', 'object', 'int', 'integer', 'float', 'numeric', 'mixed'
120 107
        ]);
121
    }
122
123 12
    private function normalizeModelClass($class)
124
    {
125 12
        if (strpos($class, '\\') === false) {
126 12
            $currentNamespace = (new \ReflectionClass($this))->getNamespaceName();
127 12
            $class = sprintf("%s\\%s", $currentNamespace, $class);
128 12
        }
129
130 12
        return $class;
131
    }
132
133 114
    private function extractTypeFromDocBlock(\ReflectionClass $reflClass, $propertyName)
134
    {
135 114
        $docComment = $reflClass->getProperty($propertyName)->getDocComment();
136
137 114
        if (!$docComment) {
138 3
            return false;
139
        }
140
141 111
        $matches = [];
142 111
        preg_match('#@var ((\[\])?[\w|\\\]+)#', $docComment, $matches);
143 111
        return isset($matches[1]) ? $matches[1] : null;
144
    }
145
146
    /**
147
     * Internal method which retrieves the values of provided keys.
148
     *
149
     * @param array $keys
150
     *
151
     * @return array
152
     */
153 52
    protected function getAttrs(array $keys)
154
    {
155 52
        $output = [];
156
157 52
        foreach ($keys as $key) {
158 52
            if (property_exists($this, $key) && $this->$key !== null) {
159 52
                $output[$key] = $this->$key;
160 52
            }
161 52
        }
162
163 52
        return $output;
164
    }
165
166
    /**
167
     * @param array $definition
168
     *
169
     * @return mixed
170
     */
171 47
    public function executeWithState(array $definition)
172
    {
173 47
        return $this->execute($definition, $this->getAttrs(array_keys($definition['params'])));
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179 28
    public function enumerate(array $def, array $userVals = [], callable $mapFn = null)
180
    {
181 28
        $operation = $this->getOperation($def);
182 28
        $markerKey = $this->markerKey ?: self::DEFAULT_MARKER_KEY;
183 28
        $supportsPagination = $operation->hasParam('marker');
184
185 28
        $limit = isset($userVals['limit']) ? $userVals['limit'] : false;
186 28
        $count = 0;
187
188 28
        $totalReached = function ($count) use ($limit) {
189 28
            return $limit && $count >= $limit;
190 28
        };
191
192 28
        while (true) {
193 28
            $response = $this->sendRequest($operation, $userVals);
194 28
            $json = Utils::flattenJson(Utils::jsonDecode($response), $this->resourcesKey);
195
196 28
            if ($response->getStatusCode() === 204 || !$json) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $json of type array 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...
197 4
                break;
198
            }
199
200 28
            foreach ($json as $resourceData) {
201 28
                if ($totalReached($count)) {
202 2
                    break;
203
                }
204
205 28
                $count++;
206
207 28
                $resource = $this->newInstance();
208 28
                $resource->populateFromArray($resourceData);
209
210 28
                if ($mapFn) {
211 1
                    call_user_func_array($mapFn, [$resource]);
212 1
                }
213
214 28
                if ($supportsPagination) {
215 10
                    $userVals['marker'] = $resource->$markerKey;
216 10
                }
217
218 28
                yield $resource;
219 28
            }
220
221 28
            if ($totalReached($count) || !$supportsPagination) {
222 24
                break;
223
            }
224 4
        }
225 28
    }
226
}
227