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.
Completed
Pull Request — master (#65)
by Ha
04:16
created

AbstractResource::extractTypeFromDocBlock()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
crap 3
1
<?php declare (strict_types=1);
2
3
namespace OpenStack\Common\Resource;
4
5
use OpenStack\Common\Transport\Serializable;
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 implements ResourceInterface, Serializable
17
{
18
    /**
19
     * The JSON key that indicates how the API nests singular resources. For example, when
20
     * performing a GET, it could respond with ``{"server": {"id": "12345"}}``. In this case,
21
     * "server" is the resource key, since the essential state of the server is nested inside.
22
     *
23
     * @var string
24
     */
25
    protected $resourceKey;
26
27
    /**
28
     * An array of aliases that will be checked when the resource is being populated. For example,
29
     *
30
     * 'FOO_BAR' => 'fooBar'
31
     *
32
     * will extract FOO_BAR from the response, and save it as 'fooBar' in the resource.
33
     *
34
     * @var array
35
     */
36
    protected $aliases = [];
37
38
    /**
39
     * Populates the current resource from a response object.
40
     *
41
     * @param ResponseInterface $response
42
     *
43
     * @return AbstractResource
44
     */
45
    public function populateFromResponse(ResponseInterface $response): self
46
    {
47
        if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) {
48
            $json = Utils::jsonDecode($response);
49
            if (!empty($json)) {
50
                $this->populateFromArray(Utils::flattenJson($json, $this->resourceKey));
51
            }
52
        }
53
54
        return $this;
55
    }
56
57
    /**
58
     * Populates the current resource from a data array.
59
     *
60
     * @param array $array
61
     *
62
     * @return mixed|void
63 74
     */
64
    public function populateFromArray(array $array): self
65 74
    {
66 61
        $reflClass = new \ReflectionClass($this);
67 61
68 61
        foreach ($array as $key => $val) {
69 61
            $propertyName = (string) (isset($this->aliases[$key]) ? $this->aliases[$key] : $key);
70 61
71
            if (property_exists($this, $propertyName)) {
72 74
                if ($type = $this->extractTypeFromDocBlock($reflClass, $propertyName)) {
73
                    $val = $this->parseDocBlockValue($type, $val);
74
                }
75
76
                $this->$propertyName = $val;
77
            }
78
        }
79
80
        return $this;
81
    }
82 133
83
    private function parseDocBlockValue(string $type, $val)
84 133
    {
85
        if (is_null($val)) {
86 133
            return $val;
87 133
        } elseif (strpos($type, '[]') === 0 && is_array($val)) {
88
            $array = [];
89 133
            foreach ($val as $subVal) {
90 131
                $array[] = $this->model($this->normalizeModelClass(substr($type, 2)), $subVal);
91 128
            }
92 128
            $val = $array;
93
        } elseif (strcasecmp($type, '\datetimeimmutable') === 0) {
94 131
            $val = new \DateTimeImmutable($val);
95 131
        } elseif ($this->isNotNativeType($type)) {
96 133
            $val = $this->model($this->normalizeModelClass($type), $val);
97 133
        }
98
99 128
        return $val;
100 2
    }
101 128
102 6
    private function isNotNativeType(string $type): bool
103 6
    {
104 6
        return !in_array($type, [
105 6
            'string', 'bool', 'boolean', 'double', 'null', 'array', 'object', 'int', 'integer', 'float', 'numeric',
106 6
            'mixed'
107 128
        ]);
108 28
    }
109 127
110 11
    private function normalizeModelClass(string $class): string
111 11
    {
112
        if (strpos($class, '\\') === false) {
113 128
            $currentNamespace = (new \ReflectionClass($this))->getNamespaceName();
114
            $class = sprintf("%s\\%s", $currentNamespace, $class);
115
        }
116 124
117
        return $class;
118 124
    }
119 124
120 124
    private function extractTypeFromDocBlock(\ReflectionClass $reflClass, string $propertyName)
121
    {
122
        $docComment = $reflClass->getProperty($propertyName)->getDocComment();
123 12
124
        if (!$docComment) {
125 12
            return false;
126 12
        }
127 12
128 12
        $matches = [];
129
        preg_match('#@var ((\[\])?[\w|\\\]+)#', $docComment, $matches);
130 12
        return isset($matches[1]) ? $matches[1] : null;
131
    }
132
133 131
    /**
134
     * Internal method which retrieves the values of provided keys.
135 131
     *
136
     * @param array $keys
137 131
     *
138 3
     * @return array
139
     */
140
    protected function getAttrs(array $keys)
141 128
    {
142 128
        $output = [];
143 128
144
        foreach ($keys as $key) {
145
            if (property_exists($this, $key) && $this->$key !== null) {
146
                $output[$key] = $this->$key;
147
            }
148
        }
149
150
        return $output;
151
    }
152
153 65 View Code Duplication
    public function model(string $class, $data = null): ResourceInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
154
    {
155 65
        $model = new $class();
156
157 65
        // @codeCoverageIgnoreStart
158 65
        if (!$model instanceof ResourceInterface) {
159 65
            throw new \RuntimeException(sprintf('%s does not implement %s', $class, ResourceInterface::class));
160 65
        }
161 65
        // @codeCoverageIgnoreEnd
162
163 65
        if ($data instanceof ResponseInterface) {
164
            $model->populateFromResponse($data);
165
        } elseif (is_array($data)) {
166
            $model->populateFromArray($data);
167
        }
168
169
        return $model;
170
    }
171 60
172
    public function serialize(): \stdClass
173 60
    {
174
        $output = new \stdClass();
175
176 38
        foreach ((new \ReflectionClass($this))->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
177
            $name = $property->getName();
178 38
            $val = $this->{$name};
179
180 38
            $fn = function ($val) {
181 9
                return ($val instanceof Serializable) ? $val->serialize() : $val;
182 9
            };
183 9
184
            if (is_array($val)) {
185 38
                foreach ($val as $sk => $sv) {
186
                    $val[$sk] = $fn($sv);
187
                }
188
            }
189
190
            $output->{$name} = $fn($val);
191 33
        }
192
193 33
        return $output;
194
    }
195
}
196