Response.php 4.8 KB

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