AbstractRequest::__serialize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

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
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 array  $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
 */
43
abstract class AbstractRequest implements RequestInterface, ArrayAccess, IteratorAggregate, Serializable, JsonSerializable, Stringable
44
{
45
    /**
46
     * URI
47
     *
48
     * @var string
49
     */
50
    public $uri;
51
52
    /**
53
     * Method
54
     *
55
     * @var string
56
     */
57
    public $method = '';
58
59
    /**
60
     * Options
61
     *
62
     * @var array<mixed>
63
     */
64
    public $options = [];
65
66
    /**
67
     * Request option (eager or lazy)
68
     *
69
     * @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...
70
     */
71
    public $in = 'lazy';
72
73
    /**
74
     * Request Result
75
     *
76
     * @var ?ResourceObject
77
     */
78
    protected $result;
79
80
    /**
81
     * @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...
82
     * @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...
83
     *
84
     * @throws MethodException
85
     */
86
    public function __construct(
87
        protected InvokerInterface $invoker,
88
        public ResourceObject $resourceObject,
89
        string $method = Request::GET,
90
        public array $query = [],
91
        // phpcs:ignore SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingAnyTypeHint
92
        public array $links = [],
93
        private readonly LinkerInterface|null $linker = null,
94
    ) {
95
        if (! in_array(strtolower($method), ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'], true)) {
96
            throw new MethodException($method, 400);
97
        }
98
99
        $this->method = $method;
100
    }
101
102
    /** @psalm-suppress UnevaluatedCode */
103
    #[Override]
104
    public function __toString(): string
105
    {
106
        try {
107
            $this->invoke();
108
109
            return (string) $this->result;
110
        } catch (Throwable $e) {
111
            trigger_error($e->getMessage() . PHP_EOL . $e->getTraceAsString(), E_USER_ERROR);
112
113
            /** @noinspection PhpUnreachableStatementInspection */
114
            return ''; // @phpstan-ignore-line
115
        }
116
    }
117
118
    /**
119
     * {@inheritDoc}
120
     *
121
     * @param Query $query
122
     */
123
    #[Override]
124
    public function __invoke(array|null $query = null): ResourceObject
125
    {
126
        if (is_array($query)) {
127
            $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...
128
        }
129
130
        $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...
131
        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...
132
            return $this->linker->invoke($this);
133
        }
134
135
        return clone $this->invoker->invoke($this);
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     *
141
     * @return mixed
142
     *
143
     * @noinspection MagicMethodsValidityInspection
144
     */
145
    public function __get(string $name)
146
    {
147
        $this->result = $this->invoke();
148
149
        return $this->result->{$name};
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     *
155
     * @throws OutOfBoundsException
156
     */
157
    #[Override]
158
    #[ReturnTypeWillChange]
159
    public function offsetSet($offset, $value)
160
    {
161
        throw new OutOfBoundsException(__METHOD__ . ' is unavailable.', 400);
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     *
167
     * @param string $offset
168
     *
169
     * @return never
170
     *
171
     * @throws OutOfBoundsException
172
     */
173
    #[Override]
174
    #[ReturnTypeWillChange]
175
    public function offsetUnset($offset)
176
    {
177
        unset($offset);
178
179
        throw new OutOfBoundsException(__METHOD__ . ' is unavailable.', 400);
180
    }
181
182
    /**
183
     * {@inheritDoc}
184
     */
185
    #[Override]
186
    public function request()
187
    {
188
        if ($this->in === 'eager') {
189
            $this->result = $this->invoke();
190
191
            return $this->result;
192
        }
193
194
        return $this;
195
    }
196
197
    /**
198
     * {@inheritDoc}
199
     *
200
     * @param string $offset
201
     *
202
     * @return mixed
203
     *
204
     * @throws OutOfBoundsException
205
     */
206
    #[Override]
207
    #[ReturnTypeWillChange]
208
    public function offsetGet($offset)
209
    {
210
        $this->invoke();
211
        assert($this->result instanceof ResourceObject);
212
        if (! is_array($this->result->body) || ! array_key_exists($offset, $this->result->body)) {
213
            throw new OutOfBoundsException("[{$offset}] for object[" . $this->result::class . ']', 400);
214
        }
215
216
        return $this->result->body[$offset];
217
    }
218
219
    /**
220
     * {@inheritDoc}
221
     */
222
    #[Override]
223
    #[ReturnTypeWillChange]
224
    public function offsetExists($offset): bool
225
    {
226
        $this->invoke();
227
        assert($this->result instanceof ResourceObject);
228
229
        return is_array($this->result->body) && array_key_exists($offset, $this->result->body);
230
    }
231
232
    /**
233
     * Invoke resource request then return resource body iterator
234
     *
235
     * @psalm-return ArrayIterator
236
     * @phpstan-return ArrayIterator<string, mixed>
237
     */
238
    #[Override]
239
    public function getIterator(): ArrayIterator
240
    {
241
        $this->invoke();
242
        assert($this->result instanceof ResourceObject);
243
244
        return is_array($this->result->body) ? new ArrayIterator($this->result->body) : new ArrayIterator([]);
245
    }
246
247
    /**
248
     * {@inheritDoc}
249
     */
250
    #[Override]
251
    public function hash(): string
252
    {
253
        return md5($this->resourceObject::class . $this->method . serialize($this->query) . serialize($this->links));
254
    }
255
256
    /**
257
     * {@inheritDoc}
258
     *
259
     * @return never
260
     *
261
     * @noinspection MagicMethodsValidityInspection
262
     */
263
    public function __serialize()
264
    {
265
        throw new LogicException(__METHOD__ . ' not supported');
266
    }
267
268
    /**
269
     * @param array<mixed> $data
270
     *
271
     * @codeCoverageIgnore
272
     */
273
    public function __unserialize(array $data): void
274
    {
275
        unset($data);
276
    }
277
278
    private function invoke(): ResourceObject
279
    {
280
        if ($this->result === null) {
281
            /* @noinspection ImplicitMagicMethodCallInspection */
282
            $this->result = ($this)();
283
        }
284
285
        return $this->result;
286
    }
287
288
    #[Override]
289
    public function jsonSerialize(): ResourceObject
290
    {
291
        return $this->invoke();
292
    }
293
294
    /**
295
     * @return never
296
     *
297
     * @codeCoverageIgnore
298
     * @psalm-suppress MethodSignatureMustProvideReturnType - method not supported
299
     */
300
    #[Override]
301
    public function serialize()
302
    {
303
        $this->__serialize();
304
    }
305
306
    /**
307
     * @param string $data
308
     *
309
     * @codeCoverageIgnore
310
     * @psalm-suppress MethodSignatureMustProvideReturnType - method not supported
311
     */
312
    #[Override]
313
    public function unserialize($data)
314
    {
315
        unset($data);
316
    }
317
}
318