terrylinooo /
psr-http
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * This file is part of the Shieldon package. |
||
| 4 | * |
||
| 5 | * (c) Terry L. <[email protected]> |
||
| 6 | * |
||
| 7 | * For the full copyright and license information, please view the LICENSE |
||
| 8 | * file that was distributed with this source code. |
||
| 9 | */ |
||
| 10 | |||
| 11 | declare(strict_types=1); |
||
| 12 | |||
| 13 | namespace Shieldon\Psr7; |
||
| 14 | |||
| 15 | use Psr\Http\Message\ResponseInterface; |
||
| 16 | use Shieldon\Psr7\Message; |
||
| 17 | use InvalidArgumentException; |
||
| 18 | |||
| 19 | use function gettype; |
||
| 20 | use function is_integer; |
||
| 21 | use function is_string; |
||
| 22 | use function sprintf; |
||
| 23 | use function str_replace; |
||
| 24 | use function strpos; |
||
| 25 | |||
| 26 | /* |
||
| 27 | * Representation of an outgoing, server-side response. |
||
| 28 | */ |
||
| 29 | class Response extends Message implements ResponseInterface |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * HTTP status number. |
||
| 33 | * |
||
| 34 | * @var int |
||
| 35 | */ |
||
| 36 | protected $status; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * HTTP status reason phrase. |
||
| 40 | * |
||
| 41 | * @var string |
||
| 42 | */ |
||
| 43 | protected $reasonPhrase; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * HTTP status codes. |
||
| 47 | * |
||
| 48 | * @see https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
||
| 49 | * |
||
| 50 | * @var array |
||
| 51 | */ |
||
| 52 | protected static $statusCode = [ |
||
| 53 | |||
| 54 | // 1xx: Informational |
||
| 55 | // Request received, continuing process. |
||
| 56 | 100 => 'Continue', |
||
| 57 | 101 => 'Switching Protocols', |
||
| 58 | 102 => 'Processing', |
||
| 59 | |||
| 60 | // 2xx: Success |
||
| 61 | // The action was successfully received, understood, and accepted. |
||
| 62 | 200 => 'OK', |
||
| 63 | 201 => 'Created', |
||
| 64 | 202 => 'Accepted', |
||
| 65 | 203 => 'Non-Authoritative Information', |
||
| 66 | 204 => 'No Content', |
||
| 67 | 205 => 'Reset Content', |
||
| 68 | 206 => 'Partial Content', |
||
| 69 | 207 => 'Multi-status', |
||
| 70 | 208 => 'Already Reported', |
||
| 71 | |||
| 72 | // 3xx: Redirection |
||
| 73 | // Further action must be taken in order to complete the request. |
||
| 74 | 300 => 'Multiple Choices', |
||
| 75 | 301 => 'Moved Permanently', |
||
| 76 | 302 => 'Found', |
||
| 77 | 303 => 'See Other', |
||
| 78 | 304 => 'Not Modified', |
||
| 79 | 305 => 'Use Proxy', |
||
| 80 | 306 => 'Switch Proxy', |
||
| 81 | 307 => 'Temporary Redirect', |
||
| 82 | 308 => 'Permanent Redirect', |
||
| 83 | // => '309-399 Unassigned.' |
||
| 84 | |||
| 85 | // 4xx: Client Error |
||
| 86 | // The request contains bad syntax or cannot be fulfilled. |
||
| 87 | 400 => 'Bad Request', |
||
| 88 | 401 => 'Unauthorized', |
||
| 89 | 402 => 'Payment Required', |
||
| 90 | 403 => 'Forbidden', |
||
| 91 | 404 => 'Not Found', |
||
| 92 | 405 => 'Method Not Allowed', |
||
| 93 | 406 => 'Not Acceptable', |
||
| 94 | 407 => 'Proxy Authentication Required', |
||
| 95 | 408 => 'Request Time-out', |
||
| 96 | 409 => 'Conflict', |
||
| 97 | 410 => 'Gone', |
||
| 98 | 411 => 'Length Required', |
||
| 99 | 412 => 'Precondition Failed', |
||
| 100 | 413 => 'Request Entity Too Large', |
||
| 101 | 414 => 'Request-URI Too Large', |
||
| 102 | 415 => 'Unsupported Media Type', |
||
| 103 | 416 => 'Requested range not satisfiable', |
||
| 104 | 417 => 'Expectation Failed', |
||
| 105 | // => '418-412: Unassigned' |
||
| 106 | 422 => 'Unprocessable Entity', |
||
| 107 | 423 => 'Locked', |
||
| 108 | 424 => 'Failed Dependency', |
||
| 109 | 425 => 'Unordered Collection', |
||
| 110 | 426 => 'Upgrade Required', |
||
| 111 | 428 => 'Precondition Required', |
||
| 112 | 429 => 'Too Many Requests', |
||
| 113 | 431 => 'Request Header Fields Too Large', |
||
| 114 | // => '432-450: Unassigned.' |
||
| 115 | 451 => 'Unavailable For Legal Reasons', |
||
| 116 | // => '452-499: Unassigned.' |
||
| 117 | |||
| 118 | // 5xx: Server Error |
||
| 119 | // The server failed to fulfill an apparently valid request. |
||
| 120 | 500 => 'Internal Server Error', |
||
| 121 | 501 => 'Not Implemented', |
||
| 122 | 502 => 'Bad Gateway', |
||
| 123 | 503 => 'Service Unavailable', |
||
| 124 | 504 => 'Gateway Time-out', |
||
| 125 | 505 => 'HTTP Version not supported', |
||
| 126 | 506 => 'Variant Also Negotiates', |
||
| 127 | 507 => 'Insufficient Storage', |
||
| 128 | 508 => 'Loop Detected', |
||
| 129 | 510 => 'Not Extended', |
||
| 130 | 511 => 'Network Authentication Required', |
||
| 131 | // => '512-599 Unassigned.' |
||
| 132 | ]; |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Response Constructor. |
||
| 136 | * |
||
| 137 | * @param int $status Response HTTP status code. |
||
| 138 | * @param array $headers Response headers. |
||
| 139 | * @param StreamInterface|string $body Response body. |
||
|
0 ignored issues
–
show
|
|||
| 140 | * @param string $version Response protocol version. |
||
| 141 | * @param string $reason Reaspnse HTTP reason phrase. |
||
| 142 | */ |
||
| 143 | 10 | public function __construct( |
|
| 144 | int $status = 200 , |
||
| 145 | array $headers = [] , |
||
| 146 | $body = '' , |
||
| 147 | string $version = '1.1', |
||
| 148 | string $reason = 'OK' |
||
| 149 | ) { |
||
| 150 | 10 | $this->assertStatus($status); |
|
| 151 | 9 | $this->assertReasonPhrase($reason); |
|
| 152 | 9 | $this->assertProtocolVersion($version); |
|
| 153 | |||
| 154 | 9 | $this->setHeaders($headers); |
|
| 155 | 9 | $this->setBody($body); |
|
| 156 | |||
| 157 | 9 | $this->status = $status; |
|
| 158 | 9 | $this->protocolVersion = $version; |
|
| 159 | 9 | $this->reasonPhrase = $reason; |
|
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * {@inheritdoc} |
||
| 164 | */ |
||
| 165 | 4 | public function getStatusCode(): int |
|
| 166 | { |
||
| 167 | 4 | return $this->status; |
|
| 168 | } |
||
| 169 | |||
| 170 | /** |
||
| 171 | * {@inheritdoc} |
||
| 172 | */ |
||
| 173 | 6 | public function withStatus($code, $reasonPhrase = ''): ResponseInterface |
|
| 174 | { |
||
| 175 | 6 | $this->assertStatus($code); |
|
| 176 | 5 | $this->assertReasonPhrase($reasonPhrase); |
|
| 177 | |||
| 178 | 3 | if ($reasonPhrase === '' && isset(self::$statusCode[$code])) { |
|
| 179 | 1 | $reasonPhrase = self::$statusCode[$code]; |
|
| 180 | } |
||
| 181 | |||
| 182 | 3 | $clone = clone $this; |
|
| 183 | 3 | $clone->status = $code; |
|
| 184 | 3 | $clone->reasonPhrase = $reasonPhrase; |
|
| 185 | |||
| 186 | 3 | return $clone; |
|
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * {@inheritdoc} |
||
| 191 | */ |
||
| 192 | 1 | public function getReasonPhrase(): string |
|
| 193 | { |
||
| 194 | 1 | return $this->reasonPhrase; |
|
| 195 | } |
||
| 196 | |||
| 197 | /* |
||
| 198 | |-------------------------------------------------------------------------- |
||
| 199 | | Non PSR-7 Methods. |
||
| 200 | |-------------------------------------------------------------------------- |
||
| 201 | */ |
||
| 202 | |||
| 203 | /** |
||
| 204 | * Throw exception when the HTTP status code is not valid. |
||
| 205 | * |
||
| 206 | * @param int $code HTTP status code. |
||
| 207 | * |
||
| 208 | * @return void |
||
| 209 | * |
||
| 210 | * @throws InvalidArgumentException |
||
| 211 | */ |
||
| 212 | 10 | protected function assertStatus($code) |
|
| 213 | { |
||
| 214 | 10 | if (!is_integer($code)) { |
|
|
0 ignored issues
–
show
|
|||
| 215 | 1 | throw new InvalidArgumentException( |
|
| 216 | 1 | sprintf( |
|
| 217 | 1 | 'Status code should be an integer value, but "%s" provided.', |
|
| 218 | 1 | gettype($code) |
|
| 219 | 1 | ) |
|
| 220 | 1 | ); |
|
| 221 | } |
||
| 222 | |||
| 223 | 10 | if (!($code > 100 && $code < 599)) { |
|
| 224 | 1 | throw new InvalidArgumentException( |
|
| 225 | 1 | sprintf( |
|
| 226 | 1 | 'Status code should be in a range of 100-599, but "%s" provided.', |
|
| 227 | 1 | $code |
|
| 228 | 1 | ) |
|
| 229 | 1 | ); |
|
| 230 | } |
||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * Throw exception when the HTTP reason phrase is not valid. |
||
| 235 | * |
||
| 236 | * @param string $reasonPhrase |
||
| 237 | * |
||
| 238 | * @return void |
||
| 239 | * |
||
| 240 | * @throws InvalidArgumentException |
||
| 241 | */ |
||
| 242 | 9 | protected function assertReasonPhrase($reasonPhrase) |
|
| 243 | { |
||
| 244 | 9 | if ($reasonPhrase === '') { |
|
| 245 | 1 | return; |
|
| 246 | } |
||
| 247 | |||
| 248 | 9 | if (!is_string($reasonPhrase)) { |
|
|
0 ignored issues
–
show
|
|||
| 249 | 1 | throw new InvalidArgumentException( |
|
| 250 | 1 | sprintf( |
|
| 251 | 1 | 'Reason phrase must be a string, but "%s" provided.', |
|
| 252 | 1 | gettype($reasonPhrase) |
|
| 253 | 1 | ) |
|
| 254 | 1 | ); |
|
| 255 | } |
||
| 256 | |||
| 257 | // Special characters, such as "line breaks", "tab" and others... |
||
| 258 | 9 | $escapeCharacters = [ |
|
| 259 | 9 | '\f', '\r', '\n', '\t', '\v', '\0', '[\b]', '\s', '\S', '\w', '\W', '\d', '\D', '\b', '\B', '\cX', '\xhh', '\uhhhh' |
|
| 260 | 9 | ]; |
|
| 261 | |||
| 262 | 9 | $filteredPhrase = str_replace($escapeCharacters, '', $reasonPhrase); |
|
| 263 | |||
| 264 | 9 | if ($reasonPhrase !== $filteredPhrase) { |
|
| 265 | 1 | foreach ($escapeCharacters as $escape) { |
|
| 266 | 1 | if (strpos($reasonPhrase, $escape) !== false) { |
|
| 267 | 1 | throw new InvalidArgumentException( |
|
| 268 | 1 | sprintf( |
|
| 269 | 1 | 'Reason phrase contains "%s" that is considered as a prohibited character.', |
|
| 270 | 1 | $escape |
|
| 271 | 1 | ) |
|
| 272 | 1 | ); |
|
| 273 | } |
||
| 274 | } |
||
| 275 | } |
||
| 276 | } |
||
| 277 | } |
||
| 278 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths