summaryrefslogtreecommitdiffstats
path: root/src/Controller.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/Controller.php')
-rw-r--r--src/Controller.php420
1 files changed, 402 insertions, 18 deletions
diff --git a/src/Controller.php b/src/Controller.php
index ddec2c0..057653e 100644
--- a/src/Controller.php
+++ b/src/Controller.php
@@ -23,6 +23,26 @@ abstract class Controller
protected $response = null;
/**
+ * Common input and output formats with associated MIME
+ * @var array
+ */
+ protected $contentFormats = [
+ 'text/html' => '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
@@ -65,6 +85,205 @@ abstract class Controller
}
/**
+ * Set the headers with HTTP status code and content type.
+ * @link http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+ *
+ * Examples:
+ * <code>
+ * $this->responseWith(200, 'json');
+ * $this->responseWith(200, 'application/json');
+ * $this->responseWith(204);
+ * $this->responseWith("204 Created");
+ * $this->responseWith('json');
+ * </code>
+ *
+ * @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 <a href="' . $url . '">' . $url . '</a>');
+
+ 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
@@ -111,25 +330,190 @@ abstract class Controller
}
/**
- * Check if response is 4xx or 5xx error
- *
- * @return boolean
- */
- public function isError()
- {
- return $this->isClientError() || $this->isServerError();
- }
-
- /**
- * Get status code of response
- *
- * @return int
- */
- protected function getResponseStatusCode()
- {
+ * Check if response is 4xx or 5xx error
+ *
+ * @return boolean
+ */
+ public function isError()
+ {
+ return $this->isClientError() || $this->isServerError();
+ }
+
+ /**
+ * Check if request is GET request
+ *
+ * @return boolean
+ */
+ public function isGetRequest()
+ {
+ $method = $this->getRequestMethod();
+
+ return !$method || $method === 'GET';
+ }
+
+ /**
+ * Check if request is POST request
+ *
+ * @return boolean
+ */
+ public function isPostRequest()
+ {
+ return $this->getRequestMethod() === 'POST';
+ }
+
+ /**
+ * Check if request is PUT request
+ *
+ * @return boolean
+ */
+ public function isPutRequest()
+ {
+ return $this->getRequestMethod() === 'PUT';
+ }
+
+ /**
+ * Check if request is DELETE request
+ *
+ * @return boolean
+ */
+ public function isDeleteRequest()
+ {
+ return $this->getRequestMethod() === 'DELETE';
+ }
+
+ /**
+ * Check if request is HEAD request
+ *
+ * @return boolean
+ */
+ public function isHeadRequest()
+ {
+ return $this->getRequestMethod() === 'HEAD';
+ }
+
+ /**
+ * 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);
- return $response ? $response->getStatusCode() : 0;
- }
+ $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;
+ }
+
+ /**
+ * Get method of request
+ *
+ * @return string
+ */
+ protected function getRequestMethod()
+ {
+ $request = $this->getRequest();
+
+ return $request ? $request->getMethod() : '';
+ }
}