Issues (49)

src/AbstractRequest.php (1 issue)

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