'html',
'application/json' => 'json',
'application/xml' => 'xml',
'text/xml' => 'xml',
'text/plain' => 'text',
'application/javascript' => 'js',
'text/css' => 'css',
'image/png' => 'png',
'image/gif' => 'gif',
'image/jpeg' => 'jpeg',
'image/x-icon' => 'ico',
'application/x-www-form-urlencoded' => 'post',
'multipart/form-data' => 'post'
];
/**
* Run the controller
*
* @return ResponseInterface
*/
abstract public function run();
/**
* Get request, set for controller
*
* @return ServerRequestInterface
*/
public function getRequest()
{
return $this->request;
}
/**
* Get response. set for controller
*
* @return ResponseInterface
*/
public function getResponse()
{
return $this->response;
}
/**
* Run the controller as function
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
{
$this->request = $request;
$this->response = $response;
return $this->run();
}
/**
* Set the headers with HTTP status code and content type.
* @link http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
*
* Examples:
*
* $this->responseWith(200, 'json');
* $this->responseWith(200, 'application/json');
* $this->responseWith(204);
* $this->responseWith("204 Created");
* $this->responseWith('json');
*
*
* @param int $code HTTP status code (may be omitted)
* @param string|array $format Mime or content format
* @return ResponseInterface $response
*/
public function responseWith($code, $format = null)
{
$response = $this->getResponse();
// Shift arguments if $code is omitted
if (!is_int($code) && !preg_match('/^\d{3}\b/', $code)) {
list($code, $format) = array_merge([null], func_get_args());
}
if ($code) {
$response = $response->withStatus((int)$code);
}
if ($format) {
$contentType = $this->getContentType($format);
$response = $response->withHeader('Content-Type', $contentType);
}
return $response;
}
/**
* Response with success 200 code
*
* @return ResponseInterface $response
*/
public function ok()
{
return $this->responseWith(200);
}
/**
* Response with created 201 code, and optionaly redirect to created location
*
* @param string $location Url of created resource
* @return ResponseInterface $response
*/
public function created($location = '')
{
$response = $this->responseWith(201);
if ($location) {
$response = $response->withHeader('Location', $location);
}
return $response;
}
/**
* Response with 204 'No Content'
*
* @return ResponseInterface $response
*/
public function noContent()
{
return $this->responseWith(204);
}
/**
* Redirect to url
*
* @param string $url
* @param int $code 301 (Moved Permanently), 303 (See Other) or 307 (Temporary Redirect)
* @return ResponseInterface $response
*/
public function redirect($url, $code = 303)
{
$response = $this->responseWith($code, 'html');
$response = $response->withHeader('Location', $url);
$response->getBody()->write('You are being redirected to ' . $url . '');
return $response;
}
/**
* Redirect to previous page, or to home page
*
* @return ResponseInterface $response
*/
public function back()
{
return $this->redirect($this->getLocalReferer() ?: '/');
}
/**
* Route to 401
* Note: While the 401 route is used, we don't respond with a 401 http status code.
*
* @return ResponseInterface $response
*/
public function requireLogin()
{
return $this->redirect('/401');
}
/**
* Alias of requireLogin
*
* @return ResponseInterface $response
*/
public function requireAuth()
{
return $this->requireLogin();
}
/**
* Set response to error 'Bad Request' state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function badRequest($message, $code = 400)
{
return $this->error($message, $code);
}
/**
* Set response to error 'Forbidden' state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function forbidden($message, $code = 403)
{
return $this->error($message, $code);
}
/**
* Set response to error 'Not Found' state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function notFound($message, $code = 404)
{
return $this->error($message, $code);
}
/**
* Set response to error 'Conflict' state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function conflict($message, $code = 409)
{
return $this->error($message, $code);
}
/**
* Set response to error 'Too Many Requests' state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function tooManyRequests($message, $code = 429)
{
return $this->error($message, $code);
}
/**
* Set response to error state
*
* @param string $message
* @param int $code HTTP status code
* @return ResponseInterface $response
*/
public function error($message, $code = 400)
{
$response = $this->getResponse();
$errorResponse = $response->withStatus($code);
$errorResponse->getBody()->write($message);
return $errorResponse;
}
/**
* Check if response is 2xx succesful, or empty
*
* @return boolean
*/
public function isSuccessful()
{
$code = $this->getResponseStatusCode();
return !$code || ($code >= 200 && $code < 300);
}
/**
* Check if response is a 3xx redirect
*
* @return boolean
*/
public function isRedirection()
{
$code = $this->getResponseStatusCode();
return $code >= 300 && $code < 400;
}
/**
* Check if response is a 4xx client error
*
* @return boolean
*/
public function isClientError()
{
$code = $this->getResponseStatusCode();
return $code >= 400 && $code < 500;
}
/**
* Check if response is a 5xx redirect
*
* @return boolean
*/
public function isServerError()
{
return $this->getResponseStatusCode() >= 500;
}
/**
* Check if response is 4xx or 5xx error
*
* @return boolean
*/
public function isError()
{
return $this->isClientError() || $this->isServerError();
}
/**
* Returns the HTTP referer if it is on the current host
*
* @return string
*/
public function getLocalReferer()
{
$request = $this->getRequest();
$referer = $request->getHeaderLine('HTTP_REFERER');
$host = $request->getHeaderLine('HTTP_HOST');
return $referer && parse_url($referer, PHP_URL_HOST) === $host ? $referer : '';
}
/**
* Output result
*
* @param mixed $data
* @param string $format
* @return ResponseInterface $response
*/
public function output($data, $format)
{
$response = $this->getResponse();
$contentType = $this->getContentType($format);
$response = $response->withHeader('Content-Type', $contentType);
$content = is_scalar($data) ? $data : $this->encodeData($data, $format);
$response->getBody()->write($content);
return $response;
}
/**
* Encode data to send to client
*
* @param mixed $data
* @param string $format
* @return string
*/
public function encodeData($data, $format)
{
switch ($format) {
case 'json': return $this->encodeDataAsJson($data);
case 'xml': return $this->encodeDataAsXml($data);
case 'html':
throw new \InvalidArgumentException("To encode HTML please use a view");
default:
throw new \InvalidArgumentException("Can not encode data for format '$format'");
}
}
/**
* Encode data as xml
*
* @param \SimpleXMLElement $data
* @return string
*/
protected function encodeDataAsXml(\SimpleXMLElement $data)
{
return $data->asXML();
}
/**
* Encode data as json
*
* @param mixed
* @return string
*/
protected function encodeDataAsJson($data)
{
$data = json_encode($data);
return $this->isJsonp() ?
$this->getRequest()->getQueryParams()['callback'] . '(' . $data . ')' :
$data;
}
/**
* Check if we should respond with jsonp
*
* @return boolean
*/
protected function isJsonp()
{
$request = $this->getRequest();
return $request && !empty($request->getQueryParams()['callback']);
}
/**
* Get status code of response
*
* @return int
*/
protected function getResponseStatusCode()
{
$response = $this->getResponse();
return $response ? $response->getStatusCode() : 0;
}
/**
* Get valid content type by simple word description
*
* @param string $format
* @return string
*/
protected function getContentType($format)
{
return array_search($format, $this->contentFormats) ?: $format;
}
}