This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace PHPDaemon\HTTPRequest; |
||
3 | |||
4 | use PHPDaemon\Core\Daemon; |
||
5 | use PHPDaemon\FS\File; |
||
6 | use PHPDaemon\FS\FileSystem; |
||
7 | use PHPDaemon\Request\RequestHeadersAlreadySent; |
||
8 | use PHPDaemon\Traits\DeferredEventHandlers; |
||
9 | use PHPDaemon\Utils\MIME; |
||
10 | |||
11 | /** |
||
12 | * HTTP request |
||
13 | * @package PHPDaemon\HTTPRequest |
||
14 | * @author Vasily Zorin <[email protected]> |
||
15 | */ |
||
16 | abstract class Generic extends \PHPDaemon\Request\Generic |
||
17 | { |
||
18 | use DeferredEventHandlers; |
||
19 | use \PHPDaemon\Traits\Sessions; |
||
20 | |||
21 | /** |
||
22 | * @var array Status codes |
||
23 | */ |
||
24 | protected static $codes = [ |
||
25 | 100 => 'Continue', |
||
26 | 101 => 'Switching Protocols', |
||
27 | 200 => 'OK', |
||
28 | 201 => 'Created', |
||
29 | 202 => 'Accepted', |
||
30 | 203 => 'Non-Authoritative Information', |
||
31 | 204 => 'No Content', |
||
32 | 205 => 'Reset Content', |
||
33 | 206 => 'Partial Content', |
||
34 | 300 => 'Multiple Choices', |
||
35 | 301 => 'Moved Permanently', |
||
36 | 302 => 'Found', |
||
37 | 303 => 'See Other', |
||
38 | 304 => 'Not Modified', |
||
39 | 305 => 'Use Proxy', |
||
40 | 306 => '(Unused)', |
||
41 | 307 => 'Temporary Redirect', |
||
42 | 400 => 'Bad Request', |
||
43 | 401 => 'Unauthorized', |
||
44 | 402 => 'Payment Required', |
||
45 | 403 => 'Forbidden', |
||
46 | 404 => 'Not Found', |
||
47 | 405 => 'Method Not Allowed', |
||
48 | 406 => 'Not Acceptable', |
||
49 | 407 => 'Proxy Authentication Required', |
||
50 | 408 => 'Request Timeout', |
||
51 | 409 => 'Conflict', |
||
52 | 410 => 'Gone', |
||
53 | 411 => 'Length Required', |
||
54 | 412 => 'Precondition Failed', |
||
55 | 413 => 'Request Entity Too Large', |
||
56 | 414 => 'Request-URI Too Long', |
||
57 | 415 => 'Unsupported Media Type', |
||
58 | 416 => 'Requested Range Not Satisfiable', |
||
59 | 417 => 'Expectation Failed', |
||
60 | 422 => 'Unprocessable Entity', |
||
61 | 423 => 'Locked', |
||
62 | 500 => 'Internal Server Error', |
||
63 | 501 => 'Not Implemented', |
||
64 | 502 => 'Bad Gateway', |
||
65 | 503 => 'Service Unavailable', |
||
66 | 504 => 'Gateway Timeout', |
||
67 | 505 => 'HTTP Version Not Supported', |
||
68 | ]; |
||
69 | |||
70 | /** |
||
71 | * @var boolean Keepalive? |
||
72 | */ |
||
73 | public $keepalive = false; |
||
74 | |||
75 | /** |
||
76 | * @var integer Current response length |
||
77 | */ |
||
78 | public $responseLength = 0; |
||
79 | |||
80 | /** |
||
81 | * @var integer Content length from header() method |
||
82 | */ |
||
83 | protected $contentLength; |
||
84 | |||
85 | /** |
||
86 | * @var integer Number of outgoing cookie-headers |
||
87 | */ |
||
88 | protected $cookieNum = 0; |
||
89 | |||
90 | /** |
||
91 | * @var array Replacement pairs for processing some header values in parse_str() |
||
92 | */ |
||
93 | public static $hvaltr = ['; ' => '&', ';' => '&', ' ' => '%20']; |
||
94 | |||
95 | /** |
||
96 | * @var array State |
||
97 | */ |
||
98 | public static $htr = ['-' => '_']; |
||
99 | |||
100 | /** |
||
101 | * @var array Outgoing headers |
||
102 | */ |
||
103 | protected $headers = ['STATUS' => '200 OK']; |
||
104 | |||
105 | /** |
||
106 | * @var boolean Headers sent? |
||
107 | */ |
||
108 | protected $headers_sent = false; |
||
109 | |||
110 | /** |
||
111 | * @var boolean File name where output started in the file and line variables |
||
112 | */ |
||
113 | protected $headers_sent_file; |
||
114 | |||
115 | /** |
||
116 | * @var boolean Line number where output started in the file and line variables |
||
117 | */ |
||
118 | protected $headers_sent_line; |
||
119 | |||
120 | /** |
||
121 | * @var File File pointer to send output (X-Sendfile) |
||
122 | */ |
||
123 | protected $sendfp; |
||
124 | |||
125 | /** |
||
126 | * @var boolean Frozen input? |
||
127 | */ |
||
128 | protected $frozenInput = false; |
||
129 | |||
130 | /** |
||
131 | * @var array Content type parameters |
||
132 | */ |
||
133 | protected $contype; |
||
134 | |||
135 | /** |
||
136 | * Preparing before init |
||
137 | * @param object $req Source request |
||
138 | * @return void |
||
139 | */ |
||
140 | protected function preinit($req) |
||
141 | { |
||
142 | if ($req === null) { |
||
143 | $req = new \stdClass; |
||
144 | $req->attrs = new \stdClass; |
||
145 | $req->attrs->inputDone = true; |
||
146 | $req->attrs->paramsDone = true; |
||
147 | $req->attrs->chunked = false; |
||
148 | } |
||
149 | |||
150 | $this->attrs = $req->attrs; |
||
151 | |||
152 | if ($this->upstream->pool->config->expose->value) { |
||
153 | $this->header('X-Powered-By: phpDaemon/' . Daemon::$version); |
||
154 | } |
||
155 | |||
156 | $this->attrs->input->setRequest($this); |
||
157 | |||
158 | $this->parseParams(); |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Called when first deferred event used |
||
163 | * @return void |
||
164 | */ |
||
165 | public function firstDeferredEventUsed() |
||
166 | { |
||
167 | $this->bind('finish', [$this, 'cleanupDeferredEventHandlers']); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Output whole contents of file |
||
172 | * @param string $path Path |
||
173 | * @param callable $cb Callback |
||
174 | * @param integer $pri Priority |
||
175 | * @return boolean Success |
||
176 | */ |
||
177 | public function sendfile($path, $cb, $pri = EIO_PRI_DEFAULT) |
||
178 | { |
||
179 | if ($this->state === self::STATE_FINISHED) { |
||
180 | return false; |
||
181 | } |
||
182 | try { |
||
183 | $this->header('Content-Type: ' . MIME::get($path)); |
||
184 | } catch (RequestHeadersAlreadySent $e) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
![]() |
|||
185 | } |
||
186 | if ($this->upstream->checkSendfileCap()) { |
||
187 | FileSystem::sendfile($this->upstream, $path, $cb, function ($file, $length, $handler) { |
||
188 | try { |
||
189 | $this->header('Content-Length: ' . $length); |
||
190 | } catch (RequestHeadersAlreadySent $e) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
191 | } |
||
192 | $this->ensureSentHeaders(); |
||
193 | $this->upstream->onWriteOnce(function ($conn) use ($handler, $file) { |
||
0 ignored issues
–
show
|
|||
194 | $handler($file); |
||
195 | }); |
||
196 | return true; |
||
197 | }, 0, null, $pri); |
||
198 | return true; |
||
199 | } |
||
200 | $first = true; |
||
201 | |||
202 | FileSystem::readfileChunked( |
||
203 | $path, |
||
204 | $cb, |
||
205 | function ($file, $chunk) use (&$first) { |
||
206 | // readed chunk |
||
207 | if ($this->upstream->isFreed()) { |
||
208 | return false; |
||
209 | } |
||
210 | |||
211 | if ($first) { |
||
212 | try { |
||
213 | $this->header('Content-Length: ' . $file->stat['size']); |
||
214 | } catch (RequestHeadersAlreadySent $e) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
215 | } |
||
216 | $first = false; |
||
217 | } |
||
218 | $this->out($chunk); |
||
219 | return true; |
||
220 | } |
||
221 | ); |
||
222 | |||
223 | return true; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Get cookie by name |
||
228 | * @param string $name Name of cookie |
||
229 | * @return string Contents |
||
230 | */ |
||
231 | protected function getCookieStr($name) |
||
232 | { |
||
233 | return static::getString($this->attrs->cookie[$name]); |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Called to check if Request is ready |
||
238 | * @return boolean Ready? |
||
239 | */ |
||
240 | public function checkIfReady() |
||
241 | { |
||
242 | if (!$this->attrs->paramsDone || !$this->attrs->inputDone) { |
||
243 | return false; |
||
244 | } |
||
245 | if (isset($this->appInstance->passphrase)) { |
||
246 | if (!isset($this->attrs->server['PASSPHRASE']) |
||
247 | || ($this->appInstance->passphrase !== $this->attrs->server['PASSPHRASE']) |
||
248 | ) { |
||
249 | $this->finish(); |
||
250 | } |
||
251 | |||
252 | return false; |
||
253 | } |
||
254 | |||
255 | if ($this->attrs->input->isFrozen()) { |
||
256 | return false; |
||
257 | } |
||
258 | |||
259 | if ($this->sleepTime === 0) { |
||
260 | $this->wakeup(); |
||
261 | } |
||
262 | |||
263 | return true; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Upload maximum file size |
||
268 | * @return integer |
||
269 | */ |
||
270 | public function getUploadMaxSize() |
||
271 | { |
||
272 | return $this->upstream->pool->config->uploadmaxsize->value; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Parses GET-query string and other request's headers |
||
277 | * @return void |
||
278 | */ |
||
279 | protected function parseParams() |
||
280 | { |
||
281 | if (!isset($this->attrs->server['HTTP_CONTENT_LENGTH'])) { |
||
282 | $this->attrs->contentLength = 0; |
||
283 | } else { |
||
284 | $this->attrs->contentLength = (int)$this->attrs->server['HTTP_CONTENT_LENGTH']; |
||
285 | } |
||
286 | |||
287 | if (isset($this->attrs->server['CONTENT_TYPE']) && !isset($this->attrs->server['HTTP_CONTENT_TYPE'])) { |
||
288 | $this->attrs->server['HTTP_CONTENT_TYPE'] = $this->attrs->server['CONTENT_TYPE']; |
||
289 | } |
||
290 | |||
291 | View Code Duplication | if (isset($this->attrs->server['QUERY_STRING'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
292 | self::parseStr($this->attrs->server['QUERY_STRING'], $this->attrs->get); |
||
293 | } |
||
294 | if (isset($this->attrs->server['REQUEST_METHOD']) |
||
295 | && ($this->attrs->server['REQUEST_METHOD'] === 'POST' || $this->attrs->server['REQUEST_METHOD'] === 'PUT') |
||
296 | && isset($this->attrs->server['HTTP_CONTENT_TYPE']) |
||
297 | ) { |
||
298 | $this->attrs->server['REQUEST_METHOD_POST'] = true; |
||
299 | self::parseStr($this->attrs->server['HTTP_CONTENT_TYPE'], $this->contype, true); |
||
300 | $found = false; |
||
301 | foreach ($this->contype as $k => $v) { |
||
302 | if (mb_orig_strpos($k, '/') === false) { |
||
303 | continue; |
||
304 | } |
||
305 | if (!$found) { |
||
306 | $found = true; |
||
307 | } else { |
||
308 | unset($this->contype[$k]); |
||
309 | } |
||
310 | } |
||
311 | |||
312 | if (isset($this->contype['multipart/form-data']) |
||
313 | && (isset($this->contype['boundary'])) |
||
314 | ) { |
||
315 | $this->attrs->input->setBoundary($this->contype['boundary']); |
||
316 | } |
||
317 | } else { |
||
318 | $this->attrs->server['REQUEST_METHOD_POST'] = false; |
||
319 | } |
||
320 | |||
321 | View Code Duplication | if (isset($this->attrs->server['HTTP_COOKIE'])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
322 | self::parseStr($this->attrs->server['HTTP_COOKIE'], $this->attrs->cookie, true); |
||
0 ignored issues
–
show
$this->attrs->server['HTTP_COOKIE'] is of type boolean , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
323 | } |
||
324 | |||
325 | if (isset($this->attrs->server['HTTP_AUTHORIZATION'])) { |
||
326 | $e = explode(' ', $this->attrs->server['HTTP_AUTHORIZATION'], 2); |
||
327 | |||
328 | if (($e[0] === 'Basic') && isset($e[1])) { |
||
329 | $e[1] = base64_decode($e[1]); |
||
330 | $e = explode(':', $e[1], 2); |
||
331 | |||
332 | if (isset($e[1])) { |
||
333 | list($this->attrs->server['PHP_AUTH_USER'], $this->attrs->server['PHP_AUTH_PW']) = $e; |
||
334 | } |
||
335 | } |
||
336 | } |
||
337 | |||
338 | $this->onParsedParams(); |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Prepares the request body |
||
343 | * @return void |
||
344 | */ |
||
345 | public function postPrepare() |
||
346 | { |
||
347 | if (!$this->attrs->server['REQUEST_METHOD_POST']) { |
||
348 | return; |
||
349 | } |
||
350 | if (isset($this->attrs->server['REQUEST_PREPARED_UPLOADS']) && $this->attrs->server['REQUEST_PREPARED_UPLOADS'] === 'nginx') { |
||
351 | if (isset($this->attrs->server['REQUEST_PREPARED_UPLOADS_URL_PREFIX'])) { |
||
352 | $URLprefix = $this->attrs->server['REQUEST_PREPARED_UPLOADS_URL_PREFIX']; |
||
353 | $l = mb_orig_strlen($URLprefix); |
||
354 | foreach (['PHP_SELF', 'REQUEST_URI', 'SCRIPT_NAME', 'DOCUMENT_URI'] as $k) { |
||
355 | if (!isset($this->attrs->server[$k])) { |
||
356 | continue; |
||
357 | } |
||
358 | if (strncmp($this->attrs->server[$k], $URLprefix, $l) === 0) { |
||
359 | $this->attrs->server[$k] = substr($this->attrs->server[$k], $l - 1); |
||
360 | } |
||
361 | } |
||
362 | } |
||
363 | $prefix = 'file.'; |
||
364 | $prefixlen = mb_orig_strlen($prefix); |
||
365 | foreach ($this->attrs->post as $k => $v) { |
||
366 | if (strncmp($k, $prefix, $prefixlen) === 0) { |
||
367 | $e = explode('.', substr($k, $prefixlen)); |
||
368 | if (!isset($e[1])) { |
||
369 | $e = ['file', $e[0]]; |
||
370 | } |
||
371 | if (!isset($this->attrs->files[$e[0]])) { |
||
372 | $this->attrs->files[$e[0]] = ['error' => UPLOAD_ERR_OK]; |
||
373 | } |
||
374 | $this->attrs->files[$e[0]][$e[1]] = $v; |
||
375 | unset($this->attrs->post[$k]); |
||
376 | } |
||
377 | } |
||
378 | $uploadTmp = $this->getUploadTempDir(); |
||
379 | foreach ($this->attrs->files as $k => &$file) { |
||
380 | if (!isset($file['tmp_name']) |
||
381 | || !isset($file['name']) |
||
382 | || !ctype_digit(basename($file['tmp_name'])) |
||
383 | || (mb_orig_strpos(pathinfo($file['tmp_name'], PATHINFO_DIRNAME), $uploadTmp) !== 0) |
||
384 | ) { |
||
385 | unset($this->attrs->files[$k]); |
||
386 | continue; |
||
387 | } |
||
388 | FileSystem::open($file['tmp_name'], 'c+!', function ($fp) use (&$file) { |
||
389 | if (!$fp) { |
||
390 | return; |
||
391 | } |
||
392 | $file['fp'] = $fp; |
||
393 | }); |
||
394 | } |
||
395 | unset($file); |
||
396 | } |
||
397 | if (isset($this->attrs->server['REQUEST_BODY_FILE']) |
||
398 | && $this->upstream->pool->config->autoreadbodyfile->value |
||
399 | ) { |
||
400 | $this->readBodyFile(); |
||
401 | } |
||
402 | } |
||
403 | |||
404 | /** |
||
405 | * Ensure that headers are sent |
||
406 | * @return boolean Were already sent? |
||
407 | */ |
||
408 | public function ensureSentHeaders() |
||
409 | { |
||
410 | if ($this->headers_sent) { |
||
411 | return true; |
||
412 | } |
||
413 | if (isset($this->headers['STATUS'])) { |
||
414 | $h = (isset($this->attrs->noHttpVer) && ($this->attrs->noHttpVer) ? 'Status: ' : $this->attrs->server['SERVER_PROTOCOL']) . ' ' . $this->headers['STATUS'] . "\r\n"; |
||
415 | } else { |
||
416 | $h = ''; |
||
417 | } |
||
418 | $http11 = $this->attrs->server['SERVER_PROTOCOL'] === 'HTTP/1.1'; |
||
419 | if ($this->contentLength === null |
||
420 | && $this->upstream->checkChunkedEncCap() |
||
421 | && $http11 |
||
422 | ) { |
||
423 | $this->attrs->chunked = true; |
||
424 | } |
||
425 | if ($this->attrs->chunked) { |
||
426 | $this->header('Transfer-Encoding: chunked'); |
||
427 | } |
||
428 | |||
429 | if ($http11) { |
||
430 | $connection = isset($this->attrs->server['HTTP_CONNECTION']) ? strtolower($this->attrs->server['HTTP_CONNECTION']) : 'keep-alive'; |
||
431 | if ($connection === 'keep-alive' && $this->upstream->getKeepaliveTimeout() > 0) { |
||
432 | $this->header('Connection: keep-alive'); |
||
433 | $this->keepalive = true; |
||
434 | } else { |
||
435 | $this->header('Connection: close'); |
||
436 | } |
||
437 | } else { |
||
438 | $this->header('Connection: close'); |
||
439 | } |
||
440 | |||
441 | foreach ($this->headers as $k => $line) { |
||
442 | if ($k !== 'STATUS') { |
||
443 | $h .= $line . "\r\n"; |
||
444 | } |
||
445 | } |
||
446 | $h .= "\r\n"; |
||
447 | $this->headers_sent_file = __FILE__; |
||
0 ignored issues
–
show
The property
$headers_sent_file was declared of type boolean , but __FILE__ is of type string . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
448 | $this->headers_sent_line = __LINE__; |
||
0 ignored issues
–
show
The property
$headers_sent_line was declared of type boolean , but __LINE__ is of type integer . Maybe add a type cast?
This check looks for assignments to scalar types that may be of the wrong type. To ensure the code behaves as expected, it may be a good idea to add an explicit type cast. $answer = 42;
$correct = false;
$correct = (bool) $answer;
![]() |
|||
449 | $this->headers_sent = true; |
||
450 | $this->upstream->requestOut($this, $h); |
||
451 | return false; |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * Output some data |
||
456 | * @param string $s String to out |
||
457 | * @param boolean $flush ob_flush? |
||
458 | * @return boolean Success |
||
459 | */ |
||
460 | public function out($s, $flush = true) |
||
461 | { |
||
462 | if ($flush) { |
||
463 | if (!Daemon::$obInStack) { // preventing recursion |
||
464 | ob_flush(); |
||
465 | } |
||
466 | } |
||
467 | |||
468 | if ($this->aborted) { |
||
469 | return false; |
||
470 | } |
||
471 | if (!isset($this->upstream)) { |
||
472 | return false; |
||
473 | } |
||
474 | |||
475 | $l = mb_orig_strlen($s); |
||
476 | $this->responseLength += $l; |
||
477 | |||
478 | $this->ensureSentHeaders(); |
||
479 | |||
480 | if ($this->attrs->chunked) { |
||
481 | for ($o = 0; $o < $l;) { |
||
482 | $c = min($this->upstream->pool->config->chunksize->value, $l - $o); |
||
483 | |||
484 | $chunk = dechex($c) . "\r\n" |
||
485 | . ($c === $l ? $s : mb_orig_substr($s, $o, $c)) // content |
||
486 | . "\r\n"; |
||
487 | |||
488 | if ($this->sendfp) { |
||
489 | $this->sendfp->write($chunk); |
||
490 | } else { |
||
491 | $this->upstream->requestOut($this, $chunk); |
||
492 | } |
||
493 | |||
494 | $o += $c; |
||
495 | } |
||
496 | return true; |
||
497 | } else { |
||
498 | if ($this->sendfp) { |
||
499 | $this->sendfp->write($s); |
||
500 | return true; |
||
501 | } |
||
502 | |||
503 | if (Daemon::$compatMode) { |
||
504 | echo $s; |
||
505 | return true; |
||
506 | } |
||
507 | |||
508 | return $this->upstream->requestOut($this, $s); |
||
509 | } |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Called when request's headers parsed |
||
514 | * @return void |
||
515 | */ |
||
516 | public function onParsedParams() |
||
517 | { |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * Outputs data with headers (split by \r\n\r\n) |
||
522 | * @param string $s Data |
||
523 | * @return boolean Success |
||
524 | */ |
||
525 | public function combinedOut($s) |
||
526 | { |
||
527 | if (!$this->headers_sent) { |
||
528 | $e = explode("\r\n\r\n", $s, 2); |
||
529 | $h = explode("\r\n", $e[0]); |
||
530 | |||
531 | foreach ($h as $l) { |
||
532 | $this->header($l); |
||
533 | } |
||
534 | |||
535 | if (isset($e[1])) { |
||
536 | return $this->out($e[1]); |
||
537 | } |
||
538 | |||
539 | return true; |
||
540 | } else { |
||
541 | return $this->out($s); |
||
542 | } |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * Use chunked encoding |
||
547 | * @return void |
||
548 | */ |
||
549 | public function chunked() |
||
550 | { |
||
551 | $this->header('Transfer-Encoding: chunked'); |
||
552 | $this->attrs->chunked = true; |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Called when the request wakes up |
||
557 | * @return void |
||
558 | */ |
||
559 | public function onWakeup() |
||
560 | { |
||
561 | parent::onWakeup(); |
||
562 | if (!Daemon::$obInStack) { // preventing recursion |
||
563 | @ob_flush(); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
564 | } |
||
565 | $_GET = &$this->attrs->get; |
||
566 | $_POST = &$this->attrs->post; |
||
567 | $_COOKIE = &$this->attrs->cookie; |
||
568 | $_REQUEST = &$this->attrs->request; |
||
569 | $_SESSION = &$this->attrs->session; |
||
570 | $_FILES = &$this->attrs->files; |
||
571 | $_SERVER = &$this->attrs->server; |
||
572 | } |
||
573 | |||
574 | /** |
||
575 | * Called when the request starts sleep |
||
576 | * @return void |
||
577 | */ |
||
578 | public function onSleep() |
||
579 | { |
||
580 | if (!Daemon::$obInStack) { // preventing recursion |
||
581 | @ob_flush(); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
582 | } |
||
583 | unset($_GET); |
||
584 | unset($_POST); |
||
585 | unset($_COOKIE); |
||
586 | unset($_REQUEST); |
||
587 | unset($_SESSION); |
||
588 | unset($_FILES); |
||
589 | unset($_SERVER); |
||
590 | parent::onSleep(); |
||
591 | } |
||
592 | |||
593 | /** |
||
594 | * Send HTTP-status |
||
595 | * @param integer $code Code |
||
596 | * @throws RequestHeadersAlreadySent |
||
597 | * @return boolean Success |
||
598 | */ |
||
599 | public function status($code = 200) |
||
600 | { |
||
601 | if (!isset(self::$codes[$code])) { |
||
602 | return false; |
||
603 | } |
||
604 | $this->header($code . ' ' . self::$codes[$code]); |
||
605 | return true; |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * Checks if headers have been sent |
||
610 | * @param string &$file File name |
||
611 | * @param integer &$line Line in file |
||
612 | * @return boolean Success |
||
613 | */ |
||
614 | public function headersSent(&$file, &$line) |
||
615 | { |
||
616 | $file = $this->headers_sent_file; |
||
617 | $line = $this->headers_sent_line; |
||
618 | return $this->headers_sent; |
||
619 | } |
||
620 | |||
621 | /** |
||
622 | * Return current list of headers |
||
623 | * @return array Headers |
||
624 | */ |
||
625 | public function headersList() |
||
626 | { |
||
627 | return array_values($this->headers); |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * Set the cookie |
||
632 | * @param string $name Name of cookie |
||
633 | * @param string $value Value |
||
634 | * @param integer $maxage Optional. Max-Age. Default is 0 |
||
635 | * @param string $path Optional. Path. Default is empty string |
||
636 | * @param string $domain Optional. Domain. Default is empty string |
||
637 | * @param boolean $secure Optional. Secure. Default is false |
||
638 | * @param boolean $HTTPOnly Optional. HTTPOnly. Default is false |
||
639 | * @return void |
||
640 | */ |
||
641 | View Code Duplication | public function setcookie( |
|
0 ignored issues
–
show
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. ![]() |
|||
642 | $name, |
||
643 | $value = '', |
||
644 | $maxage = 0, |
||
645 | $path = '', |
||
646 | $domain = '', |
||
647 | $secure = false, |
||
648 | $HTTPOnly = false |
||
649 | ) { |
||
650 | $this->header( |
||
651 | 'Set-Cookie: ' . $name . '=' . rawurlencode($value) |
||
652 | . (empty($domain) ? '' : '; Domain=' . $domain) |
||
653 | . (empty($maxage) ? '' : '; Max-Age=' . $maxage) |
||
654 | . (empty($path) ? '' : '; Path=' . $path) |
||
655 | . (!$secure ? '' : '; Secure') |
||
656 | . (!$HTTPOnly ? '' : '; HttpOnly'), |
||
657 | false |
||
658 | ); |
||
659 | } |
||
660 | |||
661 | /** |
||
662 | * Send the header |
||
663 | * @param string $s Header. Example: 'Location: http://php.net/' |
||
664 | * @param boolean $replace Optional. Replace? |
||
665 | * @param integer $code Optional. HTTP response code |
||
0 ignored issues
–
show
Should the type for parameter
$code not be false|integer ?
This check looks for It makes a suggestion as to what type it considers more descriptive. Most often this is a case of a parameter that can be null in addition to its declared types. ![]() |
|||
666 | * @throws \PHPDaemon\Request\RequestHeadersAlreadySent |
||
667 | * @return boolean Success |
||
668 | */ |
||
669 | View Code Duplication | public function header($s, $replace = true, $code = false) |
|
0 ignored issues
–
show
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. ![]() |
|||
670 | { |
||
671 | if (!$code) { |
||
0 ignored issues
–
show
The expression
$code of type false|integer is loosely compared to false ; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||
672 | $this->status($code); |
||
0 ignored issues
–
show
It seems like
$code defined by parameter $code on line 669 can also be of type false ; however, PHPDaemon\HTTPRequest\Generic::status() does only seem to accept integer , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
673 | } |
||
674 | |||
675 | if ($this->headers_sent) { |
||
676 | throw new RequestHeadersAlreadySent; |
||
677 | } |
||
678 | $s = strtr($s, "\r\n", ' '); |
||
679 | |||
680 | $e = explode(':', $s, 2); |
||
681 | |||
682 | if (!isset($e[1])) { |
||
683 | $e[0] = 'STATUS'; |
||
684 | |||
685 | if (strncmp($s, 'HTTP/', 5) === 0) { |
||
686 | $s = substr($s, 9); |
||
687 | } |
||
688 | } |
||
689 | |||
690 | $k = strtr(strtoupper($e[0]), Generic::$htr); |
||
691 | |||
692 | if ($k === 'CONTENT_TYPE') { |
||
693 | self::parseStr(strtolower($e[1]), $ctype, true); |
||
694 | if (!isset($ctype['charset'])) { |
||
695 | $ctype['charset'] = $this->upstream->pool->config->defaultcharset->value; |
||
696 | |||
697 | $s = $e[0] . ': '; |
||
698 | $i = 0; |
||
699 | foreach ($ctype as $k => $v) { |
||
700 | $s .= ($i > 0 ? '; ' : '') . $k . ($v !== '' ? '=' . $v : ''); |
||
701 | ++$i; |
||
702 | } |
||
703 | } |
||
704 | } |
||
705 | |||
706 | if ($k === 'SET_COOKIE') { |
||
707 | $k .= '_' . ++$this->cookieNum; |
||
708 | } elseif (!$replace && isset($this->headers[$k])) { |
||
709 | return false; |
||
710 | } |
||
711 | |||
712 | $this->headers[$k] = $s; |
||
713 | |||
714 | if ($k === 'CONTENT_LENGTH') { |
||
715 | $this->contentLength = (int)$e[1]; |
||
716 | } elseif ($k === 'LOCATION') { |
||
717 | $this->status(301); |
||
718 | } |
||
719 | |||
720 | if (Daemon::$compatMode) { |
||
721 | is_callable('header_native') ? header_native($s) : header($s); |
||
722 | } |
||
723 | |||
724 | return true; |
||
725 | } |
||
726 | |||
727 | /** |
||
728 | * Removes a header |
||
729 | * @param string $s Header name. Example: 'Location' |
||
730 | * @return void |
||
731 | */ |
||
732 | public function removeHeader($s) |
||
733 | { |
||
734 | unset($this->headers[strtr(strtoupper($s), Generic::$htr)]); |
||
735 | } |
||
736 | |||
737 | /** |
||
738 | * Converts human-readable representation of size to number of bytes |
||
739 | * @param string $value String of size |
||
740 | * @return integer |
||
741 | */ |
||
742 | View Code Duplication | public static function parseSize($value) |
|
0 ignored issues
–
show
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. ![]() |
|||
743 | { |
||
744 | $l = substr($value, -1); |
||
745 | |||
746 | if ($l === 'b' || $l === 'B') { |
||
747 | return ((int)substr($value, 0, -1)); |
||
748 | } |
||
749 | |||
750 | if ($l === 'k') { |
||
751 | return ((int)substr($value, 0, -1) * 1000); |
||
752 | } |
||
753 | |||
754 | if ($l === 'K') { |
||
755 | return ((int)substr($value, 0, -1) * 1024); |
||
756 | } |
||
757 | |||
758 | if ($l === 'm') { |
||
759 | return ((int)substr($value, 0, -1) * 1000 * 1000); |
||
760 | } |
||
761 | |||
762 | if ($l === 'M') { |
||
763 | return ((int)substr($value, 0, -1) * 1024 * 1024); |
||
764 | } |
||
765 | |||
766 | if ($l === 'g') { |
||
767 | return ((int)substr($value, 0, -1) * 1000 * 1000 * 1000); |
||
768 | } |
||
769 | |||
770 | if ($l === 'G') { |
||
771 | return ((int)substr($value, 0, -1) * 1024 * 1024 * 1024); |
||
772 | } |
||
773 | |||
774 | return (int)$value; |
||
775 | } |
||
776 | |||
777 | /** |
||
778 | * Called when file upload started |
||
779 | * @param Input $in Input buffer |
||
780 | * @return void |
||
781 | */ |
||
782 | public function onUploadFileStart($in) |
||
783 | { |
||
784 | $this->freezeInput(); |
||
785 | FileSystem::tempnam(ini_get('upload_tmp_dir'), 'php', function ($fp) use ($in) { |
||
786 | if (!$fp) { |
||
787 | $in->curPart['fp'] = false; |
||
788 | $in->curPart['error'] = UPLOAD_ERR_NO_TMP_DIR; |
||
789 | } else { |
||
790 | $in->curPart['fp'] = $fp; |
||
791 | $in->curPart['tmp_name'] = $fp->path; |
||
792 | } |
||
793 | $this->unfreezeInput(); |
||
794 | }); |
||
795 | } |
||
796 | |||
797 | /** |
||
798 | * Called when chunk of incoming file has arrived |
||
799 | * @param Input $in Input buffer |
||
800 | * @param boolean $last Last? |
||
801 | * @return void |
||
802 | */ |
||
803 | public function onUploadFileChunk($in, $last = false) |
||
804 | { |
||
805 | if ($in->curPart['error'] !== UPLOAD_ERR_OK) { |
||
806 | // just drop the chunk |
||
807 | return; |
||
808 | } |
||
809 | $cb = function ($fp, $result) use ($last, $in) { |
||
0 ignored issues
–
show
|
|||
810 | if ($last) { |
||
811 | unset($in->curPart['fp']); |
||
812 | } |
||
813 | $this->unfreezeInput(); |
||
814 | }; |
||
815 | if ($in->writeChunkToFd($in->curPart['fp']->getFd())) { |
||
816 | // We had written via internal method |
||
817 | return; |
||
818 | } |
||
819 | // Internal method is not available, let's get chunk data into $chunk and then use File->write() |
||
820 | $chunk = $in->getChunkString(); |
||
821 | if ($chunk === false) { |
||
822 | return; |
||
823 | } |
||
824 | $this->freezeInput(); |
||
825 | $in->curPart['fp']->write($chunk, $cb); |
||
826 | } |
||
827 | |||
828 | /** |
||
829 | * Freeze input |
||
830 | * @return void |
||
831 | */ |
||
832 | protected function freezeInput() |
||
833 | { |
||
834 | $this->upstream->freezeInput(); |
||
835 | $this->attrs->input->freeze(); |
||
836 | } |
||
837 | |||
838 | /** |
||
839 | * Unfreeze input |
||
840 | * @return void |
||
841 | */ |
||
842 | protected function unfreezeInput() |
||
843 | { |
||
844 | $this->upstream->unfreezeInput(); |
||
845 | if (isset($this->attrs->input)) { |
||
846 | $this->attrs->input->unfreeze(); |
||
847 | } |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * Returns path to directory of temporary upload files |
||
852 | * @return string |
||
853 | */ |
||
854 | public function getUploadTempDir() |
||
855 | { |
||
856 | if ($r = ini_get('upload_tmp_dir')) { |
||
857 | return $r; |
||
858 | } |
||
859 | return sys_get_temp_dir(); |
||
860 | } |
||
861 | |||
862 | /** |
||
863 | * Tells whether the file was uploaded via HTTP POST |
||
864 | * @param string $path The filename being checked |
||
865 | * @return boolean Whether if this is uploaded file |
||
866 | */ |
||
867 | public function isUploadedFile($path) |
||
868 | { |
||
869 | if (!$path) { |
||
870 | return false; |
||
871 | } |
||
872 | if (mb_orig_strpos($path, $this->getUploadTempDir() . '/') !== 0) { |
||
873 | return false; |
||
874 | } |
||
875 | foreach ($this->attrs->files as $file) { |
||
876 | if ($file['tmp_name'] === $path) { |
||
877 | return true; |
||
878 | } |
||
879 | } |
||
880 | return false; |
||
881 | } |
||
882 | |||
883 | /** |
||
884 | * Moves an uploaded file to a new location |
||
885 | * @param string $filename The filename of the uploaded file |
||
886 | * @param string $dest The destination of the moved file |
||
887 | * @return boolean Success |
||
0 ignored issues
–
show
|
|||
888 | */ |
||
889 | public function moveUploadedFile($filename, $dest) |
||
890 | { |
||
891 | if (!$this->isUploadedFile($filename)) { |
||
892 | return false; |
||
893 | } |
||
894 | return FileSystem::rename($filename, $dest); |
||
895 | } |
||
896 | |||
897 | /** |
||
898 | * Read request body from the file given in REQUEST_BODY_FILE parameter |
||
899 | * @return boolean Success |
||
900 | */ |
||
901 | public function readBodyFile() |
||
902 | { |
||
903 | if (!isset($this->attrs->server['REQUEST_BODY_FILE'])) { |
||
904 | return false; |
||
905 | } |
||
906 | |||
907 | FileSystem::readfileChunked( |
||
908 | $this->attrs->server['REQUEST_BODY_FILE'], |
||
909 | function ($file, $success) { |
||
0 ignored issues
–
show
|
|||
910 | $this->attrs->inputDone = true; |
||
911 | if ($this->sleepTime === 0) { |
||
912 | $this->wakeup(); |
||
913 | } |
||
914 | }, |
||
915 | function ($file, $chunk) { |
||
916 | // readed chunk |
||
917 | $this->stdin($chunk); |
||
0 ignored issues
–
show
The method
stdin does not exist on object<PHPDaemon\HTTPRequest\Generic> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
918 | } |
||
919 | ); |
||
920 | |||
921 | return true; |
||
922 | } |
||
923 | |||
924 | /** |
||
925 | * Replacement for default parse_str(), it supoorts UCS-2 like this: %uXXXX |
||
926 | * @param string $s String to parse |
||
927 | * @param array &$var Reference to the resulting array |
||
928 | * @param boolean $header Header-style string |
||
929 | * @return void |
||
930 | */ |
||
931 | public static function parseStr($s, &$var, $header = false) |
||
932 | { |
||
933 | static $cb; |
||
934 | if ($cb === null) { |
||
935 | $cb = function ($m) { |
||
936 | return urlencode(html_entity_decode('&#' . hexdec($m[1]) . ';', ENT_NOQUOTES, 'utf-8')); |
||
937 | }; |
||
938 | } |
||
939 | if ($header) { |
||
940 | $s = strtr($s, Generic::$hvaltr); |
||
941 | } |
||
942 | if ((stripos($s, '%u') !== false) |
||
943 | && preg_match('~(%u[a-f\d]{4}|%[c-f][a-f\d](?!%[89a-f][a-f\d]))~is', $s, $m) |
||
944 | ) { |
||
945 | $s = preg_replace_callback('~%(u[a-f\d]{4}|[a-f\d]{2})~i', $cb, $s); |
||
946 | } |
||
947 | parse_str($s, $var); |
||
948 | } |
||
949 | |||
950 | /** |
||
951 | * Called after request finish |
||
952 | * @param callable $cb Callback |
||
0 ignored issues
–
show
Should the type for parameter
$cb not be callable|null ?
This check looks for It makes a suggestion as to what type it considers more descriptive. Most often this is a case of a parameter that can be null in addition to its declared types. ![]() |
|||
953 | * @return void |
||
954 | */ |
||
955 | protected function postFinishHandler($cb = null) |
||
956 | { |
||
957 | if (!$this->headers_sent) { |
||
958 | $this->out(''); |
||
959 | } |
||
960 | $this->sendfp = null; |
||
961 | if (isset($this->attrs->files)) { |
||
962 | foreach ($this->attrs->files as $f) { |
||
963 | if (isset($f['tmp_name'])) { |
||
964 | FileSystem::unlink($f['tmp_name']); |
||
965 | } |
||
966 | } |
||
967 | } |
||
968 | if (isset($this->attrs->session)) { |
||
969 | $this->sessionCommit($cb); |
||
970 | } else { |
||
971 | $cb === null || $cb(); |
||
972 | } |
||
973 | } |
||
974 | } |
||
975 |