1<?php 2 3declare(strict_types=1); 4 5namespace GuzzleHttp\Psr7; 6 7use Psr\Http\Message\ResponseInterface; 8use Psr\Http\Message\StreamInterface; 9 10/** 11 * PSR-7 response implementation. 12 */ 13class Response implements ResponseInterface 14{ 15 use MessageTrait; 16 17 /** Map of standard HTTP status code/reason phrases */ 18 private const PHRASES = [ 19 100 => 'Continue', 20 101 => 'Switching Protocols', 21 102 => 'Processing', 22 200 => 'OK', 23 201 => 'Created', 24 202 => 'Accepted', 25 203 => 'Non-Authoritative Information', 26 204 => 'No Content', 27 205 => 'Reset Content', 28 206 => 'Partial Content', 29 207 => 'Multi-status', 30 208 => 'Already Reported', 31 300 => 'Multiple Choices', 32 301 => 'Moved Permanently', 33 302 => 'Found', 34 303 => 'See Other', 35 304 => 'Not Modified', 36 305 => 'Use Proxy', 37 306 => 'Switch Proxy', 38 307 => 'Temporary Redirect', 39 308 => 'Permanent Redirect', 40 400 => 'Bad Request', 41 401 => 'Unauthorized', 42 402 => 'Payment Required', 43 403 => 'Forbidden', 44 404 => 'Not Found', 45 405 => 'Method Not Allowed', 46 406 => 'Not Acceptable', 47 407 => 'Proxy Authentication Required', 48 408 => 'Request Time-out', 49 409 => 'Conflict', 50 410 => 'Gone', 51 411 => 'Length Required', 52 412 => 'Precondition Failed', 53 413 => 'Request Entity Too Large', 54 414 => 'Request-URI Too Large', 55 415 => 'Unsupported Media Type', 56 416 => 'Requested range not satisfiable', 57 417 => 'Expectation Failed', 58 418 => 'I\'m a teapot', 59 422 => 'Unprocessable Entity', 60 423 => 'Locked', 61 424 => 'Failed Dependency', 62 425 => 'Unordered Collection', 63 426 => 'Upgrade Required', 64 428 => 'Precondition Required', 65 429 => 'Too Many Requests', 66 431 => 'Request Header Fields Too Large', 67 451 => 'Unavailable For Legal Reasons', 68 500 => 'Internal Server Error', 69 501 => 'Not Implemented', 70 502 => 'Bad Gateway', 71 503 => 'Service Unavailable', 72 504 => 'Gateway Time-out', 73 505 => 'HTTP Version not supported', 74 506 => 'Variant Also Negotiates', 75 507 => 'Insufficient Storage', 76 508 => 'Loop Detected', 77 510 => 'Not Extended', 78 511 => 'Network Authentication Required', 79 ]; 80 81 /** @var string */ 82 private $reasonPhrase; 83 84 /** @var int */ 85 private $statusCode; 86 87 /** 88 * @param int $status Status code 89 * @param (string|string[])[] $headers Response headers 90 * @param string|resource|StreamInterface|null $body Response body 91 * @param string $version Protocol version 92 * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) 93 */ 94 public function __construct( 95 int $status = 200, 96 array $headers = [], 97 $body = null, 98 string $version = '1.1', 99 string $reason = null 100 ) { 101 $this->assertStatusCodeRange($status); 102 103 $this->statusCode = $status; 104 105 if ($body !== '' && $body !== null) { 106 $this->stream = Utils::streamFor($body); 107 } 108 109 $this->setHeaders($headers); 110 if ($reason == '' && isset(self::PHRASES[$this->statusCode])) { 111 $this->reasonPhrase = self::PHRASES[$this->statusCode]; 112 } else { 113 $this->reasonPhrase = (string) $reason; 114 } 115 116 $this->protocol = $version; 117 } 118 119 public function getStatusCode(): int 120 { 121 return $this->statusCode; 122 } 123 124 public function getReasonPhrase(): string 125 { 126 return $this->reasonPhrase; 127 } 128 129 public function withStatus($code, $reasonPhrase = ''): ResponseInterface 130 { 131 $this->assertStatusCodeIsInteger($code); 132 $code = (int) $code; 133 $this->assertStatusCodeRange($code); 134 135 $new = clone $this; 136 $new->statusCode = $code; 137 if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) { 138 $reasonPhrase = self::PHRASES[$new->statusCode]; 139 } 140 $new->reasonPhrase = (string) $reasonPhrase; 141 142 return $new; 143 } 144 145 /** 146 * @param mixed $statusCode 147 */ 148 private function assertStatusCodeIsInteger($statusCode): void 149 { 150 if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) { 151 throw new \InvalidArgumentException('Status code must be an integer value.'); 152 } 153 } 154 155 private function assertStatusCodeRange(int $statusCode): void 156 { 157 if ($statusCode < 100 || $statusCode >= 600) { 158 throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); 159 } 160 } 161} 162