getResponse()->$fn($header, $value);
$this->setResponse($response);
}
/**
* Set the headers with HTTP status code and content type.
* @link http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
*
* Examples:
*
* $this->respondWith(200, 'json');
* $this->respondWith(200, 'application/json');
* $this->respondWith(204);
* $this->respondWith("204 Created");
* $this->respondWith('json');
*
*
* @param int|string $status HTTP status (may be omitted)
* @param string|array $format Mime or content format
*/
public function respondWith($status, $format = null)
{
$response = $this->getResponse();
// Shift arguments if $code is omitted
if (isset($status) && !is_int($status) && (!is_string($status) || !preg_match('/^\d{3}\b/', $status))) {
list($status, $format) = array_merge([null], func_get_args());
}
if (!empty($status)) {
list($code, $phrase) = explode(' ', $status, 2) + [1 => null];
$response = $response->withStatus((int)$code, $phrase);
}
if (!empty($format)) {
$contentType = $this->getContentType($format);
$response = $response->withHeader('Content-Type', $contentType);
}
$this->setResponse($response);
}
/**
* Response with 200 OK
*
* @return ResponseInterface $response
*/
public function ok()
{
$this->respondWith(200);
}
/**
* Response with created 201 code, and optionally the created location
*
* @param string $location Url of created resource
*/
public function created($location = null)
{
$this->respondWith(201);
if (!empty($location)) {
$this->setResponseHeader('Location', $location);
}
}
/**
* Response with 203 Accepted
*/
public function accepted()
{
$this->respondWith(202);
}
/**
* Response with 204 No Content
*
* @param int $code 204 (No Content) or 205 (Reset Content)
*/
public function noContent($code = 204)
{
$this->respondWith($code);
}
/**
* Respond with a 206 Partial content with `Content-Range` header
*
* @param int $rangeFrom Beginning of the range in bytes
* @param int $rangeTo End of the range in bytes
* @param int $totalSize Total size in bytes
*/
public function partialContent($rangeFrom, $rangeTo, $totalSize)
{
$this->respondWith(206);
$this->setResponseHeader('Content-Range', "bytes {$rangeFrom}-{$rangeTo}/{$totalSize}");
$this->setResponseHeader('Content-Length', $rangeTo - $rangeFrom);
}
/**
* Redirect to url and output a short message with the link
*
* @param string $url
* @param int $code 301 (Moved Permanently), 302 (Found), 303 (See Other) or 307 (Temporary Redirect)
*/
public function redirect($url, $code = 303)
{
$this->respondWith($code);
$this->setResponseHeader('Location', $url);
$urlHtml = htmlentities($url);
$this->output('You are being redirected to ' . $urlHtml . '', 'text/html');
}
/**
* Redirect to previous page, or to home page
*
* @return ResponseInterface $response
*/
public function back()
{
$this->redirect($this->getLocalReferer() ?: '/');
}
/**
* Respond with 304 Not Modified
*/
public function notModified()
{
$this->respondWith(304);
}
/**
* Respond with 400 Bad Request
*
* @param string $message
* @param int $code HTTP status code
*/
public function badRequest($message, $code = 400)
{
$this->respondWith($code);
$this->output($message);
}
/**
* Respond with a 401 Unauthorized
*/
public function requireAuth()
{
$this->respondWith(401);
}
/**
* Alias of requireAuth
* @deprecated
*/
final public function requireLogin()
{
$this->requireAuth();
}
/**
* Respond with 402 Payment Required
*
* @param string $message
*/
public function paymentRequired($message = "Payment required")
{
$this->respondWith(402);
$this->output($message);
}
/**
* Respond with 403 Forbidden
*
* @param string $message
*/
public function forbidden($message = "Access denied")
{
$this->respondWith(403);
$this->output($message);
}
/**
* Respond with 404 Not Found
*
* @param string $message
* @param int $code 404 (Not Found), 405 (Method not allowed) or 410 (Gone)
*/
public function notFound($message = "Not found", $code = 404)
{
$this->respondWith($code);
$this->output($message);
}
/**
* Respond with 409 Conflict
*
* @param string $message
*/
public function conflict($message)
{
$this->respondWith(409);
$this->output($message);
}
/**
* Respond with 429 Too Many Requests
*
* @param string $message
*/
public function tooManyRequests($message = "Too many requests")
{
$this->respondWith(429);
$this->output($message);
}
/**
* Respond with a server error
*
* @param string $message
* @param int $code HTTP status code
*/
public function error($message = "An unexpected error occured", $code = 500)
{
$this->respondWith($code);
$this->output($message);
}
/**
* Get MIME type for extension
*
* @param string $format
* @return string
*/
protected function getContentType($format)
{
// Check if it's already MIME
if (\Jasny\str_contains($format, '/')) {
return $format;
}
$repository = new ApacheMimeTypes();
$mime = $repository->findType($format);
if (!isset($mime)) {
throw new \UnexpectedValueException("Format '$format' doesn't correspond with a MIME type");
}
return $mime;
}
/**
* If a non scalar value is passed without an format, use this format
*
* @param string $format Format by extention or MIME
*/
public function byDefaultSerializeTo($format)
{
$this->defaultFormat = $format;
}
/**
* Serialize data
*
* @param mixed $data
* @param string $contentType
* @return string
*/
protected function serializeData($data, $contentType)
{
if (is_string($data)) {
return $data;
}
$repository = new ApacheMimeTypes();
list($format) = $repository->findExtensions($contentType) + [null];
$method = 'serializeDataTo' . $format;
if (method_exists($this, $method)) {
return $this->$method($data);
}
$type = (is_object($data) ? get_class($data) . ' ' : '') . gettype($data);
throw new \UnexpectedValueException("Unable to serialize $type to '$contentType'");
}
/**
* Serialize data to JSON
* @internal made private because this will likely move to a library
*
* @param mixed $data
* @return string
*/
private function serializeDataToJson($data)
{
return json_encode($data);
}
/**
* Serialize data to XML.
* @internal made private because this will likely move to a library
*
* @param mixed $data
* @return string
*/
private function serializeDataToXml($data)
{
if ($data instanceof \SimpleXMLElement) {
return $data->asXML();
}
if (($data instanceof \DOMNode && isset($data->ownerDocument)) || $data instanceof \DOMDocument) {
$dom = $data instanceof \DOMDocument ? $data : $data->ownerDocument;
return $dom->saveXML($data);
}
$type = (is_object($data) ? get_class($data) . ' ' : '') . gettype($data);
throw new \UnexpectedValueException("Unable to serialize $type to XML");
}
/**
* Set the content type for the output
*
* @param string $format
* @return string
*/
protected function outputContentType($format)
{
if (!isset($format)) {
$contentType = $this->getResponse()->getHeaderLine('Content-Type');
if (empty($contentType)) {
$format = $this->defaultFormat ?: 'text/html';
}
}
if (empty($contentType)) {
$contentType = $this->getContentType($format);
$this->setResponseHeader('Content-Type', $contentType);
}
return $contentType;
}
/**
* Output result
*
* @param mixed $data
* @param string $format Output format as MIME or extension
*/
public function output($data, $format = null)
{
$contentType = $this->outputContentType($format);
try {
$content = $this->serializeData($data, $contentType);
} catch (\UnexpectedValueException $e) {
if (!isset($format) && isset($this->defaultFormat) && $this->defaultFormat !== $contentType) {
$this->output($data, $this->defaultFormat); // Try default format instead
return;
}
throw $e;
}
$this->getResponse()->getBody()->write($content);
}
}