Response
The response is actually a support\Response
object. To facilitate the creation of this object, Webman provides some helper functions.
Return an arbitrary response
Example
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman');
}
}
The implementation of the response function is as follows:
function response($body = '', $status = 200, $headers = array())
{
return new Response($status, $headers, $body);
}
You can also create an empty response
object first and then use $response->cookie()
, $response->header()
, $response->withHeaders()
, and $response->withBody()
to set the return content as appropriate.
public function hello(Request $request)
{
// Create an object
$response = response();
// .... Business logic omitted
// Set cookie
$response->cookie('foo', 'value');
// .... Business logic omitted
// Set HTTP headers
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... Business logic omitted
// Set the data to return
$response->withBody('The returned data');
return $response;
}
Return JSON
Example
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}
The implementation of the json function is as follows:
function json($data, $options = JSON_UNESCAPED_UNICODE)
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
Return XML
Example
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
$xml = <<<XML
<?xml version='1.0' standalone='yes'?>
<values>
<truevalue>1</truevalue>
<falsevalue>0</falsevalue>
</values>
XML;
return xml($xml);
}
}
The implementation of the xml function is as follows:
function xml($xml)
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
Return a view
Create a file app/controller/FooController.php
as follows:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return view('foo/hello', ['name' => 'webman']);
}
}
Create a file app/view/foo/hello.html
as follows:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>
Redirect
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return redirect('/user');
}
}
The implementation of the redirect function is as follows:
function redirect($location, $status = 302, $headers = [])
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
Header settings
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman', 200, [
'Content-Type' => 'application/json',
'X-Header-One' => 'Header Value'
]);
}
}
You can also use the header
and withHeaders
methods to set headers individually or in bulk.
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->header('Content-Type', 'application/json')
->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
}
}
You can also set headers in advance and set the data to return last.
public function hello(Request $request)
{
// Create an object
$response = response();
// .... Business logic omitted
// Set HTTP headers
$response->header('Content-Type', 'application/json');
$response->withHeaders([
'X-Header-One' => 'Header Value 1',
'X-Header-Tow' => 'Header Value 2',
]);
// .... Business logic omitted
// Set the data to return
$response->withBody('The returned data');
return $response;
}
Set cookie
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response('hello webman')
->cookie('foo', 'value');
}
}
You can also set the cookie in advance, then set the data to return.
public function hello(Request $request)
{
// Create an object
$response = response();
// .... Business logic omitted
// Set cookie
$response->cookie('foo', 'value');
// .... Business logic omitted
// Set the data to return
$response->withBody('The returned data');
return $response;
}
The complete parameters for the cookie method are as follows:
cookie($name, $value = '', $max_age = 0, $path = '', $domain = '', $secure = false, $http_only = false)
Return a file stream
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->file(public_path() . '/favicon.ico');
}
}
- Webman supports sending very large files.
- For large files (over 2M), Webman will not read the entire file into memory at once but will read the file in chunks and send it at appropriate times.
- Webman will optimize the file read and send speed according to the client's receiving speed, ensuring the fastest file transfer while minimizing memory usage.
- Data sending is non-blocking and will not affect the processing of other requests.
- The file method will automatically add the
if-modified-since
header and check it on the next request; if the file has not been modified, it will return 304 to save bandwidth. - The sent file will automatically be sent to the browser with the appropriate
Content-Type
header. - If the file does not exist, it will automatically turn into a 404 response.
Download a file
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function hello(Request $request)
{
return response()->download(public_path() . '/favicon.ico', 'filename.ico');
}
}
The download method is essentially the same as the file method, with the distinction that:
- Setting a filename for the downloaded file will cause the file to be downloaded instead of displayed in the browser.
- The download method will not check the
if-modified-since
header.
Get output
Some libraries directly print the file content to standard output, which means that the data is printed in the command-line terminal and not sent to the browser. In this case, we need to capture the data into a variable using ob_start();
and ob_get_clean();
, and then send the data to the browser, for example:
<?php
namespace app\controller;
use support\Request;
class ImageController
{
public function get(Request $request)
{
// Create an image
$im = imagecreatetruecolor(120, 20);
$text_color = imagecolorallocate($im, 233, 14, 91);
imagestring($im, 1, 5, 5, 'A Simple Text String', $text_color);
// Start capturing output
ob_start();
// Output the image
imagejpeg($im);
// Retrieve the image content
$image = ob_get_clean();
// Send the image
return response($image)->header('Content-Type', 'image/jpeg');
}
}
Chunked responses
Sometimes we want to send responses in chunks. You can refer to the example below.
<?php
namespace app\controller;
use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;
use Workerman\Timer;
class IndexController
{
public function index(Request $request): Response
{
// Get the connection
$connection = $request->connection;
// Send HTTP body periodically
$timer = Timer::add(1, function () use ($connection, &$timer) {
static $i = 0;
if ($i++ < 10) {
// Send HTTP body
$connection->send(new Chunk($i));
} else {
// Remove unnecessary timer to prevent memory leaks
Timer::del($timer);
// Send an empty Chunk to notify the client that the response has ended
$connection->send(new Chunk(''));
}
});
// Output an HTTP header with Transfer-Encoding: chunked, the HTTP body will be sent asynchronously
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
If you are calling a large model, refer to the example below.
composer require webman/openai
<?php
namespace app\controller;
use support\Request;
use Webman\Openai\Chat;
use Workerman\Protocols\Http\Chunk;
class ChatController
{
public function completions(Request $request)
{
$connection = $request->connection;
// If you can't access https://api.openai.com, you can use https://api.openai-proxy.com instead
$chat = new Chat(['apikey' => 'sk-xx', 'api' => 'https://api.openai.com']);
$chat->completions(
[
'model' => 'gpt-3.5-turbo',
'stream' => true,
'messages' => [['role' => 'user', 'content' => 'hello']],
], [
'stream' => function($data) use ($connection) {
// When the OpenAI API returns data, forward it to the browser
$connection->send(new Chunk(json_encode($data, JSON_UNESCAPED_UNICODE) . "\n"));
},
'complete' => function($result, $response) use ($connection) {
// Check for errors when the response ends
if (isset($result['error'])) {
$connection->send(new Chunk(json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"));
}
// Return an empty chunk to signify the end of the response
$connection->send(new Chunk(''));
},
]);
// First return an HTTP header, and the subsequent data will be returned asynchronously
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}