spiral /
roadrunner
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 | |||
| 3 | /** |
||
| 4 | * High-performance PHP process supervisor and load balancer written in Go |
||
| 5 | * |
||
| 6 | * @author Wolfy-J |
||
| 7 | */ |
||
| 8 | declare(strict_types=1); |
||
| 9 | |||
| 10 | namespace Spiral\RoadRunner; |
||
| 11 | |||
| 12 | use Psr\Http\Message\ResponseInterface; |
||
| 13 | use Psr\Http\Message\ServerRequestFactoryInterface; |
||
| 14 | use Psr\Http\Message\ServerRequestInterface; |
||
| 15 | use Psr\Http\Message\StreamFactoryInterface; |
||
| 16 | use Psr\Http\Message\UploadedFileFactoryInterface; |
||
| 17 | use Psr\Http\Message\UploadedFileInterface; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Manages PSR-7 request and response. |
||
| 21 | */ |
||
| 22 | class PSR7Client |
||
| 23 | { |
||
| 24 | /** @var HttpClient */ |
||
| 25 | private $httpClient; |
||
| 26 | |||
| 27 | /** @var ServerRequestFactoryInterface */ |
||
| 28 | private $requestFactory; |
||
| 29 | |||
| 30 | /** @var StreamFactoryInterface */ |
||
| 31 | private $streamFactory; |
||
| 32 | |||
| 33 | /** @var UploadedFileFactoryInterface */ |
||
| 34 | private $uploadsFactory; |
||
| 35 | |||
| 36 | /** @var mixed[] */ |
||
| 37 | private $originalServer = []; |
||
| 38 | |||
| 39 | /** @var string[] Valid values for HTTP protocol version */ |
||
| 40 | private static $allowedVersions = ['1.0', '1.1', '2',]; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @param Worker $worker |
||
| 44 | * @param ServerRequestFactoryInterface|null $requestFactory |
||
| 45 | * @param StreamFactoryInterface|null $streamFactory |
||
| 46 | * @param UploadedFileFactoryInterface|null $uploadsFactory |
||
| 47 | */ |
||
| 48 | public function __construct( |
||
| 49 | Worker $worker, |
||
| 50 | ServerRequestFactoryInterface $requestFactory = null, |
||
| 51 | StreamFactoryInterface $streamFactory = null, |
||
| 52 | UploadedFileFactoryInterface $uploadsFactory = null |
||
| 53 | ) { |
||
| 54 | $this->httpClient = new HttpClient($worker); |
||
| 55 | $this->requestFactory = $requestFactory ?? new Diactoros\ServerRequestFactory(); |
||
| 56 | $this->streamFactory = $streamFactory ?? new Diactoros\StreamFactory(); |
||
| 57 | $this->uploadsFactory = $uploadsFactory ?? new Diactoros\UploadedFileFactory(); |
||
| 58 | $this->originalServer = $_SERVER; |
||
| 59 | } |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @return Worker |
||
| 63 | */ |
||
| 64 | public function getWorker(): Worker |
||
| 65 | { |
||
| 66 | return $this->httpClient->getWorker(); |
||
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | * @return ServerRequestInterface|null |
||
| 71 | */ |
||
| 72 | public function acceptRequest(): ?ServerRequestInterface |
||
| 73 | { |
||
| 74 | $rawRequest = $this->httpClient->acceptRequest(); |
||
| 75 | if ($rawRequest === null) { |
||
| 76 | return null; |
||
| 77 | } |
||
| 78 | |||
| 79 | $_SERVER = $this->configureServer($rawRequest['ctx']); |
||
| 80 | |||
| 81 | $request = $this->requestFactory->createServerRequest( |
||
| 82 | $rawRequest['ctx']['method'], |
||
| 83 | $rawRequest['ctx']['uri'], |
||
| 84 | $_SERVER |
||
| 85 | ); |
||
| 86 | |||
| 87 | parse_str($rawRequest['ctx']['rawQuery'], $query); |
||
| 88 | |||
| 89 | $request = $request |
||
| 90 | ->withProtocolVersion(static::fetchProtocolVersion($rawRequest['ctx']['protocol'])) |
||
|
0 ignored issues
–
show
|
|||
| 91 | ->withCookieParams($rawRequest['ctx']['cookies']) |
||
| 92 | ->withQueryParams($query) |
||
| 93 | ->withUploadedFiles($this->wrapUploads($rawRequest['ctx']['uploads'])); |
||
| 94 | |||
| 95 | foreach ($rawRequest['ctx']['attributes'] as $name => $value) { |
||
| 96 | $request = $request->withAttribute($name, $value); |
||
| 97 | } |
||
| 98 | |||
| 99 | foreach ($rawRequest['ctx']['headers'] as $name => $value) { |
||
| 100 | $request = $request->withHeader($name, $value); |
||
| 101 | } |
||
| 102 | |||
| 103 | if ($rawRequest['ctx']['parsed']) { |
||
| 104 | return $request->withParsedBody(json_decode($rawRequest['body'], true)); |
||
| 105 | } |
||
| 106 | |||
| 107 | if ($rawRequest['body'] !== null) { |
||
| 108 | return $request->withBody($this->streamFactory->createStream($rawRequest['body'])); |
||
|
0 ignored issues
–
show
It seems like
$rawRequest['body'] can also be of type object<Error>; however, Psr\Http\Message\StreamF...terface::createStream() does only seem to accept string, maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. Loading history...
|
|||
| 109 | } |
||
| 110 | |||
| 111 | return $request; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Send response to the application server. |
||
| 116 | * |
||
| 117 | * @param ResponseInterface $response |
||
| 118 | */ |
||
| 119 | public function respond(ResponseInterface $response): void |
||
| 120 | { |
||
| 121 | $this->httpClient->respond( |
||
| 122 | $response->getStatusCode(), |
||
| 123 | $response->getBody()->__toString(), |
||
| 124 | $response->getHeaders() |
||
| 125 | ); |
||
| 126 | } |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Returns altered copy of _SERVER variable. Sets ip-address, |
||
| 130 | * request-time and other values. |
||
| 131 | * |
||
| 132 | * @param mixed[] $ctx |
||
| 133 | * @return mixed[] |
||
| 134 | */ |
||
| 135 | protected function configureServer(array $ctx): array |
||
| 136 | { |
||
| 137 | $server = $this->originalServer; |
||
| 138 | |||
| 139 | $server['REQUEST_URI'] = $ctx['uri']; |
||
| 140 | $server['REQUEST_TIME'] = time(); |
||
| 141 | $server['REQUEST_TIME_FLOAT'] = microtime(true); |
||
| 142 | $server['REMOTE_ADDR'] = $ctx['attributes']['ipAddress'] ?? $ctx['remoteAddr'] ?? '127.0.0.1'; |
||
| 143 | $server['REQUEST_METHOD'] = $ctx['method']; |
||
| 144 | |||
| 145 | $server['HTTP_USER_AGENT'] = ''; |
||
| 146 | foreach ($ctx['headers'] as $key => $value) { |
||
| 147 | $key = strtoupper(str_replace('-', '_', $key)); |
||
| 148 | if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH'])) { |
||
| 149 | $server[$key] = implode(', ', $value); |
||
| 150 | } else { |
||
| 151 | $server['HTTP_' . $key] = implode(', ', $value); |
||
| 152 | } |
||
| 153 | } |
||
| 154 | |||
| 155 | return $server; |
||
| 156 | } |
||
| 157 | |||
| 158 | /** |
||
| 159 | * Wraps all uploaded files with UploadedFile. |
||
| 160 | * |
||
| 161 | * @param array[] $files |
||
| 162 | * |
||
| 163 | * @return UploadedFileInterface[]|mixed[] |
||
| 164 | */ |
||
| 165 | private function wrapUploads($files): array |
||
| 166 | { |
||
| 167 | if (empty($files)) { |
||
| 168 | return []; |
||
| 169 | } |
||
| 170 | |||
| 171 | $result = []; |
||
| 172 | foreach ($files as $index => $f) { |
||
| 173 | if (!isset($f['name'])) { |
||
| 174 | $result[$index] = $this->wrapUploads($f); |
||
| 175 | continue; |
||
| 176 | } |
||
| 177 | |||
| 178 | if (UPLOAD_ERR_OK === $f['error']) { |
||
| 179 | $stream = $this->streamFactory->createStreamFromFile($f['tmpName']); |
||
| 180 | } else { |
||
| 181 | $stream = $this->streamFactory->createStream(); |
||
| 182 | } |
||
| 183 | |||
| 184 | $result[$index] = $this->uploadsFactory->createUploadedFile( |
||
| 185 | $stream, |
||
| 186 | $f['size'], |
||
| 187 | $f['error'], |
||
| 188 | $f['name'], |
||
| 189 | $f['mime'] |
||
| 190 | ); |
||
| 191 | } |
||
| 192 | |||
| 193 | return $result; |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * Normalize HTTP protocol version to valid values |
||
| 198 | * |
||
| 199 | * @param string $version |
||
| 200 | * @return string |
||
| 201 | */ |
||
| 202 | private static function fetchProtocolVersion(string $version): string |
||
| 203 | { |
||
| 204 | $v = substr($version, 5); |
||
| 205 | |||
| 206 | if ($v === '2.0') { |
||
| 207 | return '2'; |
||
| 208 | } |
||
| 209 | |||
| 210 | // Fallback for values outside of valid protocol versions |
||
| 211 | if (!in_array($v, static::$allowedVersions, true)) { |
||
| 212 | return '1.1'; |
||
| 213 | } |
||
| 214 | |||
| 215 | return $v; |
||
| 216 | } |
||
| 217 | } |
||
| 218 |
Let’s assume you have a class which uses late-static binding:
}
The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the
getSomeVariable()on that sub-class, you will receive a runtime error:In the case above, it makes sense to update
SomeClassto useselfinstead: