AbstractRequest::__serialize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use ArrayAccess;
8
use ArrayIterator;
9
use BEAR\Resource\Exception\MethodException;
10
use BEAR\Resource\Exception\OutOfBoundsException;
11
use IteratorAggregate;
12
use JsonSerializable;
13
use LogicException;
14
use Override;
15
use ReturnTypeWillChange;
0 ignored issues
show
Bug introduced by
The type ReturnTypeWillChange was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Serializable;
17
use Stringable;
18
use Throwable;
19
20
use function array_key_exists;
21
use function array_merge;
22
use function assert;
23
use function in_array;
24
use function is_array;
25
use function md5;
26
use function serialize;
27
use function strtolower;
28
use function trigger_error;
29
30
use const E_USER_ERROR;
31
use const PHP_EOL;
32
33
/**
34
 * @property int     $code
35
 * @property Headers $headers
36
 * @property mixed   $body
37
 * @property string  $view
38
 * @phpstan-implements IteratorAggregate<string, mixed>
39
 * @phpstan-implements ArrayAccess<string, mixed>
40
 * @psalm-suppress PropertyNotSetInConstructor
41
 * @psalm-import-type Query from Types
42
 * @psalm-import-type Headers from Types
43
 * @psalm-import-type Body from Types
44
 */
45
abstract class AbstractRequest implements RequestInterface, ArrayAccess, IteratorAggregate, Serializable, JsonSerializable, Stringable
46
{
47
    /**
48
     * URI
49
     *
50
     * @var string
51
     */
52
    public $uri;
53
54
    /**
55
     * Method
56
     *
57
     * @var string
58
     */
59
    public $method = '';
60
61
    /**
62
     * Options
63
     *
64
     * @var Body
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\Body was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
65
     */
66
    public $options = [];
67
68
    /**
69
     * Request option (eager or lazy)
70
     *
71
     * @var 'eager'|'lazy'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'eager'|'lazy' at position 0 could not be parsed: Unknown type name ''eager'' at position 0 in 'eager'|'lazy'.
Loading history...
72
     */
73
    public $in = 'lazy';
74
75
    /**
76
     * Request Result
77
     *
78
     * @var ?ResourceObject
79
     */
80
    protected $result;
81
82
    /**
83
     * @param Query          $query
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\Query was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
84
     * @param list<LinkType> $links
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
85
     *
86
     * @throws MethodException
87
     */
88
    public function __construct(
89
        protected InvokerInterface $invoker,
90
        public ResourceObject $resourceObject,
91
        string $method = Request::GET,
92
        public array $query = [],
93
        // phpcs:ignore SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingAnyTypeHint
94
        public array $links = [],
95
        private readonly LinkerInterface|null $linker = null,
96
    ) {
97
        if (! in_array(strtolower($method), ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'], true)) {
98
            throw new MethodException($method, 400);
99 91
        }
100
101
        $this->method = $method;
102
    }
103
104
    /** @psalm-suppress UnevaluatedCode */
105
    #[Override]
106
    public function __toString(): string
107 91
    {
108 91
        try {
109 91
            $this->invoke();
110 1
111
            return (string) $this->result;
112 91
        } catch (Throwable $e) {
113 91
            trigger_error($e->getMessage() . PHP_EOL . $e->getTraceAsString(), E_USER_ERROR);
114 91
115 91
            /** @noinspection PhpUnreachableStatementInspection */
116 91
            return ''; // @phpstan-ignore-line
117
        }
118 6
    }
119
120
    /**
121 6
     * {@inheritDoc}
122
     *
123 5
     * @param Query $query
124 1
     */
125 1
    #[Override]
126
    public function __invoke(array|null $query = null): ResourceObject
127 1
    {
128
        if (is_array($query)) {
129
            $this->query = array_merge($this->query, $query);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->query, $query) of type array is incompatible with the declared type BEAR\Resource\Query of property $query.

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...
130
        }
131
132
        $this->resourceObject->uri->query = $this->query;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->query of type array is incompatible with the declared type BEAR\Resource\Query of property $query.

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...
133
        if ($this->links && $this->linker instanceof LinkerInterface) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->links 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...
134 56
            return $this->linker->invoke($this);
135
        }
136 56
137 15
        return clone $this->invoker->invoke($this);
138
    }
139 56
140 56
    /**
141 3
     * {@inheritDoc}
142
     *
143
     * @return mixed
144 53
     *
145
     * @noinspection MagicMethodsValidityInspection
146
     */
147
    public function __get(string $name)
148
    {
149
        $this->result = $this->invoke();
150 3
151
        return $this->result->{$name};
152 3
    }
153
154 3
    /**
155
     * {@inheritDoc}
156
     *
157
     * @throws OutOfBoundsException
158
     */
159
    #[Override]
160
    #[ReturnTypeWillChange]
161
    public function offsetSet($offset, $value)
162 1
    {
163
        throw new OutOfBoundsException(__METHOD__ . ' is unavailable.', 400);
164 1
    }
165
166
    /**
167
     * {@inheritDoc}
168
     *
169
     * @param string $offset
170
     *
171
     * @return never
172 1
     *
173
     * @throws OutOfBoundsException
174 1
     */
175
    #[Override]
176 1
    #[ReturnTypeWillChange]
177
    public function offsetUnset($offset)
178
    {
179
        unset($offset);
180
181
        throw new OutOfBoundsException(__METHOD__ . ' is unavailable.', 400);
182 26
    }
183
184 26
    /**
185 19
     * {@inheritDoc}
186
     */
187 17
    #[Override]
188
    public function request()
189
    {
190 8
        if ($this->in === 'eager') {
191
            $this->result = $this->invoke();
192
193
            return $this->result;
194
        }
195
196
        return $this;
197
    }
198 2
199
    /**
200 2
     * {@inheritDoc}
201 2
     *
202 1
     * @param string $offset
203
     *
204
     * @return mixed
205
     *
206
     * @throws OutOfBoundsException
207
     */
208
    #[Override]
209
    #[ReturnTypeWillChange]
210
    public function offsetGet($offset)
211
    {
212
        $this->invoke();
213 2
        assert($this->result instanceof ResourceObject);
214
        if (! is_array($this->result->body) || ! array_key_exists($offset, $this->result->body)) {
215 2
            throw new OutOfBoundsException("[{$offset}] for object[" . $this->result::class . ']', 400);
216
        }
217
218
        return $this->result->body[$offset];
219
    }
220
221
    /**
222
     * {@inheritDoc}
223 1
     */
224 1
    #[Override]
225
    #[ReturnTypeWillChange]
226 1
    public function offsetExists($offset): bool
227
    {
228
        $this->invoke();
229
        assert($this->result instanceof ResourceObject);
230
231
        return is_array($this->result->body) && array_key_exists($offset, $this->result->body);
232
    }
233
234 5
    /**
235
     * Invoke resource request then return resource body iterator
236
     *
237
     * @psalm-return ArrayIterator
238
     * @phpstan-return ArrayIterator<string, mixed>
239
     */
240
    #[Override]
241
    public function getIterator(): ArrayIterator
242 1
    {
243
        $this->invoke();
244
        assert($this->result instanceof ResourceObject);
245
246
        return is_array($this->result->body) ? new ArrayIterator($this->result->body) : new ArrayIterator([]);
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252
    #[Override]
253
    public function hash(): string
254
    {
255 33
        return md5($this->resourceObject::class . $this->method . serialize($this->query) . serialize($this->links));
256
    }
257 32
258
    /**
259
     * {@inheritDoc}
260 30
     *
261
     * @return never
262
     *
263
     * @noinspection MagicMethodsValidityInspection
264
     */
265
    public function __serialize()
266
    {
267
        throw new LogicException(__METHOD__ . ' not supported');
268
    }
269
270
    /**
271
     * @param Body $data
272
     *
273
     * @codeCoverageIgnore
274
     */
275
    public function __unserialize(array $data): void
276
    {
277
        unset($data);
278
    }
279
280
    private function invoke(): ResourceObject
281
    {
282
        if ($this->result === null) {
283
            /* @noinspection ImplicitMagicMethodCallInspection */
284
            $this->result = ($this)();
285
        }
286
287
        return $this->result;
288
    }
289
290
    #[Override]
291
    public function jsonSerialize(): ResourceObject
292
    {
293
        return $this->invoke();
294
    }
295
296
    /**
297
     * @return never
298
     *
299
     * @codeCoverageIgnore
300
     * @psalm-suppress MethodSignatureMustProvideReturnType - method not supported
301
     */
302
    #[Override]
303
    public function serialize()
304
    {
305
        $this->__serialize();
306
    }
307
308
    /**
309
     * @param string $data
310
     *
311
     * @codeCoverageIgnore
312
     * @psalm-suppress MethodSignatureMustProvideReturnType - method not supported
313
     */
314
    #[Override]
315
    public function unserialize($data)
316
    {
317
        unset($data);
318
    }
319
}
320