1 | <?php |
||
2 | |||
3 | namespace Helix\Site; |
||
4 | |||
5 | use ArrayAccess; |
||
6 | use LogicException; |
||
7 | |||
8 | /** |
||
9 | * The request. |
||
10 | */ |
||
11 | class Request implements ArrayAccess |
||
12 | { |
||
13 | |||
14 | /** |
||
15 | * Grouped file uploads (multiple). |
||
16 | * |
||
17 | * `[ name => Upload[] ]` |
||
18 | * |
||
19 | * @var Upload[][] |
||
20 | */ |
||
21 | protected $fileGroups = []; |
||
22 | |||
23 | /** |
||
24 | * File uploads (singular). |
||
25 | * |
||
26 | * `[ name => Upload ]` |
||
27 | * |
||
28 | * @var Upload[] |
||
29 | */ |
||
30 | protected $files = []; |
||
31 | |||
32 | /** |
||
33 | * Request headers, keyed in lowercase. |
||
34 | * |
||
35 | * @var string[] |
||
36 | */ |
||
37 | protected $headers = []; |
||
38 | |||
39 | /** |
||
40 | * The request path, without arguments, cleaned up. |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $path; |
||
45 | |||
46 | /** |
||
47 | * Trust client IP forwarding from these proxies. |
||
48 | * |
||
49 | * @var string[] |
||
50 | */ |
||
51 | protected $proxies = []; |
||
52 | |||
53 | /** |
||
54 | * Constructs using CGI data. |
||
55 | */ |
||
56 | public function __construct() |
||
57 | { |
||
58 | $this->path = Util::path(urldecode(strtok($_SERVER['REQUEST_URI'], '?'))); |
||
59 | $this->headers = array_change_key_case(getallheaders()); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
60 | foreach ($_FILES as $name => $file) { |
||
61 | if (is_array($file['name'])) { |
||
62 | // php makes file groups an inside-out table. unwrap it. |
||
63 | for ($i = 0; $i < count($file['name']); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
64 | $this->fileGroups[$name][$i] = new Upload( |
||
65 | $file['error_code'][$i], |
||
66 | $file['name'][$i], |
||
67 | $file['tmp_name'][$i] |
||
68 | ); |
||
69 | } |
||
70 | } else { |
||
71 | $this->files[$name] = new Upload( |
||
72 | $file['error_code'], |
||
73 | $file['name'], |
||
74 | $file['tmp_name'] |
||
75 | ); |
||
76 | } |
||
77 | } |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * @return string |
||
82 | */ |
||
83 | final public function __toString() |
||
84 | { |
||
85 | return $this->path; |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Returns `POST` args merged over `GET` args. |
||
90 | * |
||
91 | * @return array |
||
92 | */ |
||
93 | public function getArgs(): array |
||
94 | { |
||
95 | return array_merge($_GET, $_POST); |
||
96 | } |
||
97 | |||
98 | /** |
||
99 | * Returns the client IP, which may have been forwarded. |
||
100 | * |
||
101 | * @return string |
||
102 | */ |
||
103 | public function getClient() |
||
104 | { |
||
105 | if (in_array($_SERVER['REMOTE_ADDR'], $this->proxies)) { |
||
106 | return $this['X-Forwarded-For'] ?? $_SERVER['REMOTE_ADDR']; |
||
107 | } |
||
108 | return $_SERVER['REMOTE_ADDR']; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * @param string $name |
||
113 | * @return null|Upload |
||
114 | */ |
||
115 | final public function getFile(string $name) |
||
116 | { |
||
117 | return $this->files[$name] ?? null; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @param string $name |
||
122 | * @return Upload[] |
||
123 | */ |
||
124 | final public function getFileGroup(string $name) |
||
125 | { |
||
126 | return $this->fileGroups[$name] ?? []; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * @return Upload[][] |
||
131 | */ |
||
132 | final public function getFileGroups() |
||
133 | { |
||
134 | return $this->fileGroups; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * @return Upload[] |
||
139 | */ |
||
140 | final public function getFiles() |
||
141 | { |
||
142 | return $this->files; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * @return string[] |
||
147 | */ |
||
148 | public function getHeaders(): array |
||
149 | { |
||
150 | return $this->headers; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * @return string |
||
155 | */ |
||
156 | final public function getMethod(): string |
||
157 | { |
||
158 | return $_SERVER['REQUEST_METHOD']; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * @return string |
||
163 | */ |
||
164 | final public function getPath(): string |
||
165 | { |
||
166 | return $this->path; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * @return string[] |
||
171 | */ |
||
172 | public function getProxies(): array |
||
173 | { |
||
174 | return $this->proxies; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * @return bool |
||
179 | */ |
||
180 | final public function isDelete(): bool |
||
181 | { |
||
182 | return $_SERVER['REQUEST_METHOD'] === 'DELETE'; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * @return bool |
||
187 | */ |
||
188 | final public function isGet(): bool |
||
189 | { |
||
190 | return $_SERVER['REQUEST_METHOD'] === 'GET'; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * @return bool |
||
195 | */ |
||
196 | final public function isHead(): bool |
||
197 | { |
||
198 | return $_SERVER['REQUEST_METHOD'] === 'HEAD'; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Whether the request can produce side-effects. |
||
203 | * |
||
204 | * @return bool |
||
205 | */ |
||
206 | final public function isMuting(): bool |
||
207 | { |
||
208 | return !$this->isGet() and !$this->isHead(); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @return bool |
||
213 | */ |
||
214 | final public function isPost(): bool |
||
215 | { |
||
216 | return $_SERVER['REQUEST_METHOD'] === 'POST'; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Checks for a request header. |
||
221 | * |
||
222 | * @param string $key |
||
223 | * @return bool |
||
224 | */ |
||
225 | public function offsetExists($key): bool |
||
226 | { |
||
227 | return isset($this->headers[strtolower($key)]); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Returns a request header. |
||
232 | * |
||
233 | * @param string $key |
||
234 | * @return null|string |
||
235 | */ |
||
236 | public function offsetGet($key): ?string |
||
237 | { |
||
238 | return $this->headers[strtolower($key)] ?? null; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Throws. |
||
243 | * |
||
244 | * @param mixed $key |
||
245 | * @param mixed $value |
||
246 | * @throws LogicException |
||
247 | */ |
||
248 | final public function offsetSet($key, $value): void |
||
249 | { |
||
250 | throw new LogicException('Request headers are immutable.'); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Throws. |
||
255 | * |
||
256 | * @param mixed $key |
||
257 | * @throws LogicException |
||
258 | */ |
||
259 | final public function offsetUnset($key): void |
||
260 | { |
||
261 | throw new LogicException('Request headers are immutable.'); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param string[] $proxies |
||
266 | * @return $this |
||
267 | */ |
||
268 | public function setProxies(array $proxies) |
||
269 | { |
||
270 | $this->proxies = $proxies; |
||
271 | return $this; |
||
272 | } |
||
273 | } |
||
274 |