summaryrefslogtreecommitdiffstats
path: root/src/Router
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2016-11-03 23:45:36 +0100
committerGitHub <noreply@github.com>2016-11-03 23:45:36 +0100
commitf3d05dfca77843a869e938028824f19307780939 (patch)
tree846c5e559360e530d200d897f67b5af02f8795e5 /src/Router
parentaa31c40618b0cc7b43b7a1cb107e97b49e2c06f1 (diff)
parentbd4cb77fbf04923fa31a08fdd1f33f2c0db87864 (diff)
downloadrouter-1.0.0.zip
router-1.0.0.tar.gz
router-1.0.0.tar.bz2
Merge pull request #13 from jasny/fix-testsv1.0.0
Fix tests
Diffstat (limited to 'src/Router')
-rw-r--r--src/Router/Middleware/BasePath.php24
-rw-r--r--src/Router/Middleware/ErrorHandler.php54
-rw-r--r--src/Router/Middleware/NotFound.php49
-rw-r--r--src/Router/Route.php26
-rw-r--r--src/Router/Routes/Glob.php217
-rw-r--r--src/Router/Routes/RouteBinding.php314
-rw-r--r--src/Router/Runner.php14
-rw-r--r--src/Router/Runner/Callback.php2
-rw-r--r--src/Router/Runner/Controller.php39
-rw-r--r--src/Router/Runner/PhpScript.php27
-rw-r--r--src/Router/RunnerFactory.php (renamed from src/Router/Runner/RunnerFactory.php)10
-rw-r--r--src/Router/UrlParsing.php4
12 files changed, 442 insertions, 338 deletions
diff --git a/src/Router/Middleware/BasePath.php b/src/Router/Middleware/BasePath.php
index a80d029..f356aed 100644
--- a/src/Router/Middleware/BasePath.php
+++ b/src/Router/Middleware/BasePath.php
@@ -57,13 +57,15 @@ class BasePath
$uri = $request->getUri();
$path = $this->normalizePath($uri->getPath());
- if (!$this->hasBasePath($path)) return $this->setError($response);
+ if (!$this->hasBasePath($path)) {
+ return $this->notFound($request, $response);
+ }
$noBase = $this->getBaselessPath($path);
$noBaseUri = $uri->withPath($noBase);
- $request = $request->withUri($noBaseUri)->withAttribute('original_uri', $uri);
+ $rewrittenRequest = $request->withUri($noBaseUri)->withAttribute('original_uri', $uri);
- return call_user_func($next, $request, $response);
+ return $next($rewrittenRequest, $response);
}
/**
@@ -100,19 +102,17 @@ class BasePath
}
/**
- * Set error response
+ * Respond with 404 Not Found
*
- * @param ResponseInterface $response
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
* @return ResponseInterface
*/
- protected function setError($response)
+ protected function notFound(ServerRequestInterface $request, ResponseInterface $response)
{
- $message = 'Not Found';
-
- $body = $response->getBody();
- $body->rewind();
- $body->write($message);
+ $notFound = $response->withStatus(404);
+ $notFound->getBody()->write('Not Found');
- return $response->withStatus(404, $message)->withBody($body);
+ return $notFound;
}
}
diff --git a/src/Router/Middleware/ErrorHandler.php b/src/Router/Middleware/ErrorHandler.php
deleted file mode 100644
index 789c455..0000000
--- a/src/Router/Middleware/ErrorHandler.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-namespace Jasny\Router\Middleware;
-
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\ResponseInterface;
-
-/**
- * Handle error in following middlewares/app actions
- */
-class ErrorHandler
-{
- /**
- * Run middleware action
- *
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
- * @param callback $next
- * @return ResponseInterface
- */
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null)
- {
- if ($next && !is_callable($next)) {
- throw new \InvalidArgumentException("'next' should be a callback");
- }
-
- $error = false;
-
- try {
- $response = $next ? call_user_func($next, $request, $response) : $response;
- } catch(\Throwable $e) {
- $error = true;
- } catch(\Exception $e) { #This block can be removed when migrating to PHP7, because Throwable represents both Exception and Error
- $error = true;
- }
-
- return $error ? $this->handleError($response) : $response;
- }
-
- /**
- * Handle caught error
- *
- * @param ResponseInterface $response
- * @return ResponseInterface
- */
- protected function handleError($response)
- {
- $body = $response->getBody();
- $body->rewind();
- $body->write('Unexpected error');
-
- return $response->withStatus(500, 'Internal Server Error')->withBody($body);
- }
-}
diff --git a/src/Router/Middleware/NotFound.php b/src/Router/Middleware/NotFound.php
index 97b4a51..2484a43 100644
--- a/src/Router/Middleware/NotFound.php
+++ b/src/Router/Middleware/NotFound.php
@@ -29,6 +29,7 @@ class NotFound
**/
protected $methodNotAllowed = null;
+
/**
* Class constructor
*
@@ -38,12 +39,22 @@ class NotFound
*/
public function __construct(Routes $routes, $notFound = 404, $methodNotAllowed = null)
{
- if (!(is_numeric($notFound) && $notFound >= 100 && $notFound <= 599) && !is_callable($notFound)) {
- throw new \InvalidArgumentException("'Not found' parameter should be a code in range 100-599 or a callback");
+ if (is_string($notFound) && ctype_digit($notFound)) {
+ $notFound = (int)$notFound;
+ }
+ if (!(is_int($notFound) && $notFound >= 100 && $notFound <= 999) && !is_callable($notFound)) {
+ throw new \InvalidArgumentException("'notFound' should be valid HTTP status code or a callback");
}
- if ($methodNotAllowed && !(is_numeric($methodNotAllowed) && $methodNotAllowed >= 100 && $methodNotAllowed <= 599) && !is_callable($methodNotAllowed)) {
- throw new \InvalidArgumentException("'Method not allowed' parameter should be a code in range 100-599 or a callback");
+ if (is_string($methodNotAllowed) && ctype_digit($methodNotAllowed)) {
+ $methodNotAllowed = (int)$methodNotAllowed;
+ }
+ if (
+ isset($methodNotAllowed) &&
+ !(is_int($methodNotAllowed) && $methodNotAllowed >= 100 && $methodNotAllowed <= 999) &&
+ !is_callable($methodNotAllowed)
+ ) {
+ throw new \InvalidArgumentException("'methodNotAllowed' should be valid HTTP status code or a callback");
}
$this->routes = $routes;
@@ -60,6 +71,7 @@ class NotFound
{
return $this->routes;
}
+
/**
* Run middleware action
@@ -69,37 +81,34 @@ class NotFound
* @param callback $next
* @return ResponseInterface
*/
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null)
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
- if ($next && !is_callable($next)) {
+ if (!is_callable($next)) {
throw new \InvalidArgumentException("'next' should be a callback");
}
- if ($this->getRoutes()->hasRoute($request)) {
- return $next ? $next($request, $response) : $response;
- }
-
- $status = $this->methodNotAllowed && $this->getRoutes()->hasRoute($request, false) ?
- $this->methodNotAllowed : $this->notFound;
+ if (!$this->getRoutes()->hasRoute($request)) {
+ $status = $this->methodNotAllowed && $this->getRoutes()->hasRoute($request, false) ?
+ $this->methodNotAllowed : $this->notFound;
- return is_numeric($status) ? $this->simpleResponse($response, $status) : call_user_func($status, $request, $response);
+ return is_numeric($status) ? $this->simpleResponse($response, $status) : $status($request, $response);
+ }
+
+ return $next($request, $response);
}
/**
* Simple response
*
* @param ResponseInterface $response
- * @param int $code
+ * @param int $code
* @return ResponseInterface
*/
protected function simpleResponse(ResponseInterface $response, $code)
{
- $message = 'Not Found';
-
- $body = $response->getBody();
- $body->rewind();
- $body->write($message);
+ $notFound = $response->withStatus($code);
+ $notFound->getBody()->write('Not found');
- return $response->withStatus($code, $message)->withBody($body);
+ return $notFound;
}
}
diff --git a/src/Router/Route.php b/src/Router/Route.php
index be5a052..d2158ea 100644
--- a/src/Router/Route.php
+++ b/src/Router/Route.php
@@ -10,27 +10,21 @@ class Route extends \stdClass
/**
* Class constructor
*
- * @param array $values
+ * @param array|stdClass $values
*/
- public function __construct(array $values)
- {
- foreach ($values as $key => $value) {
- $this->$key = $value;
- }
- }
-
- /**
- * Factory method
- *
- * @param array|\stdClass $values
- * @return Route
- */
- public static function create($values)
+ public function __construct($values)
{
if ($values instanceof \stdClass) {
$values = get_object_vars($values);
}
- return new static($values);
+ if (!is_array($values)) {
+ $type = (is_object($values) ? get_class($values) . ' ' : '') . gettype($values);
+ throw new \InvalidArgumentException("Route values should be an array, not a $type");
+ }
+
+ foreach ($values as $key => $value) {
+ $this->$key = $value;
+ }
}
}
diff --git a/src/Router/Routes/Glob.php b/src/Router/Routes/Glob.php
index fcece76..a4495ee 100644
--- a/src/Router/Routes/Glob.php
+++ b/src/Router/Routes/Glob.php
@@ -6,6 +6,7 @@ use ArrayObject;
use Jasny\Router\UrlParsing;
use Jasny\Router\Routes;
use Jasny\Router\Route;
+use Jasny\Router\Routes\RouteBinding;
use Psr\Http\Message\ServerRequestInterface;
/**
@@ -14,6 +15,19 @@ use Psr\Http\Message\ServerRequestInterface;
class Glob extends ArrayObject implements Routes
{
use UrlParsing;
+ use RouteBinding;
+
+ /**
+ * Class constructor
+ *
+ * @param Routes[]|array|\Traversable $input
+ * @param int $flags
+ */
+ public function __construct($input = [], $flags = 0)
+ {
+ $routes = $this->createRoutes($input);
+ parent::__construct($routes, $flags);
+ }
/**
* Create a route from an assisiative array or stdClass object
@@ -35,14 +49,7 @@ class Glob extends ArrayObject implements Routes
throw new \InvalidArgumentException("Unable to create a Route from value " . var_export($value, true));
}
- $route = Route::create($value);
-
- if (!isset($route)) {
- throw new \InvalidArgumentException("Unable to create a Route from " . var_export($value, true) . ": "
- . "neither 'controller', 'fn' or 'file' key is defined");
- }
-
- return $route;
+ return new Route($value);
}
/**
@@ -157,200 +164,6 @@ class Glob extends ArrayObject implements Routes
/**
- * Fill out the routes variables based on the url parts.
- *
- * @param array|\stdClass $vars Route variables
- * @param ServerRequestInterface $request
- * @param array $parts URL parts
- * @return array
- */
- protected function bind($vars, ServerRequestInterface $request, array $parts)
- {
- $values = [];
- $type = is_array($vars) && array_keys($vars) === array_keys(array_keys($vars)) ? 'numeric' : 'assoc';
-
- foreach ($vars as $key => $var) {
- if (!isset($var)) continue;
-
- if (is_object($var) && !$var instanceof \stdClass) {
- $part = array($var);
- } elseif (!is_scalar($var)) {
- $part = array($this->bind($var, $request, $parts));
- } elseif ($var[0] === '$') {
- $options = array_map('trim', explode('|', $var));
- $part = $this->bindVar($type, $request, $parts, $options);
- } elseif ($var[0] === '~' && substr($var, -1) === '~') {
- $pieces = array_map('trim', explode('~', substr($var, 1, -1)));
- $bound = array_filter($this->bind($pieces, $request, $parts));
- $part = array(join('', $bound));
- } else {
- $part = array($var);
- }
-
- if ($type === 'assoc') {
- $values[$key] = $part[0];
- } else {
- $values = array_merge($values, $part);
- }
- }
-
- if ($vars instanceof Route) {
- $values = Route::create($values);
- } elseif (is_object($vars) && $type === 'assoc') {
- $values = (object)$values;
- }
-
- return $values;
- }
-
- /**
- * Bind variable
- *
- * @param string $type 'assoc' or 'numeric'
- * @param ServerRequestInterface $request
- * @param array $parts
- * @param array $options
- * @return array
- */
- protected function bindVar($type, ServerRequestInterface $request, array $parts, array $options)
- {
- foreach ($options as $option) {
- $value = null;
-
- $bound =
- $this->bindVarString($option, $value) ||
- $this->bindVarSuperGlobal($option, $request, $value) ||
- $this->bindVarRequestHeader($option, $request, $value) ||
- $this->bindVarMultipleUrlParts($option, $type, $parts, $value) ||
- $this->bindVarSingleUrlPart($option, $parts, $value);
-
- if ($bound && isset($value)) {
- return $value;
- }
- }
-
- return [null];
- }
-
- /**
- * Bind variable when option is a normal string
- *
- * @param string $option
- * @param mixed $value OUTPUT
- * @return boolean
- */
- protected function bindVarString($option, &$value)
- {
- if ($option[0] !== '$') {
- $value = [$option];
- return true;
- }
-
- return false;
- }
-
- /**
- * Bind variable when option is a super global
- *
- * @param string $option
- * @param mixed $value OUTPUT
- * @return boolean
- */
- protected function bindVarSuperGlobal($option, ServerRequestInterface $request, &$value)
- {
- if (preg_match('/^\$_(GET|POST|COOKIE)\[([^\[]*)\]$/i', $option, $matches)) {
- list(, $var, $key) = $matches;
-
- $var = strtolower($var);
- $data = null;
-
- if ($var === 'get') {
- $data = $request->getQueryParams();
- } elseif ($var === 'post') {
- $data = $request->getParsedBody();
- } elseif ($var === 'cookie') {
- $data = $request->getCookieParams();
- }
-
- $value = isset($data[$key]) ? [$data[$key]] : null;
- return true;
- }
-
- return false;
- }
-
- /**
- * Bind variable when option is a request header
- *
- * @param string $option
- * @param ServerRequestInterface $request
- * @param mixed $value OUTPUT
- * @return boolean
- */
- protected function bindVarRequestHeader($option, ServerRequestInterface $request, &$value)
- {
- if (preg_match('/^\$(?:HTTP_)?([A-Z_]+)$/', $option, $matches)) {
- $sentence = preg_replace('/[\W_]+/', ' ', $matches[1]);
- $name = str_replace(' ', '-', ucwords($sentence));
-
- $value = [$request->getHeaderLine($name)];
- return true;
- }
-
- return false;
- }
-
- /**
- * Bind variable when option contains multiple URL parts
- *
- * @param string $option
- * @param string $type 'assoc' or 'numeric'
- * @param array $parts Url parts
- * @param mixed $value OUTPUT
- * @return boolean
- */
- protected function bindVarMultipleUrlParts($option, $type, array $parts, &$value)
- {
- if (substr($option, -3) === '...' && ctype_digit(substr($option, 1, -3))) {
- $i = (int)substr($option, 1, -3);
-
- if ($type === 'assoc') {
- throw new \InvalidArgumentException("Binding multiple parts using '$option' is only allowed in numeric arrays");
- } else {
- $value = array_slice($parts, $i - 1);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Bind variable when option contains a single URL part
- *
- * @param string $option
- * @param array $parts Url parts
- * @param mixed $value OUTPUT
- * @return boolean
- */
- protected function bindVarSingleUrlPart($option, array $parts, &$value)
- {
- if (ctype_digit(substr($option, 1))) {
- $i = (int)substr($option, 1);
- $part = array_slice($parts, $i - 1, 1);
-
- if (!empty($part)) {
- $value = $part;
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
* Check if a route for the URL exists
*
* @param ServerRequestInterface $request
diff --git a/src/Router/Routes/RouteBinding.php b/src/Router/Routes/RouteBinding.php
new file mode 100644
index 0000000..91563c3
--- /dev/null
+++ b/src/Router/Routes/RouteBinding.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Jasny\Router\Routes;
+
+use Jasny\Router\Route;
+use Psr\Http\Message\ServerRequestInterface;
+
+/**
+ * Functionality to set route properties based on url parameters
+ */
+trait RouteBinding
+{
+ /**
+ * Fill out the routes variables based on the url parts.
+ *
+ * @param array|\stdClass $vars Route variables
+ * @param ServerRequestInterface $request
+ * @param array $parts URL parts
+ * @return array
+ */
+ protected function bind($vars, ServerRequestInterface $request, array $parts)
+ {
+ $type = is_array($vars) && array_keys($vars) === array_keys(array_keys($vars)) ? 'numeric' : 'assoc';
+
+ $values = $this->bindParts($vars, $type, $request, $parts);
+
+ if ($vars instanceof Route) {
+ $class = get_class($vars);
+ $values = new $class($values);
+ } elseif (is_object($vars) && $type === 'assoc') {
+ $values = (object)$values;
+ }
+
+ return $values;
+ }
+
+
+ /**
+ * Fill out the values based on the url parts.
+ *
+ * @param array|\stdClass $vars Route variables
+ * @param string $type
+ * @param ServerRequestInterface $request
+ * @param array $parts URL parts
+ * @return array
+ */
+ protected function bindParts($vars, $type, ServerRequestInterface $request, array $parts)
+ {
+ $values = [];
+
+ foreach ($vars as $key => $var) {
+ $part = null;
+
+ $bound =
+ $this->bindPartObject($var, $part) ||
+ $this->bindPartArray($var, $request, $parts, $part) ||
+ $this->bindPartVar($var, $type, $request, $parts, $part) ||
+ $this->bindPartConcat($var, $request, $parts, $part) ||
+ $this->bindPartValue($var, $part);
+
+ if (!$bound) continue;
+
+ if ($type === 'assoc') {
+ $values[$key] = $part[0];
+ } else {
+ $values = array_merge($values, $part);
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Bind part if it's an object
+ *
+ * @param mixed $var
+ * @param array $part OUTPUT
+ * @return boolean
+ */
+ protected function bindPartObject($var, &$part)
+ {
+ if (!is_object($var) || $var instanceof \stdClass) {
+ return false;
+ }
+
+ $part = [$var];
+ return true;
+ }
+
+ /**
+ * Bind part if it's an array
+ *
+ * @param mixed $var
+ * @param ServerRequestInterface $request
+ * @param array $parts
+ * @param array $part OUTPUT
+ * @return boolean
+ */
+ protected function bindPartArray($var, ServerRequestInterface $request, array $parts, &$part)
+ {
+ if (!is_array($var) && !$var instanceof \stdClass) {
+ return false;
+ }
+
+ $part = [$this->bind($var, $request, $parts)];
+ return true;
+ }
+
+ /**
+ * Bind part if it's an variable
+ *
+ * @param mixed $var
+ * @param string $type
+ * @param ServerRequestInterface $request
+ * @param array $parts
+ * @param array $part OUTPUT
+ * @return boolean
+ */
+ protected function bindPartVar($var, $type, ServerRequestInterface $request, array $parts, &$part)
+ {
+ if (!is_string($var) || $var[0] !== '$') {
+ return false;
+ }
+
+ $options = array_map('trim', explode('|', $var));
+ $part = $this->bindVar($type, $request, $parts, $options);
+ return true;
+ }
+
+ /**
+ * Bind part if it's an concatenation
+ *
+ * @param mixed $var
+ * @param ServerRequestInterface $request
+ * @param array $parts
+ * @param array $part OUTPUT
+ * @return boolean
+ */
+ protected function bindPartConcat($var, ServerRequestInterface $request, array $parts, &$part)
+ {
+ if (!is_string($var) || $var[0] !== '~' || substr($var, -1) !== '~') {
+ return false;
+ }
+
+ $pieces = array_map('trim', explode('~', substr($var, 1, -1)));
+ $bound = array_filter($this->bind($pieces, $request, $parts));
+ $part = [join('', $bound)];
+
+ return true;
+ }
+
+ /**
+ * Bind part if it's a normal value
+ *
+ * @param mixed $var
+ * @param array $part OUTPUT
+ * @return boolean
+ */
+ protected function bindPartValue($var, &$part)
+ {
+ if (!isset($var)) {
+ return false;
+ }
+
+ $part = [$var];
+ return true;
+ }
+
+ /**
+ * Bind variable
+ *
+ * @param string $type 'assoc' or 'numeric'
+ * @param ServerRequestInterface $request
+ * @param array $parts
+ * @param array $options
+ * @return array
+ */
+ protected function bindVar($type, ServerRequestInterface $request, array $parts, array $options)
+ {
+ foreach ($options as $option) {
+ $value = null;
+
+ $bound =
+ $this->bindVarString($option, $value) ||
+ $this->bindVarSuperGlobal($option, $request, $value) ||
+ $this->bindVarRequestHeader($option, $request, $value) ||
+ $this->bindVarMultipleUrlParts($option, $type, $parts, $value) ||
+ $this->bindVarSingleUrlPart($option, $parts, $value);
+
+ if ($bound && isset($value)) {
+ return $value;
+ }
+ }
+
+ return [null];
+ }
+
+ /**
+ * Bind variable when option is a normal string
+ *
+ * @param string $option
+ * @param mixed $value OUTPUT
+ * @return boolean
+ */
+ protected function bindVarString($option, &$value)
+ {
+ if ($option[0] !== '$') {
+ $value = [$option];
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Bind variable when option is a super global
+ *
+ * @param string $option
+ * @param mixed $value OUTPUT
+ * @return boolean
+ */
+ protected function bindVarSuperGlobal($option, ServerRequestInterface $request, &$value)
+ {
+ if (preg_match('/^\$_(GET|POST|COOKIE)\[([^\[]*)\]$/i', $option, $matches)) {
+ list(, $var, $key) = $matches;
+
+ $var = strtolower($var);
+ $data = null;
+
+ if ($var === 'get') {
+ $data = $request->getQueryParams();
+ } elseif ($var === 'post') {
+ $data = $request->getParsedBody();
+ } elseif ($var === 'cookie') {
+ $data = $request->getCookieParams();
+ }
+
+ $value = isset($data[$key]) ? [$data[$key]] : null;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Bind variable when option is a request header
+ *
+ * @param string $option
+ * @param ServerRequestInterface $request
+ * @param mixed $value OUTPUT
+ * @return boolean
+ */
+ protected function bindVarRequestHeader($option, ServerRequestInterface $request, &$value)
+ {
+ if (preg_match('/^\$(?:HTTP_)?([A-Z_]+)$/', $option, $matches)) {
+ $sentence = preg_replace('/[\W_]+/', ' ', $matches[1]);
+ $name = str_replace(' ', '-', ucwords($sentence));
+
+ $value = [$request->getHeaderLine($name)];
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Bind variable when option contains multiple URL parts
+ *
+ * @param string $option
+ * @param string $type 'assoc' or 'numeric'
+ * @param array $parts Url parts
+ * @param mixed $value OUTPUT
+ * @return boolean
+ */
+ protected function bindVarMultipleUrlParts($option, $type, array $parts, &$value)
+ {
+ if (substr($option, -3) === '...' && ctype_digit(substr($option, 1, -3))) {
+ $i = (int)substr($option, 1, -3);
+
+ if ($type === 'assoc') {
+ throw new \InvalidArgumentException("Binding multiple parts using '$option' is only allowed in numeric arrays");
+ } else {
+ $value = array_slice($parts, $i - 1);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Bind variable when option contains a single URL part
+ *
+ * @param string $option
+ * @param array $parts Url parts
+ * @param mixed $value OUTPUT
+ * @return boolean
+ */
+ protected function bindVarSingleUrlPart($option, array $parts, &$value)
+ {
+ if (ctype_digit(substr($option, 1))) {
+ $i = (int)substr($option, 1);
+ $part = array_slice($parts, $i - 1, 1);
+
+ if (!empty($part)) {
+ $value = $part;
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/Router/Runner.php b/src/Router/Runner.php
index 87f082e..c671fa9 100644
--- a/src/Router/Runner.php
+++ b/src/Router/Runner.php
@@ -4,7 +4,6 @@ namespace Jasny\Router;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
-use Jasny\Router\Route;
/**
* A runner can be invoked in order to run the action specified in a route
@@ -23,20 +22,19 @@ abstract class Runner
/**
* Invoke the action specified in the route and call the next method
*
- * @param ServerRequestInterface $request
- * @param ResponseInterface $response
- * @param callback $next Callback for if runner is used as middleware
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @param callback $next Callback for if runner is used as middleware
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next = null)
{
- $response = $this->run($request, $response);
+ $newResponse = $this->run($request, $response);
if (isset($next)) {
- $response = call_user_func($next, $request, $response);
+ $newResponse = call_user_func($next, $request, $newResponse);
}
- return $response;
+ return $newResponse;
}
}
-
diff --git a/src/Router/Runner/Callback.php b/src/Router/Runner/Callback.php
index 7f4c457..8fa8c0e 100644
--- a/src/Router/Runner/Callback.php
+++ b/src/Router/Runner/Callback.php
@@ -29,6 +29,6 @@ class Callback extends Runner
throw new \RuntimeException("'fn' property of route shoud be a callable");
}
- return call_user_func($callback, $request, $response);
+ return $callback($request, $response);
}
}
diff --git a/src/Router/Runner/Controller.php b/src/Router/Runner/Controller.php
index 56cf2b5..caca48f 100644
--- a/src/Router/Runner/Controller.php
+++ b/src/Router/Runner/Controller.php
@@ -7,13 +7,34 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
- * Description of Controller
- *
- * @author arnold
+ * Run a route using a controller
*/
class Controller extends Runner
{
/**
+ * Get class name from controller name
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function getClass($name)
+ {
+ return strstr($name, '-') ? \Jasny\studlycase($name) : $name;
+ }
+
+ /**
+ * Instantiate a controller object
+ * @codeCoverageIgnore
+ *
+ * @param string $class
+ * @return callable|object
+ */
+ protected function instantiate($class)
+ {
+ return new $class();
+ }
+
+ /**
* Route to a controller
*
* @param ServerRequestInterface $request
@@ -23,18 +44,20 @@ class Controller extends Runner
public function run(ServerRequestInterface $request, ResponseInterface $response)
{
$route = $request->getAttribute('route');
- $class = !empty($route->controller) ? $route->controller : null;
+ $name = !empty($route->controller) ? $route->controller : null;
+ $class = $this->getClass($name);
+
if (!class_exists($class)) {
throw new \RuntimeException("Can not route to controller '$class': class not exists");
}
-
+
if (!method_exists($class, '__invoke')) {
throw new \RuntimeException("Can not route to controller '$class': class does not have '__invoke' method");
}
-
- $controller = new $class($route);
-
+
+ $controller = $this->instantiate($class);
+
return $controller($request, $response);
}
}
diff --git a/src/Router/Runner/PhpScript.php b/src/Router/Runner/PhpScript.php
index d223bd7..51480c5 100644
--- a/src/Router/Runner/PhpScript.php
+++ b/src/Router/Runner/PhpScript.php
@@ -10,7 +10,18 @@ use Psr\Http\Message\ResponseInterface;
* Route to a PHP script
*/
class PhpScript extends Runner
-{
+{
+ /**
+ * Include a file
+ * @param type $file
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ */
+ protected function includeScript($file, ServerRequestInterface $request, ResponseInterface $response)
+ {
+ return include $file;
+ }
+
/**
* Route to a file
*
@@ -23,16 +34,16 @@ class PhpScript extends Runner
$route = $request->getAttribute('route');
$file = !empty($route->file) ? ltrim($route->file, '/') : '';
- if (!file_exists($file)) {
- throw new \RuntimeException("Failed to route using '$file': File '$file' doesn't exist.");
- }
-
if ($file[0] === '~' || strpos($file, '..') !== false) {
- throw new \RuntimeException("Won't route using '$file': '~', '..' are not allowed in filename.");
+ throw new \RuntimeException("Won't route to '$file': '~', '..' are not allowed in filename");
}
- $result = include $file;
+ if (!file_exists($file)) {
+ throw new \RuntimeException("Failed to route using '$file': File doesn't exist");
+ }
+
+ $result = $this->includeScript($file, $request, $response);
- return $result === true ? $response : $result;
+ return $result === true || $result === 1 ? $response : $result;
}
}
diff --git a/src/Router/Runner/RunnerFactory.php b/src/Router/RunnerFactory.php
index e32d2cc..08ed7b0 100644
--- a/src/Router/Runner/RunnerFactory.php
+++ b/src/Router/RunnerFactory.php
@@ -1,8 +1,9 @@
<?php
-namespace Jasny\Router\Runner;
+namespace Jasny\Router;
use Jasny\Router\Route;
+use Jasny\Router\Runner;
/**
* Factory of Runner instances
@@ -18,11 +19,11 @@ class RunnerFactory
public function __invoke(Route $route)
{
if (isset($route->controller)) {
- $class = Controller::class;
+ $class = Runner\Controller::class;
} elseif (isset($route->fn)) {
- $class = Callback::class;
+ $class = Runner\Callback::class;
} elseif (isset($route->file)) {
- $class = PhpScript::class;
+ $class = Runner\PhpScript::class;
} else {
throw new \InvalidArgumentException("Route has neither 'controller', 'fn' or 'file' defined");
}
@@ -30,4 +31,3 @@ class RunnerFactory
return new $class();
}
}
-
diff --git a/src/Router/UrlParsing.php b/src/Router/UrlParsing.php
index 5727085..ee89260 100644
--- a/src/Router/UrlParsing.php
+++ b/src/Router/UrlParsing.php
@@ -31,10 +31,6 @@ trait UrlParsing
$url = rtrim($url, '/');
}
- if (substr($url, 0, 2) == '/:') {
- $url = substr($url, 2);
- }
-
return $url;
}
}