summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--composer.json3
-rw-r--r--src/Controller.php506
-rw-r--r--src/Controller/CheckRequest.php86
-rw-r--r--src/Controller/CheckResponse.php75
-rw-r--r--src/Controller/Respond.php307
-rw-r--r--src/Controller/RouteAction.php87
-rw-r--r--src/Controller/Session.php65
-rw-r--r--src/Controller/Session/Flash.php129
-rw-r--r--src/Controller/View/Twig.php (renamed from src/View/Twig.php)0
-rw-r--r--src/Flash.php109
-rw-r--r--tests/ControllerTest.php81
11 files changed, 792 insertions, 656 deletions
diff --git a/composer.json b/composer.json
index 4a67d09..90e4820 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,8 @@
"require": {
"php": ">=5.6.0",
"psr/http-message": "^1.0",
- "jasny/php-functions": "^2.0"
+ "jasny/php-functions": "^2.0",
+ "dflydev/apache-mime-types": "^1.0"
},
"require-dev": {
"jasny/php-code-quality": "^2.0",
diff --git a/src/Controller.php b/src/Controller.php
index 9f69160..93ba38d 100644
--- a/src/Controller.php
+++ b/src/Controller.php
@@ -10,6 +10,11 @@ use Psr\Http\Message\ResponseInterface;
*/
abstract class Controller
{
+ use Controller\Respond,
+ Controller\CheckRequest,
+ Controller\CheckResponse,
+ Controller\Session;
+
/**
* Server request
* @var ServerRequestInterface
@@ -22,46 +27,18 @@ 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'
- ];
/**
- * Flash
- * @var Flash
- */
- protected $flash = null;
-
- /**
- * Run the controller
- *
- * @return ResponseInterface
- */
- abstract public function run();
-
- /**
* Get request, set for controller
*
* @return ServerRequestInterface
*/
- public function getRequest()
+ protected function getRequest()
{
+ if (!isset($this->request)) {
+ throw new \RuntimeException("Request not set, the controller has not been invoked");
+ }
+
return $this->request;
}
@@ -70,470 +47,47 @@ abstract class Controller
*
* @return ResponseInterface
*/
- public function getResponse()
+ protected function getResponse()
{
+ if (!isset($this->response)) {
+ throw new \RuntimeException("Response not set, the controller has not been invoked");
+ }
+
return $this->response;
}
/**
- * Run the controller as function
+ * Get response. set for controller
*
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
* @return ResponseInterface
*/
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
+ protected function setResponse(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:
- * <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;
- }
-
- /**
- * Set the flash message and/or return the flash object.
- *
- * @param mixed $type flash type, eg. 'error', 'notice' or 'success'
- * @param mixed $message flash message
- * @return Flash
- */
- public function flash($type = null, $message = null)
- {
- if (!isset($this->flash)) $this->flash = new Flash();
- if ($type && $message) $this->flash->set($type, $message);
-
- return $this->flash;
- }
-
- /**
- * 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();
- }
-
- /**
- * 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);
-
- $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
+ * Run the controller
*
- * @return int
+ * @return ResponseInterface
*/
- protected function getResponseStatusCode()
- {
- $response = $this->getResponse();
-
- return $response ? $response->getStatusCode() : 0;
- }
+ abstract protected function run();
- /**
- * 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
+ * Run the controller as function
*
- * @return string
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @return ResponseInterface
*/
- protected function getRequestMethod()
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
{
- $request = $this->getRequest();
+ $this->request = $request;
+ $this->response = $response;
- return $request ? $request->getMethod() : '';
+ $this->useSession();
+
+ return $this->run();
}
}
diff --git a/src/Controller/CheckRequest.php b/src/Controller/CheckRequest.php
new file mode 100644
index 0000000..a63a2b7
--- /dev/null
+++ b/src/Controller/CheckRequest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Jasny\Controller;
+
+use Psr\Http\Message\ServerRequestInterface;
+
+/**
+ * Controller methods to check the request
+ */
+trait CheckRequest
+{
+ /**
+ * Get request, set for controller
+ *
+ * @return ServerRequestInterface
+ */
+ abstract protected function getRequest();
+
+
+ /**
+ * Check if request is GET request
+ *
+ * @return boolean
+ */
+ public function isGetRequest()
+ {
+ $method = $this->getRequest()->getMethod();
+
+ return !$method || $method === 'GET';
+ }
+
+ /**
+ * Check if request is POST request
+ *
+ * @return boolean
+ */
+ public function isPostRequest()
+ {
+ return $this->getRequest()->getMethod() === 'POST';
+ }
+
+ /**
+ * Check if request is PUT request
+ *
+ * @return boolean
+ */
+ public function isPutRequest()
+ {
+ return $this->getRequest()->getMethod() === 'PUT';
+ }
+
+ /**
+ * Check if request is DELETE request
+ *
+ * @return boolean
+ */
+ public function isDeleteRequest()
+ {
+ return $this->getRequest()->getMethod() === 'DELETE';
+ }
+
+ /**
+ * Check if request is HEAD request
+ *
+ * @return boolean
+ */
+ public function isHeadRequest()
+ {
+ return $this->getRequest()->getMethod() === '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 : '';
+ }
+} \ No newline at end of file
diff --git a/src/Controller/CheckResponse.php b/src/Controller/CheckResponse.php
new file mode 100644
index 0000000..7c02d46
--- /dev/null
+++ b/src/Controller/CheckResponse.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Jasny\Controller;
+
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Methods to check the response
+ */
+trait CheckResponse
+{
+ /**
+ * Get response. set for controller
+ *
+ * @return ResponseInterface
+ */
+ abstract protected function getResponse();
+
+
+ /**
+ * Check if response is 2xx succesful, or empty
+ *
+ * @return boolean
+ */
+ public function isSuccessful()
+ {
+ $code = $this->getResponse()->getStatusCode() ?: 200;
+
+ return !$code || ($code >= 200 && $code < 300);
+ }
+
+ /**
+ * Check if response is a 3xx redirect
+ *
+ * @return boolean
+ */
+ public function isRedirection()
+ {
+ $code = $this->getResponse()->getStatusCode() ?: 200;
+
+ return $code >= 300 && $code < 400;
+ }
+
+ /**
+ * Check if response is a 4xx client error
+ *
+ * @return boolean
+ */
+ public function isClientError()
+ {
+ $code = $this->getResponse()->getStatusCode() ?: 200;
+
+ return $code >= 400 && $code < 500;
+ }
+
+ /**
+ * Check if response is a 5xx redirect
+ *
+ * @return boolean
+ */
+ public function isServerError()
+ {
+ return $this->getResponse()->getStatusCode() ?: 200 >= 500;
+ }
+
+ /**
+ * Check if response is 4xx or 5xx error
+ *
+ * @return boolean
+ */
+ public function isError()
+ {
+ return $this->isClientError() || $this->isServerError();
+ }
+}
diff --git a/src/Controller/Respond.php b/src/Controller/Respond.php
new file mode 100644
index 0000000..02b2cfb
--- /dev/null
+++ b/src/Controller/Respond.php
@@ -0,0 +1,307 @@
+<?php
+
+namespace Jasny\Controller;
+
+use Psr\Http\Message\ResponseInterface;
+use Dflydev\ApacheMimeTypes\PhpRepository as ApacheMimeTypes;
+
+/**
+ * Methods for a controller to send a response
+ */
+trait Respond
+{
+ /**
+ * Get response. set for controller
+ *
+ * @return ResponseInterface
+ */
+ abstract protected function getResponse();
+
+ /**
+ * Get response. set for controller
+ *
+ * @return ResponseInterface
+ */
+ abstract protected function setResponse(ResponseInterface $response);
+
+ /**
+ * Returns the HTTP referer if it is on the current host
+ *
+ * @return string
+ */
+ abstract public function getLocalReferer();
+
+
+ /**
+ * Set a response header
+ *
+ * @param string $header
+ * @param string $value
+ * @param boolean $overwrite
+ */
+ public function setResponseHeader($header, $value, $overwrite = true)
+ {
+ $fn = $overwrite ? 'withHeader' : 'withAddedHeader';
+ $response = $this->getResponse()->$fn($value, $header);
+
+ $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:
+ * <code>
+ * $this->respondWith(200, 'json');
+ * $this->respondWith(200, 'application/json');
+ * $this->respondWith(204);
+ * $this->respondWith("204 Created");
+ * $this->respondWith('json');
+ * </code>
+ *
+ * @param int $code HTTP status code (may be omitted)
+ * @param string|array $format Mime or content format
+ */
+ public function respondWith($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);
+ }
+
+ $this->setResponse($response);
+ }
+
+
+ /**
+ * Response with 200 OK
+ *
+ * @return ResponseInterface $response
+ */
+ public function ok()
+ {
+ $this->respondWith(200);
+ }
+
+ /**
+ * Response with created 201 code, and optionaly redirect to created location
+ *
+ * @param string $location Url of created resource
+ */
+ public function created($location = '')
+ {
+ $this->respondWith(201);
+
+ if ($location) {
+ $this->setResponseHeader('Location', $location);
+ }
+ }
+
+ /**
+ * Response with 204 No Content
+ */
+ public function noContent()
+ {
+ $this->respondWith(204);
+ }
+
+ /**
+ * Redirect to url
+ *
+ * @param string $url
+ * @param int $code 301 (Moved Permanently), 303 (See Other) or 307 (Temporary Redirect)
+ */
+ public function redirect($url, $code = 303)
+ {
+ $this->respondWith($code, 'text/html');
+ $this->setResponseHeader('Location', $url);
+
+ $urlHtml = htmlentities($url);
+ $this->output('You are being redirected to <a href="' . $urlHtml . '">' . $urlHtml . '</a>');
+ }
+
+ /**
+ * Redirect to previous page, or to home page
+ *
+ * @return ResponseInterface $response
+ */
+ public function back()
+ {
+ $this->redirect($this->getLocalReferer() ?: '/');
+ }
+
+
+ /**
+ * 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 = "Forbidden")
+ {
+ $this->respondWith(403);
+ $this->output($message);
+ }
+
+ /**
+ * Respond with 404 Not Found
+ *
+ * @param string $message
+ * @param int $code HTTP status code (404 or 405)
+ */
+ 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)
+ {
+ if (\Jasny\str_contains($format, '/')) { // Already MIME
+ return $format;
+ }
+
+ $repository = new ApacheMimeTypes();
+ $mime = $repository->findType('html');
+
+ if (!isset($mime)) {
+ throw new \UnexpectedValueException("Format $format doesn't correspond with a MIME type");
+ }
+
+ return $mime;
+ }
+
+ /**
+ * Serialize data
+ *
+ * @param mixed $data
+ * @param string $contentType
+ * @return string
+ */
+ protected function serializeData($data, $contentType)
+ {
+ if ($contentType == 'json') {
+ return (is_string($data) && (json_decode($data) !== null || !json_last_error()))
+ ? $data : json_encode($data);
+ }
+
+ if (!is_scalar($data)) {
+ throw new \Exception("Unable to serialize data to {$contentType}");
+ }
+
+ return $data;
+ }
+
+ /**
+ * Output result
+ *
+ * @param mixed $data
+ * @param string $format Output format as MIME or extension
+ */
+ public function output($data, $format = null)
+ {
+ if (!isset($format)) {
+ $contentType = $this->getResponse()->getHeaderLine('Content-Type') ?: 'text/html';
+ } else {
+ $contentType = $this->getContentType($format);
+ $this->setResponseHeader('Content-Type', $contentType);
+ }
+
+ $content = $this->serializeData($data, $contentType);
+
+ $this->getResponse()->getBody()->write($content);
+ }
+}
diff --git a/src/Controller/RouteAction.php b/src/Controller/RouteAction.php
index dabd501..5303f8a 100644
--- a/src/Controller/RouteAction.php
+++ b/src/Controller/RouteAction.php
@@ -15,40 +15,75 @@ trait RouteAction
*
* @return ServerRequestInterface
*/
- abstract public function getRequest();
+ abstract protected function getRequest();
- /**
+ /**
* Get response. set for controller
*
* @return ResponseInterface
*/
- abstract public function getResponse();
+ abstract protected function getResponse();
+
+ /**
+ * Respond with a server error
+ *
+ * @param string $message
+ * @param int $code HTTP status code
+ */
+ abstract public function notFound($message = '', $code = 404);
/**
+ * Check if response is 2xx succesful, or empty
+ *
+ * @return boolean
+ */
+ abstract public function isSuccessful();
+
+
+ /**
+ * Called before executing the action.
+ * If the response is no longer a success statuc (>= 300), the action will not be executed.
+ *
+ * <code>
+ * protected function beforeAction()
+ * {
+ * $this->respondWith('json'); // Respond with JSON by default
+ *
+ * if ($this->auth->getUser()->getCredits() <= 0) {
+ * $this->paymentRequired();
+ * }
+ * }
+ * </code>
+ */
+ protected function beforeAction()
+ {
+ }
+
+ /**
* Run the controller
*
* @return ResponseInterface
*/
- public function run() {
- $request = $this->getRequest();
- $route = $request->getAttribute('route');
+ public function run()
+ {
+ $route = $this->getRequest()->getAttribute('route');
$method = $this->getActionMethod(isset($route->action) ? $route->action : 'default');
-
+
if (!method_exists($this, $method)) {
- return $this->setResponseError(404, 'Not Found');
+ return $this->notFound();
}
- try {
- $args = isset($route->args) ?
- $route->args :
- $this->getFunctionArgs($route, new \ReflectionMethod($this, $method));
- } catch (\RuntimeException $e) {
- return $this->setResponseError(400, 'Bad Request');
- }
+ $args = isset($route->args)
+ ? $route->args
+ : $this->getFunctionArgs($route, new \ReflectionMethod($this, $method));
- $response = call_user_func_array([$this, $method], $args);
+ $this->beforeAction();
+
+ if ($this->isSuccessful()) {
+ call_user_func_array([$this, $method], $args);
+ }
- return $response ?: $this->getResponse();
+ return $this->getResponse();
}
/**
@@ -96,22 +131,4 @@ trait RouteAction
return $args;
}
-
- /**
- * Set response to error state
- *
- * @param int $code
- * @param string $message
- * @return ResponseInterface
- */
- protected function setResponseError($code, $message)
- {
- $response = $this->getResponse();
-
- $errorResponse = $response->withStatus($code);
- $errorResponse->getBody()->write($message);
-
- return $errorResponse;
- }
}
-
diff --git a/src/Controller/Session.php b/src/Controller/Session.php
new file mode 100644
index 0000000..8dc292f
--- /dev/null
+++ b/src/Controller/Session.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Jasny\Controller;
+
+use Jasny\Controller\Session\Flash;
+
+/**
+ * Use session in the controller
+ */
+trait Session
+{
+ /**
+ * Session
+ * @var array|\ArrayObject
+ */
+ protected $session;
+
+ /**
+ * Flash message
+ * @var Flash
+ */
+ protected $flash;
+
+
+ /**
+ * Get request, set for controller
+ *
+ * @return ServerRequestInterface
+ */
+ abstract protected function getRequest();
+
+
+ /**
+ * Link the session to the session property in the controller
+ */
+ protected function useSession()
+ {
+ $this->session = $this->getRequest()->getAttribute('session');
+
+ if (!isset($this->session)) {
+ $this->session =& $_SESSION;
+ }
+ }
+
+
+ /**
+ * Get an/or set the flash message.
+ *
+ * @param mixed $type flash type, eg. 'error', 'notice' or 'success'
+ * @param mixed $message flash message
+ * @return Flash
+ */
+ public function flash($type = null, $message = null)
+ {
+ if (!isset($this->flash)) {
+ $this->flash = new Flash($this->session);
+ }
+
+ if ($type) {
+ $this->flash->set($type, $message);
+ }
+
+ return $this->flash;
+ }
+}
diff --git a/src/Controller/Session/Flash.php b/src/Controller/Session/Flash.php
new file mode 100644
index 0000000..be72e7c
--- /dev/null
+++ b/src/Controller/Session/Flash.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Jasny\Controller\Session;
+
+/**
+ * Class for the flash message
+ */
+class Flash
+{
+ /**
+ * @var array
+ */
+ protected $data;
+
+ /**
+ * @var array|\ArrayObject
+ */
+ protected $session;
+
+ /**
+ * Session key for flash
+ * @var string
+ */
+ protected $key = 'flash';
+
+
+ /**
+ * Class constructor
+ *
+ * @param array|\ArrayObject $session
+ */
+ public function __construct(&$session)
+ {
+ $this->session =& $session;
+ }
+
+ /**
+ * Check if the flash is set.
+ *
+ * @return boolean
+ */
+ public function isIssued()
+ {
+ return isset($this->session[$this->key]);
+ }
+
+ /**
+ * Set the flash.
+ *
+ * @param string $type flash type, eg. 'error', 'notice' or 'success'
+ * @param mixed $message flash message
+ */
+ public function set($type, $message)
+ {
+ if (!$type) {
+ throw new \InvalidArgumentException("Type should not be empty");
+ }
+
+ $this->session[$this->key] = compact('type', 'message');
+ }
+
+ /**
+ * Get the flash.
+ *
+ * @return object
+ */
+ public function get()
+ {
+ if (!isset($this->data) && isset($this->session[$this->key])) {
+ $this->data = $this->session[$this->key];
+ unset($this->session[$this->key]);
+ }
+
+ return (object)$this->data;
+ }
+
+ /**
+ * Reissue the flash.
+ */
+ public function reissue()
+ {
+ if (!isset($this->data) && isset($this->session[$this->key])) {
+ $this->data = $this->session[$this->key];
+ } else {
+ $this->session[$this->key] = $this->data;
+ }
+ }
+
+ /**
+ * Clear the flash.
+ */
+ public function clear()
+ {
+ $this->data = null;
+ unset($this->session[$this->key]);
+ }
+
+ /**
+ * Get the flash type
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ $data = $this->get();
+ return isset($data) ? $data->type : null;
+ }
+
+ /**
+ * Get the flash message
+ *
+ * @return string
+ */
+ public function getMessage()
+ {
+ $data = $this->get();
+ return isset($data) ? $data->message : null;
+ }
+
+ /**
+ * Cast object to string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string)$this->getMessage();
+ }
+}
diff --git a/src/View/Twig.php b/src/Controller/View/Twig.php
index ee5d4ce..ee5d4ce 100644
--- a/src/View/Twig.php
+++ b/src/Controller/View/Twig.php
diff --git a/src/Flash.php b/src/Flash.php
deleted file mode 100644
index ad060f9..0000000
--- a/src/Flash.php
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-
-namespace Jasny;
-
-/**
- * Class for the flash message
- */
-class Flash
-{
- /**
- * @var object
- */
- protected static $data = null;
-
- /**
- * Check if the flash is set.
- *
- * @return boolean
- */
- public static function isIssued()
- {
- return isset($_SESSION['flash']) || isset(static::$data);
- }
-
- /**
- * Set the flash.
- *
- * @param mixed $type flash type, eg. 'error', 'notice' or 'success'
- * @param mixed $message flash message
- */
- public static function set($type, $message)
- {
- if (!$type) {
- throw new \InvalidArgumentException("Type should not be empty");
- }
-
- static::$data = (object)['type'=>$type, 'message'=>$message];
-
- $_SESSION['flash'] = static::$data;
- }
-
- /**
- * Get the flash.
- *
- * @return object
- */
- public static function get()
- {
- if (!isset(static::$data) && isset($_SESSION['flash'])) {
- static::$data = (object)$_SESSION['flash'];
- unset($_SESSION['flash']);
- }
-
- return static::$data;
- }
-
- /**
- * Reissue the flash.
- */
- public static function reissue()
- {
- if (!isset(static::$data) && isset($_SESSION['flash'])) {
- static::$data = (object)$_SESSION['flash'];
- } else {
- $_SESSION['flash'] = static::$data;
- }
- }
-
- /**
- * Clear the flash.
- */
- public static function clear()
- {
- self::$data = null;
- unset($_SESSION['flash']);
- }
-
- /**
- * Get the flash type
- *
- * @return string
- */
- public static function getType()
- {
- $data = static::get();
- return isset($data) ? $data->type : null;
- }
-
- /**
- * Get the flash message
- *
- * @return string
- */
- public static function getMessage()
- {
- $data = static::get();
- return isset($data) ? $data->message : null;
- }
-
- /**
- * Cast object to string
- *
- * @return string
- */
- public function __toString()
- {
- return (string)$this->getMessage();
- }
-}
diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php
index 3c659b2..b6a6b2d 100644
--- a/tests/ControllerTest.php
+++ b/tests/ControllerTest.php
@@ -12,20 +12,44 @@ use Psr\Http\Message\StreamInterface;
class ControllerTest extends PHPUnit_Framework_TestCase
{
/**
+ * Get mock for controller
+ *
+ * @param array $methods Methods to mock
+ * @return Controller
+ */
+ public function getController($methods = [])
+ {
+ $builder = $this->getMockBuilder(Controller::class)->disableOriginalConstructor();
+ if ($methods) {
+ $builder->setMethods($methods);
+ }
+
+ return $builder->getMockForAbstractClass();
+ }
+
+ /**
* Test running controller
*/
public function testInvoke()
{
+ $test = $this;
$controller = $this->getController();
- list($request, $response) = $this->getRequests();
-
- $controller->expects($this->once())->method('run')->will($this->returnValue($response));
+
+ $request = $this->createMock(ServerRequestInterface::class);
+ $response = $this->createMock(ResponseInterface::class);
+ $finalResponse = $this->createMock(ResponseInterface::class);
+
+ $controller->expects($this->once())->method('run')
+ ->willReturnCallback(Closure::bind(function() use ($test, $request, $response, $finalResponse) {
+ $test->assertSame($request, $this->getRequest());
+ $test->assertSame($response, $this->getResponse());
+
+ return $finalResponse;
+ }, $controller, Controller::class));
$result = $controller($request, $response);
- $this->assertEquals($response, $result, "Invoking controller should return 'ResponseInterface' instance");
- $this->assertEquals($response, $controller->getResponse(), "Can not get 'ResponseInterface' instance from controller");
- $this->assertEquals($request, $controller->getRequest(), "Can not get 'ServerRequestInterface' instance from controller");
+ $this->assertEquals($finalResponse, $result);
}
/**
@@ -82,7 +106,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
-<<<<<<< HEAD
* Test functions that check request method
*
* @dataProvider requestMethodProvider
@@ -125,6 +148,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
*/
public function testEncodeDataPositive($data, $format, $callback = null)
{
+ $this->markTestSkipped();
+
$controller = $this->getController(['getRequest']);
list($request) = $this->getRequests();
@@ -183,6 +208,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
*/
public function testEncodeDataNegative($data, $format)
{
+ $this->markTestSkipped();
+
$controller = $this->getController(['getRequest']);
list($request) = $this->getRequests();
@@ -356,15 +383,15 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
- * Test responseWith function
+ * Test respondWith function
*
- * @dataProvider responseWithProvider
+ * @dataProvider respondWithProvider
* @param int|string $code
* @param string $format
* @param int $setCode Actual code that will be set in response
* @param string $contentType
*/
- public function testResponseWith($code, $format, $setCode, $contentType)
+ public function testRespondWith($code, $format, $setCode, $contentType)
{
$controller = $this->getController(['getResponse']);
list(, $response) = $this->getRequests();
@@ -372,17 +399,17 @@ class ControllerTest extends PHPUnit_Framework_TestCase
$this->expectResponseWith($response, $setCode, $contentType);
$controller->method('getResponse')->will($this->returnValue($response));
- $result = $controller->responseWith($code, $format);
+ $result = $controller->respondWith($code, $format);
$this->assertEquals($result, $response, "Response object should be returned");
}
/**
- * Test function responseWith
+ * Test function respondWith
*
* @return array
*/
- public function responseWithProvider()
+ public function respondWithProvider()
{
return [
[200, 'json', 200, 'application/json'],
@@ -394,10 +421,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
- * Test functions that are simple wrappers around responseWith function
+ * Test functions that are simple wrappers around respondWith function
*
- * @dataProvider responseWithWrappersProvider
- * @param string $functino
+ * @dataProvider respondWithWrappersProvider
+ * @param string $function
* @param int $code
*/
public function testResponseWithWrappers($function, $code)
@@ -414,11 +441,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
- * Provide data for testing responseWith wrappers
+ * Provide data for testing respondWith wrappers
*
* @return array
*/
- public function responseWithWrappersProvider()
+ public function respondWithWrappersProvider()
{
return [
['ok', 200],
@@ -626,7 +653,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
- * Expect correct work of responseWith function
+ * Expect correct work of respondWith function
*
* @param ResponseInterface $response
* @param int $code
@@ -676,22 +703,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
}
/**
- * Get mock for controller
- *
- * @param array $methods Methods to mock
- * @return Controller
- */
- public function getController($methods = [])
- {
- $builder = $this->getMockBuilder(Controller::class)->disableOriginalConstructor();
- if ($methods) {
- $builder->setMethods($methods);
- }
-
- return $builder->getMockForAbstractClass();
- }
-
- /**
* Get request and response instances
*
* @return array