33namespace React \Http \Message ;
44
55use Fig \Http \Message \StatusCodeInterface ;
6+ use Psr \Http \Message \ResponseInterface ;
67use Psr \Http \Message \StreamInterface ;
8+ use React \Http \Io \AbstractMessage ;
79use React \Http \Io \BufferedBody ;
810use React \Http \Io \HttpBodyStream ;
911use React \Stream \ReadableStreamInterface ;
10- use RingCentral \Psr7 \Response as Psr7Response ;
1112
1213/**
1314 * Represents an outgoing server response message.
3435 * `404 Not Found` status codes can used as `Response::STATUS_OK` and
3536 * `Response::STATUS_NOT_FOUND` respectively.
3637 *
37- * > Internally, this implementation builds on top of an existing incoming
38- * response message and only adds required streaming support. This base class is
38+ * > Internally, this implementation builds on top a base class which is
3939 * considered an implementation detail that may change in the future.
4040 *
4141 * @see \Psr\Http\Message\ResponseInterface
4242 */
43- final class Response extends Psr7Response implements StatusCodeInterface
43+ final class Response extends AbstractMessage implements ResponseInterface, StatusCodeInterface
4444{
4545 /**
4646 * Create an HTML response
@@ -257,6 +257,41 @@ public static function xml($xml)
257257 return new self (self ::STATUS_OK , array ('Content-Type ' => 'application/xml ' ), $ xml );
258258 }
259259
260+ /**
261+ * @var bool
262+ * @see self::$phrasesMap
263+ */
264+ private static $ phrasesInitialized = false ;
265+
266+ /**
267+ * Map of standard HTTP status codes to standard reason phrases.
268+ *
269+ * This map will be fully populated with all standard reason phrases on
270+ * first access. By default, it only contains a subset of HTTP status codes
271+ * that have a custom mapping to reason phrases (such as those with dashes
272+ * and all caps words). See `self::STATUS_*` for all possible status code
273+ * constants.
274+ *
275+ * @var array<int,string>
276+ * @see self::STATUS_*
277+ * @see self::getReasonPhraseForStatusCode()
278+ */
279+ private static $ phrasesMap = array (
280+ 200 => 'OK ' ,
281+ 203 => 'Non-Authoritative Information ' ,
282+ 207 => 'Multi-Status ' ,
283+ 226 => 'IM Used ' ,
284+ 414 => 'URI Too Large ' ,
285+ 418 => 'I \'m a teapot ' ,
286+ 505 => 'HTTP Version Not Supported '
287+ );
288+
289+ /** @var int */
290+ private $ statusCode ;
291+
292+ /** @var string */
293+ private $ reasonPhrase ;
294+
260295 /**
261296 * @param int $status HTTP status code (e.g. 200/404), see `self::STATUS_*` constants
262297 * @param array<string,string|string[]> $headers additional response headers
@@ -280,12 +315,58 @@ public function __construct(
280315 throw new \InvalidArgumentException ('Invalid response body given ' );
281316 }
282317
283- parent ::__construct (
284- $ status ,
285- $ headers ,
286- $ body ,
287- $ version ,
288- $ reason
289- );
318+ parent ::__construct ($ version , $ headers , $ body );
319+
320+ $ this ->statusCode = (int ) $ status ;
321+ $ this ->reasonPhrase = ($ reason !== '' && $ reason !== null ) ? (string ) $ reason : self ::getReasonPhraseForStatusCode ($ status );
322+ }
323+
324+ public function getStatusCode ()
325+ {
326+ return $ this ->statusCode ;
327+ }
328+
329+ public function withStatus ($ code , $ reasonPhrase = '' )
330+ {
331+ if ((string ) $ reasonPhrase === '' ) {
332+ $ reasonPhrase = self ::getReasonPhraseForStatusCode ($ code );
333+ }
334+
335+ if ($ this ->statusCode === (int ) $ code && $ this ->reasonPhrase === (string ) $ reasonPhrase ) {
336+ return $ this ;
337+ }
338+
339+ $ response = clone $ this ;
340+ $ response ->statusCode = (int ) $ code ;
341+ $ response ->reasonPhrase = (string ) $ reasonPhrase ;
342+
343+ return $ response ;
344+ }
345+
346+ public function getReasonPhrase ()
347+ {
348+ return $ this ->reasonPhrase ;
349+ }
350+
351+ /**
352+ * @param int $code
353+ * @return string default reason phrase for given status code or empty string if unknown
354+ */
355+ private static function getReasonPhraseForStatusCode ($ code )
356+ {
357+ if (!self ::$ phrasesInitialized ) {
358+ self ::$ phrasesInitialized = true ;
359+
360+ // map all `self::STATUS_` constants from status code to reason phrase
361+ // e.g. `self::STATUS_NOT_FOUND = 404` will be mapped to `404 Not Found`
362+ $ ref = new \ReflectionClass (__CLASS__ );
363+ foreach ($ ref ->getConstants () as $ name => $ value ) {
364+ if (!isset (self ::$ phrasesMap [$ value ]) && \strpos ($ name , 'STATUS_ ' ) === 0 ) {
365+ self ::$ phrasesMap [$ value ] = \ucwords (\strtolower (\str_replace ('_ ' , ' ' , \substr ($ name , 7 ))));
366+ }
367+ }
368+ }
369+
370+ return isset (self ::$ phrasesMap [$ code ]) ? self ::$ phrasesMap [$ code ] : '' ;
290371 }
291372}
0 commit comments