diff --git a/README.md b/README.md
index 50797fe6..23e4b5fa 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,10 @@ multiple concurrent HTTP requests without blocking.
* [withResponseBuffer()](#withresponsebuffer)
* [React\Http\Message](#reacthttpmessage)
* [Response](#response)
+ * [html()](#html)
+ * [json()](#json)
+ * [plaintext()](#plaintext)
+ * [xml()](#xml)
* [ServerRequest](#serverrequest)
* [ResponseException](#responseexception)
* [React\Http\Middleware](#reacthttpmiddleware)
@@ -108,11 +112,7 @@ This is an HTTP server which responds with `Hello World!` to every request.
require __DIR__ . '/vendor/autoload.php';
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
@@ -734,11 +734,7 @@ object and expects a [response](#server-response) object in return:
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
@@ -949,14 +945,10 @@ and will be passed to the callback function like this.
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- $body = "The method of the request is: " . $request->getMethod();
- $body .= "The requested path is: " . $request->getUri()->getPath();
+ $body = "The method of the request is: " . $request->getMethod() . "\n";
+ $body .= "The requested path is: " . $request->getUri()->getPath() . "\n";
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
$body
);
});
@@ -992,13 +984,9 @@ The following parameters are currently available:
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'];
+ $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'] . "\n";
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
$body
);
});
@@ -1026,11 +1014,7 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf
$body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']);
}
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/html'
- ),
+ return React\Http\Message\Response::html(
$body
);
});
@@ -1073,9 +1057,7 @@ request headers (commonly used for `POST` requests for HTML form submission data
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
$name = $request->getParsedBody()['name'] ?? 'anonymous';
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(),
+ return React\Http\Message\Response::plaintext(
"Hello $name!\n"
);
});
@@ -1098,10 +1080,8 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf
$data = json_decode((string)$request->getBody());
$name = $data->name ?? 'anonymous';
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array('Content-Type' => 'application/json'),
- json_encode(['message' => "Hello $name!"])
+ return React\Http\Message\Response::json(
+ ['message' => "Hello $name!"]
);
});
```
@@ -1121,9 +1101,7 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf
$files = $request->getUploadedFiles();
$name = isset($files['avatar']) ? $files['avatar']->getClientFilename() : 'nothing';
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(),
+ return React\Http\Message\Response::plaintext(
"Uploaded $name\n"
);
});
@@ -1204,24 +1182,16 @@ $http = new React\Http\HttpServer(
});
$body->on('end', function () use ($resolve, &$bytes){
- $resolve(new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ $resolve(React\Http\Message\Response::plaintext(
"Received $bytes bytes\n"
));
});
// an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
$body->on('error', function (Exception $e) use ($resolve, &$bytes) {
- $resolve(new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_BAD_REQUEST,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ $resolve(React\Http\Message\Response::plaintext(
"Encountered error after $bytes bytes: {$e->getMessage()}\n"
- ));
+ )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST));
});
});
}
@@ -1268,23 +1238,15 @@ $http = new React\Http\HttpServer(
function (Psr\Http\Message\ServerRequestInterface $request) {
$size = $request->getBody()->getSize();
if ($size === null) {
- $body = 'The request does not contain an explicit length.';
- $body .= 'This example does not accept chunked transfer encoding.';
-
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_LENGTH_REQUIRED,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ $body = "The request does not contain an explicit length. ";
+ $body .= "This example does not accept chunked transfer encoding.\n";
+
+ return React\Http\Message\Response::plaintext(
$body
- );
+ )->withStatus(React\Http\Message\Response::STATUS_LENGTH_REQUIRED);
}
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
"Request body size: " . $size . " bytes\n"
);
}
@@ -1340,25 +1302,16 @@ $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterf
$key = 'react\php';
if (isset($request->getCookieParams()[$key])) {
- $body = "Your cookie value is: " . $request->getCookieParams()[$key];
+ $body = "Your cookie value is: " . $request->getCookieParams()[$key] . "\n";
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
$body
);
}
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain',
- 'Set-Cookie' => urlencode($key) . '=' . urlencode('test;more')
- ),
- "Your cookie has been set."
- );
+ return React\Http\Message\Response::plaintext(
+ "Your cookie has been set.\n"
+ )->withHeader('Set-Cookie', urlencode($key) . '=' . urlencode('test;more'));
});
```
@@ -1409,11 +1362,7 @@ In its most simple form, you can use it like this:
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
@@ -1437,18 +1386,17 @@ This example shows how such a long-term action could look like:
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- return new Promise(function ($resolve, $reject) {
+ $promise = new Promise(function ($resolve, $reject) {
Loop::addTimer(1.5, function() use ($resolve) {
- $response = new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
- "Hello world"
- );
- $resolve($response);
+ $resolve();
});
});
+
+ return $promise->then(function () {
+ return React\Http\Message\Response::plaintext(
+ "Hello World!"
+ );
+ });
});
```
@@ -1567,11 +1515,7 @@ a `string` response body like this:
```php
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
@@ -1841,11 +1785,9 @@ $http = new React\Http\HttpServer(
$resolve($next($request));
});
return $promise->then(null, function (Exception $e) {
- return new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_INTERNAL_SERVER_ERROR,
- array(),
- 'Internal error: ' . $e->getMessage()
- );
+ return React\Http\Message\Response::plaintext(
+ 'Internal error: ' . $e->getMessage() . "\n"
+ )->withStatus(React\Http\Message\Response::STATUS_INTERNAL_SERVER_ERROR);
});
},
function (Psr\Http\Message\ServerRequestInterface $request) {
@@ -2463,6 +2405,183 @@ constants with the `STATUS_*` prefix. For instance, the `200 OK` and
response message and only adds required streaming support. This base class is
considered an implementation detail that may change in the future.
+##### html()
+
+The static `html(string $html): Response` method can be used to
+create an HTML response.
+
+```php
+$html = <<
+
+
Hello wörld!
+
+
+HTML;
+
+$response = React\Http\Message\Response::html($html);
+```
+
+This is a convenient shortcut method that returns the equivalent of this:
+
+```
+$response = new React\Http\Message\Response(
+ React\Http\Message\Response::STATUS_OK,
+ [
+ 'Content-Type' => 'text/html; charset=utf-8'
+ ],
+ $html
+);
+```
+
+This method always returns a response with a `200 OK` status code and
+the appropriate `Content-Type` response header for the given HTTP source
+string encoded in UTF-8 (Unicode). It's generally recommended to end the
+given plaintext string with a trailing newline.
+
+If you want to use a different status code or custom HTTP response
+headers, you can manipulate the returned response object using the
+provided PSR-7 methods or directly instantiate a custom HTTP response
+object using the `Response` constructor:
+
+```php
+$response = React\Http\Message\Response::html(
+ "Error
\nInvalid user name given.
\n"
+)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+```
+
+##### json()
+
+The static `json(mixed $data): Response` method can be used to
+create a JSON response.
+
+```php
+$response = React\Http\Message\Response::json(['name' => 'Alice']);
+```
+
+This is a convenient shortcut method that returns the equivalent of this:
+
+```
+$response = new React\Http\Message\Response(
+ React\Http\Message\Response::STATUS_OK,
+ [
+ 'Content-Type' => 'application/json'
+ ],
+ json_encode(
+ ['name' => 'Alice'],
+ JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION
+ ) . "\n"
+);
+```
+
+This method always returns a response with a `200 OK` status code and
+the appropriate `Content-Type` response header for the given structured
+data encoded as a JSON text.
+
+The given structured data will be encoded as a JSON text. Any `string`
+values in the data must be encoded in UTF-8 (Unicode). If the encoding
+fails, this method will throw an `InvalidArgumentException`.
+
+By default, the given structured data will be encoded with the flags as
+shown above. This includes pretty printing (PHP 5.4+) and preserving
+zero fractions for `float` values (PHP 5.6.6+) to ease debugging. It is
+assumed any additional data overhead is usually compensated by using HTTP
+response compression.
+
+If you want to use a different status code or custom HTTP response
+headers, you can manipulate the returned response object using the
+provided PSR-7 methods or directly instantiate a custom HTTP response
+object using the `Response` constructor:
+
+```php
+$response = React\Http\Message\Response::json(
+ ['error' => 'Invalid user name given']
+)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+```
+
+##### plaintext()
+
+The static `plaintext(string $text): Response` method can be used to
+create a plaintext response.
+
+```php
+$response = React\Http\Message\Response::plaintext("Hello wörld!\n");
+```
+
+This is a convenient shortcut method that returns the equivalent of this:
+
+```
+$response = new React\Http\Message\Response(
+ React\Http\Message\Response::STATUS_OK,
+ [
+ 'Content-Type' => 'text/plain; charset=utf-8'
+ ],
+ "Hello wörld!\n"
+);
+```
+
+This method always returns a response with a `200 OK` status code and
+the appropriate `Content-Type` response header for the given plaintext
+string encoded in UTF-8 (Unicode). It's generally recommended to end the
+given plaintext string with a trailing newline.
+
+If you want to use a different status code or custom HTTP response
+headers, you can manipulate the returned response object using the
+provided PSR-7 methods or directly instantiate a custom HTTP response
+object using the `Response` constructor:
+
+```php
+$response = React\Http\Message\Response::plaintext(
+ "Error: Invalid user name given.\n"
+)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+```
+
+##### xml()
+
+The static `xml(string $xml): Response` method can be used to
+create an XML response.
+
+```php
+$xml = <<
+
+ Hello wörld!
+
+
+XML;
+
+$response = React\Http\Message\Response::xml($xml);
+```
+
+This is a convenient shortcut method that returns the equivalent of this:
+
+```
+$response = new React\Http\Message\Response(
+ React\Http\Message\Response::STATUS_OK,
+ [
+ 'Content-Type' => 'application/xml'
+ ],
+ $xml
+);
+```
+
+This method always returns a response with a `200 OK` status code and
+the appropriate `Content-Type` response header for the given XML source
+string. It's generally recommended to use UTF-8 (Unicode) and specify
+this as part of the leading XML declaration and to end the given XML
+source string with a trailing newline.
+
+If you want to use a different status code or custom HTTP response
+headers, you can manipulate the returned response object using the
+provided PSR-7 methods or directly instantiate a custom HTTP response
+object using the `Response` constructor:
+
+```php
+$response = React\Http\Message\Response::xml(
+ "Invalid user name given.\n"
+)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+```
+
#### ServerRequest
The `React\Http\Message\ServerRequest` class can be used to
diff --git a/examples/51-server-hello-world.php b/examples/51-server-hello-world.php
index 88831525..9ff84eee 100644
--- a/examples/51-server-hello-world.php
+++ b/examples/51-server-hello-world.php
@@ -1,17 +1,10 @@
'text/plain'
- ),
- "Hello world\n"
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
+ return React\Http\Message\Response::plaintext(
+ "Hello World!\n"
);
});
diff --git a/examples/52-server-count-visitors.php b/examples/52-server-count-visitors.php
index bdd53af9..341f9498 100644
--- a/examples/52-server-count-visitors.php
+++ b/examples/52-server-count-visitors.php
@@ -1,17 +1,10 @@
'text/plain'
- ),
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) use (&$counter) {
+ return React\Http\Message\Response::plaintext(
"Welcome number " . ++$counter . "!\n"
);
});
diff --git a/examples/53-server-whatsmyip.php b/examples/53-server-whatsmyip.php
index e0835a82..1e394b9e 100644
--- a/examples/53-server-whatsmyip.php
+++ b/examples/53-server-whatsmyip.php
@@ -1,18 +1,11 @@
getServerParams()['REMOTE_ADDR'];
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
+ $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'] . "\n";
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
$body
);
});
diff --git a/examples/54-server-query-parameter.php b/examples/54-server-query-parameter.php
index 18dd56b0..9b2d5749 100644
--- a/examples/54-server-query-parameter.php
+++ b/examples/54-server-query-parameter.php
@@ -1,11 +1,8 @@
getQueryParams();
$body = 'The query parameter "foo" is not set. Click the following link ';
@@ -15,11 +12,7 @@
$body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']);
}
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/html'
- ),
+ return React\Http\Message\Response::html(
$body
);
});
diff --git a/examples/55-server-cookie-handling.php b/examples/55-server-cookie-handling.php
index 8260fc33..796da24d 100644
--- a/examples/55-server-cookie-handling.php
+++ b/examples/55-server-cookie-handling.php
@@ -1,33 +1,21 @@
getCookieParams()[$key])) {
- $body = "Your cookie value is: " . $request->getCookieParams()[$key];
+ $body = "Your cookie value is: " . $request->getCookieParams()[$key] . "\n";
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ return React\Http\Message\Response::plaintext(
$body
);
}
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain',
- 'Set-Cookie' => urlencode($key) . '=' . urlencode('test;more')
- ),
- "Your cookie has been set."
- );
+ return React\Http\Message\Response::plaintext(
+ "Your cookie has been set.\n"
+ )->withHeader('Set-Cookie', urlencode($key) . '=' . urlencode('test;more'));
});
$socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0');
diff --git a/examples/56-server-sleep.php b/examples/56-server-sleep.php
index 6bb6f82b..2a3c9027 100644
--- a/examples/56-server-sleep.php
+++ b/examples/56-server-sleep.php
@@ -1,25 +1,21 @@
'text/plain'
- ),
- "Hello world"
- );
- $resolve($response);
+ $resolve();
});
});
+
+ return $promise->then(function () {
+ return React\Http\Message\Response::plaintext(
+ "Hello world!\n"
+ );
+ });
});
$socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0');
diff --git a/examples/57-server-error-handling.php b/examples/57-server-error-handling.php
index 71cbad15..a9fb6bad 100644
--- a/examples/57-server-error-handling.php
+++ b/examples/57-server-error-handling.php
@@ -1,30 +1,18 @@
'text/plain'
- ),
- "Hello World!\n"
- );
+ if ($count % 2 === 0) {
+ throw new Exception('Second call');
+ }
- $resolve($response);
- });
+ return React\Http\Message\Response::plaintext(
+ "Hello World!\n"
+ );
});
$socket = new React\Socket\SocketServer(isset($argv[1]) ? $argv[1] : '0.0.0.0:0');
diff --git a/examples/58-server-stream-response.php b/examples/58-server-stream-response.php
index 015ddd9a..cf65a3bf 100644
--- a/examples/58-server-stream-response.php
+++ b/examples/58-server-stream-response.php
@@ -1,13 +1,12 @@
getMethod() !== 'GET' || $request->getUri()->getPath() !== '/') {
return new Response(Response::STATUS_NOT_FOUND);
}
diff --git a/examples/59-server-json-api.php b/examples/59-server-json-api.php
index 0d50b52b..f48be7e3 100644
--- a/examples/59-server-json-api.php
+++ b/examples/59-server-json-api.php
@@ -6,49 +6,32 @@
// $ php examples/59-server-json-api.php 8080
// $ curl -v http://localhost:8080/ -H 'Content-Type: application/json' -d '{"name":"Alice"}'
-use Psr\Http\Message\ServerRequestInterface;
use React\Http\Message\Response;
require __DIR__ . '/../vendor/autoload.php';
-$http = new React\Http\HttpServer(function (ServerRequestInterface $request) {
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
if ($request->getHeaderLine('Content-Type') !== 'application/json') {
- return new Response(
- Response::STATUS_UNSUPPORTED_MEDIA_TYPE,
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'Only supports application/json')) . "\n"
- );
+ return Response::json(
+ array('error' => 'Only supports application/json')
+ )->withStatus(Response::STATUS_UNSUPPORTED_MEDIA_TYPE);
}
$input = json_decode($request->getBody()->getContents());
if (json_last_error() !== JSON_ERROR_NONE) {
- return new Response(
- Response::STATUS_BAD_REQUEST,
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'Invalid JSON data given')) . "\n"
- );
+ return Response::json(
+ array('error' => 'Invalid JSON data given')
+ )->withStatus(Response::STATUS_BAD_REQUEST);
}
if (!isset($input->name) || !is_string($input->name)) {
- return new Response(
- Response::STATUS_UNPROCESSABLE_ENTITY,
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('error' => 'JSON data does not contain a string "name" property')) . "\n"
- );
+ return Response::json(
+ array('error' => 'JSON data does not contain a string "name" property')
+ )->withStatus(Response::STATUS_UNPROCESSABLE_ENTITY);
}
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'application/json'
- ),
- json_encode(array('message' => 'Hello ' . $input->name)) . "\n"
+ return Response::json(
+ array('message' => 'Hello ' . $input->name)
);
});
diff --git a/examples/61-server-hello-world-https.php b/examples/61-server-hello-world-https.php
index 2fd6f9af..23906430 100644
--- a/examples/61-server-hello-world-https.php
+++ b/examples/61-server-hello-world-https.php
@@ -1,17 +1,10 @@
'text/plain'
- ),
- "Hello world!\n"
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
+ return React\Http\Message\Response::plaintext(
+ "Hello World!\n"
);
});
diff --git a/examples/62-server-form-upload.php b/examples/62-server-form-upload.php
index 899caa0a..52864c82 100644
--- a/examples/62-server-form-upload.php
+++ b/examples/62-server-form-upload.php
@@ -109,11 +109,7 @@
HTML;
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/html; charset=UTF-8'
- ),
+ return Response::html(
$html
);
};
diff --git a/examples/63-server-streaming-request.php b/examples/63-server-streaming-request.php
index b20b8f08..fef6f008 100644
--- a/examples/63-server-streaming-request.php
+++ b/examples/63-server-streaming-request.php
@@ -19,24 +19,16 @@ function (Psr\Http\Message\ServerRequestInterface $request) {
});
$body->on('end', function () use ($resolve, &$bytes){
- $resolve(new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ $resolve(React\Http\Message\Response::plaintext(
"Received $bytes bytes\n"
));
});
// an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event
$body->on('error', function (Exception $e) use ($resolve, &$bytes) {
- $resolve(new React\Http\Message\Response(
- React\Http\Message\Response::STATUS_BAD_REQUEST,
- array(
- 'Content-Type' => 'text/plain'
- ),
+ $resolve(React\Http\Message\Response::plaintext(
"Encountered error after $bytes bytes: {$e->getMessage()}\n"
- ));
+ )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST));
});
});
}
diff --git a/examples/71-server-http-proxy.php b/examples/71-server-http-proxy.php
index e0bf8404..cf63c4ae 100644
--- a/examples/71-server-http-proxy.php
+++ b/examples/71-server-http-proxy.php
@@ -3,25 +3,17 @@
// $ php examples/71-server-http-proxy.php 8080
// $ curl -v --proxy http://localhost:8080 http://reactphp.org/
-use Psr\Http\Message\RequestInterface;
-use React\Http\Message\Response;
-use RingCentral\Psr7;
-
require __DIR__ . '/../vendor/autoload.php';
// Note how this example uses the `HttpServer` without the `StreamingRequestMiddleware`.
// This means that this proxy buffers the whole request before "processing" it.
// As such, this is store-and-forward proxy. This could also use the advanced
// `StreamingRequestMiddleware` to forward the incoming request as it comes in.
-$http = new React\Http\HttpServer(function (RequestInterface $request) {
+$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
if (strpos($request->getRequestTarget(), '://') === false) {
- return new Response(
- Response::STATUS_BAD_REQUEST,
- array(
- 'Content-Type' => 'text/plain'
- ),
- 'This is a plain HTTP proxy'
- );
+ return React\Http\Message\Response::plaintext(
+ "This is a plain HTTP proxy\n"
+ )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
}
// prepare outgoing client request by updating request-target and Host header
@@ -35,12 +27,8 @@
// pseudo code only: simply dump the outgoing request as a string
// left up as an exercise: use an HTTP client to send the outgoing request
// and forward the incoming response to the original client request
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/plain'
- ),
- Psr7\str($outgoing)
+ return React\Http\Message\Response::plaintext(
+ RingCentral\Psr7\str($outgoing)
);
});
diff --git a/examples/99-server-benchmark-download.php b/examples/99-server-benchmark-download.php
index 6c737605..ddd4760a 100644
--- a/examples/99-server-benchmark-download.php
+++ b/examples/99-server-benchmark-download.php
@@ -93,11 +93,7 @@ public function getSize()
$http = new React\Http\HttpServer(function (ServerRequestInterface $request) {
switch ($request->getUri()->getPath()) {
case '/':
- return new Response(
- Response::STATUS_OK,
- array(
- 'Content-Type' => 'text/html'
- ),
+ return Response::html(
'1g.bin
10g.bin'
);
case '/1g.bin':
diff --git a/src/Message/Response.php b/src/Message/Response.php
index 0ce8bef0..edd6245b 100644
--- a/src/Message/Response.php
+++ b/src/Message/Response.php
@@ -42,6 +42,221 @@
*/
final class Response extends Psr7Response implements StatusCodeInterface
{
+ /**
+ * Create an HTML response
+ *
+ * ```php
+ * $html = <<
+ *
+ * Hello wörld!
+ *
+ *
+ * HTML;
+ *
+ * $response = React\Http\Message\Response::html($html);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'text/html; charset=utf-8'
+ * ],
+ * $html
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given HTTP source
+ * string encoded in UTF-8 (Unicode). It's generally recommended to end the
+ * given plaintext string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::html(
+ * "Error
\nInvalid user name given.
\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $html
+ * @return self
+ */
+ public static function html($html)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'text/html; charset=utf-8'), $html);
+ }
+
+ /**
+ * Create a JSON response
+ *
+ * ```php
+ * $response = React\Http\Message\Response::json(['name' => 'Alice']);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'application/json'
+ * ],
+ * json_encode(
+ * ['name' => 'Alice'],
+ * JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION
+ * ) . "\n"
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given structured
+ * data encoded as a JSON text.
+ *
+ * The given structured data will be encoded as a JSON text. Any `string`
+ * values in the data must be encoded in UTF-8 (Unicode). If the encoding
+ * fails, this method will throw an `InvalidArgumentException`.
+ *
+ * By default, the given structured data will be encoded with the flags as
+ * shown above. This includes pretty printing (PHP 5.4+) and preserving
+ * zero fractions for `float` values (PHP 5.6.6+) to ease debugging. It is
+ * assumed any additional data overhead is usually compensated by using HTTP
+ * response compression.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::json(
+ * ['error' => 'Invalid user name given']
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param mixed $data
+ * @return self
+ * @throws \InvalidArgumentException when encoding fails
+ */
+ public static function json($data)
+ {
+ $json = @\json_encode(
+ $data,
+ (\defined('JSON_PRETTY_PRINT') ? \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE : 0) | (\defined('JSON_PRESERVE_ZERO_FRACTION') ? \JSON_PRESERVE_ZERO_FRACTION : 0)
+ );
+
+ // throw on error, now `false` but used to be `(string) "null"` before PHP 5.5
+ if ($json === false || (\PHP_VERSION_ID < 50500 && \json_last_error() !== \JSON_ERROR_NONE)) {
+ throw new \InvalidArgumentException(
+ 'Unable to encode given data as JSON' . (\function_exists('json_last_error_msg') ? ': ' . \json_last_error_msg() : ''),
+ \json_last_error()
+ );
+ }
+
+ return new self(self::STATUS_OK, array('Content-Type' => 'application/json'), $json . "\n");
+ }
+
+ /**
+ * Create a plaintext response
+ *
+ * ```php
+ * $response = React\Http\Message\Response::plaintext("Hello wörld!\n");
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'text/plain; charset=utf-8'
+ * ],
+ * "Hello wörld!\n"
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given plaintext
+ * string encoded in UTF-8 (Unicode). It's generally recommended to end the
+ * given plaintext string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::plaintext(
+ * "Error: Invalid user name given.\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $text
+ * @return self
+ */
+ public static function plaintext($text)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'text/plain; charset=utf-8'), $text);
+ }
+
+ /**
+ * Create an XML response
+ *
+ * ```php
+ * $xml = <<
+ *
+ * Hello wörld!
+ *
+ *
+ * XML;
+ *
+ * $response = React\Http\Message\Response::xml($xml);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'application/xml'
+ * ],
+ * $xml
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given XML source
+ * string. It's generally recommended to use UTF-8 (Unicode) and specify
+ * this as part of the leading XML declaration and to end the given XML
+ * source string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::xml(
+ * "Invalid user name given.\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $xml
+ * @return self
+ */
+ public static function xml($xml)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'application/xml'), $xml);
+ }
+
/**
* @param int $status HTTP status code (e.g. 200/404), see `self::STATUS_*` constants
* @param array $headers additional response headers
diff --git a/tests/Message/ResponseTest.php b/tests/Message/ResponseTest.php
index 3c6ad3fd..ed21cdc2 100644
--- a/tests/Message/ResponseTest.php
+++ b/tests/Message/ResponseTest.php
@@ -53,4 +53,75 @@ public function testResourceBodyWillThrow()
$this->setExpectedException('InvalidArgumentException');
new Response(200, array(), tmpfile());
}
+
+
+ public function testHtmlMethodReturnsHtmlResponse()
+ {
+ $response = Response::html('Hello wörld!');
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals('Hello wörld!', (string) $response->getBody());
+ }
+
+ /**
+ * @requires PHP 5.4
+ */
+ public function testJsonMethodReturnsPrettyPrintedJsonResponse()
+ {
+ $response = Response::json(array('text' => 'Hello wörld!'));
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('application/json', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals("{\n \"text\": \"Hello wörld!\"\n}\n", (string) $response->getBody());
+ }
+
+ /**
+ * @requires PHP 5.6.6
+ */
+ public function testJsonMethodReturnsZeroFractionsInJsonResponse()
+ {
+ $response = Response::json(1.0);
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('application/json', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals("1.0\n", (string) $response->getBody());
+ }
+
+ public function testJsonMethodReturnsJsonTextForSimpleString()
+ {
+ $response = Response::json('Hello world!');
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('application/json', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals("\"Hello world!\"\n", (string) $response->getBody());
+ }
+
+ public function testJsonMethodThrowsForInvalidString()
+ {
+ if (PHP_VERSION_ID < 50500) {
+ $this->setExpectedException('InvalidArgumentException', 'Unable to encode given data as JSON');
+ } else {
+ $this->setExpectedException('InvalidArgumentException', 'Unable to encode given data as JSON: Malformed UTF-8 characters, possibly incorrectly encoded');
+ }
+ Response::json("Hello w\xF6rld!");
+ }
+
+ public function testPlaintextMethodReturnsPlaintextResponse()
+ {
+ $response = Response::plaintext("Hello wörld!\n");
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('text/plain; charset=utf-8', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals("Hello wörld!\n", (string) $response->getBody());
+ }
+
+ public function testXmlMethodReturnsXmlResponse()
+ {
+ $response = Response::xml('Hello wörld!');
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('application/xml', $response->getHeaderLine('Content-Type'));
+ $this->assertEquals('Hello wörld!', (string) $response->getBody());
+ }
}