summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml20
-rw-r--r--README.md2
-rw-r--r--composer.json6
-rw-r--r--src/Router.php43
-rw-r--r--src/Router/Routes/Glob.php13
-rw-r--r--src/Router/Runner.php49
-rw-r--r--src/Router/Runner/Callback.php3
-rw-r--r--src/Router/Runner/Controller.php5
-rw-r--r--src/Router/Runner/PhpScript.php15
-rw-r--r--src/Router/Runner/RunnerFactory.php33
-rw-r--r--tests/Router/Routes/GlobTest.php23
-rw-r--r--tests/Router/Runner/CallbackTest.php4
-rw-r--r--tests/Router/Runner/ControllerTest.php2
-rw-r--r--tests/Router/Runner/PhpScriptTest.php6
-rw-r--r--tests/Router/Runner/RunnerFactoryTest.php41
-rw-r--r--tests/Router/RunnerTest.php33
-rw-r--r--tests/RouterTest.php40
17 files changed, 211 insertions, 127 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6f0320f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+
+php:
+ - 5.6
+ - 7.0
+
+branches:
+ only:
+ - master
+
+install:
+ - composer install
+ - wget https://scrutinizer-ci.com/ocular.phar -O "$HOME/ocular.phar"
+
+script:
+ - vendor/bin/phpunit --coverage-clover cache/logs/clover.xml
+
+after_success:
+ - php "$HOME/ocular.phar" code-coverage:upload --format=php-clover cache/logs/clover.xml
+
diff --git a/README.md b/README.md
index 5aa2cf1..54e1f66 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@ Jasny Router
============
[![Build Status](https://secure.travis-ci.org/jasny/router.png?branch=master)](http://travis-ci.org/jasny/router)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/jasny/router/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/jasny/router/?branch=master)
+[![Code Coverage](https://scrutinizer-ci.com/g/jasny/router/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/jasny/router/?branch=master)
Installation
diff --git a/composer.json b/composer.json
index 7a1dfc6..147e6ea 100644
--- a/composer.json
+++ b/composer.json
@@ -19,12 +19,12 @@
"jasny/php-functions": "^2.0",
"psr/http-message": "^1.0"
},
+ "require-dev": {
+ "jasny/php-code-quality": "^2.0"
+ },
"autoload": {
"psr-4": {
"Jasny\\": "src/"
}
- },
- "require-dev": {
- "jasny/php-code-quality": "^2.0"
}
}
diff --git a/src/Router.php b/src/Router.php
index 18c40be..6d38678 100644
--- a/src/Router.php
+++ b/src/Router.php
@@ -2,7 +2,7 @@
namespace Jasny;
-use Jasny\Router\Runner;
+use Jasny\Router\Runner\RunnerFactory;
use Jasny\Router\Routes\Glob;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
@@ -23,6 +23,12 @@ class Router
* @var array
**/
protected $middlewares = [];
+
+ /**
+ * Factory of Runner objects
+ * @var RunnerFactory
+ **/
+ protected $factory = null;
/**
* Class constructor
@@ -55,6 +61,37 @@ class Router
}
/**
+ * Get factory of Runner objects
+ *
+ * @return RunnerFactory
+ */
+ public function getFactory()
+ {
+ if (!isset($this->factory)) {
+ $this->factory = new RunnerFactory();
+ }
+
+ return $this->factory;
+ }
+
+ /**
+ * Set the factory of Runner objects
+ *
+ * @param callable $factory
+ * @return Router $this
+ */
+ public function setFactory($factory)
+ {
+ if (!is_callable($factory)) {
+ throw new \InvalidArgumentException("Factory must be a callable");
+ }
+
+ $this->factory = $factory;
+
+ return $this;
+ }
+
+ /**
* Add middleware call to router
*
* @param callback $middleware
@@ -125,7 +162,9 @@ class Router
if (!$route) return $this->notFound($response);
- $runner = Runner::create($route);
+ $request->withAttribute('route', $route);
+ $factory = $this->getFactory();
+ $runner = $factory($route);
return $runner($request, $response, $next);
}
diff --git a/src/Router/Routes/Glob.php b/src/Router/Routes/Glob.php
index a9c2148..fcece76 100644
--- a/src/Router/Routes/Glob.php
+++ b/src/Router/Routes/Glob.php
@@ -61,19 +61,6 @@ class Glob extends ArrayObject implements Routes
}
/**
- * Class constructor
- *
- * @param Route[]|array|\Traversable $input
- * @param type $flags
- * @param type $iterator_class
- */
- public function __construct($input = [], $flags = 0, $iterator_class = "ArrayIterator")
- {
- parent::__construct($input, $flags, $iterator_class);
- }
-
-
- /**
* {@inheritdoc}
*/
public function append($route)
diff --git a/src/Router/Runner.php b/src/Router/Runner.php
index 51300a4..87f082e 100644
--- a/src/Router/Runner.php
+++ b/src/Router/Runner.php
@@ -10,32 +10,7 @@ use Jasny\Router\Route;
* A runner can be invoked in order to run the action specified in a route
*/
abstract class Runner
-{
- /**
- * @var \stdClass
- */
- protected $route;
-
- /**
- * Class constructor
- *
- * @param \stdClass $route
- */
- public function __construct(\stdClass $route)
- {
- $this->route = $route;
- }
-
- /**
- * Get runner route
- *
- * @return Route
- */
- public function getRoute()
- {
- return $this->route;
- }
-
+{
/**
* Invoke the action specified in the route
*
@@ -63,27 +38,5 @@ abstract class Runner
return $response;
}
-
- /**
- * Factory method
- *
- * @param Route $route
- * @return Runner
- * @throws \RuntimeException if the route is misconfigured
- */
- public static function create(Route $route)
- {
- if (isset($route->controller)) {
- $class = Runner\Controller::class;
- } elseif (isset($route->fn)) {
- $class = Runner\Callback::class;
- } elseif (isset($route->file)) {
- $class = Runner\PhpScript::class;
- } else {
- throw new \RuntimeException("Route has neither 'controller', 'fn' or 'file' defined");
- }
-
- return new $class($route);
- }
}
diff --git a/src/Router/Runner/Callback.php b/src/Router/Runner/Callback.php
index 34ebc4c..7f4c457 100644
--- a/src/Router/Runner/Callback.php
+++ b/src/Router/Runner/Callback.php
@@ -22,7 +22,8 @@ class Callback extends Runner
*/
public function run(ServerRequestInterface $request, ResponseInterface $response)
{
- $callback = !empty($this->route->fn) ? $this->route->fn : null;
+ $route = $request->getAttribute('route');
+ $callback = !empty($route->fn) ? $route->fn : null;
if (!is_callable($callback)) {
throw new \RuntimeException("'fn' property of route shoud be a callable");
diff --git a/src/Router/Runner/Controller.php b/src/Router/Runner/Controller.php
index 0488be5..56cf2b5 100644
--- a/src/Router/Runner/Controller.php
+++ b/src/Router/Runner/Controller.php
@@ -22,7 +22,8 @@ class Controller extends Runner
*/
public function run(ServerRequestInterface $request, ResponseInterface $response)
{
- $class = !empty($this->route->controller) ? $this->route->controller : null;
+ $route = $request->getAttribute('route');
+ $class = !empty($route->controller) ? $route->controller : null;
if (!class_exists($class)) {
throw new \RuntimeException("Can not route to controller '$class': class not exists");
@@ -32,7 +33,7 @@ class Controller extends Runner
throw new \RuntimeException("Can not route to controller '$class': class does not have '__invoke' method");
}
- $controller = new $class($this->route);
+ $controller = new $class($route);
return $controller($request, $response);
}
diff --git a/src/Router/Runner/PhpScript.php b/src/Router/Runner/PhpScript.php
index f6595a3..d223bd7 100644
--- a/src/Router/Runner/PhpScript.php
+++ b/src/Router/Runner/PhpScript.php
@@ -10,17 +10,7 @@ use Psr\Http\Message\ResponseInterface;
* Route to a PHP script
*/
class PhpScript extends Runner
-{
- /**
- * Return route file path
- *
- * @return string
- */
- public function __toString()
- {
- return (string)$this->route->file;
- }
-
+{
/**
* Route to a file
*
@@ -30,7 +20,8 @@ class PhpScript extends Runner
*/
public function run(ServerRequestInterface $request, ResponseInterface $response)
{
- $file = !empty($this->route->file) ? ltrim($this->route->file, '/') : '';
+ $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.");
diff --git a/src/Router/Runner/RunnerFactory.php b/src/Router/Runner/RunnerFactory.php
new file mode 100644
index 0000000..e32d2cc
--- /dev/null
+++ b/src/Router/Runner/RunnerFactory.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Jasny\Router\Runner;
+
+use Jasny\Router\Route;
+
+/**
+ * Factory of Runner instances
+ */
+class RunnerFactory
+{
+ /**
+ * Create Runner instance
+ *
+ * @param Route $route
+ * @return Runner
+ */
+ public function __invoke(Route $route)
+ {
+ if (isset($route->controller)) {
+ $class = Controller::class;
+ } elseif (isset($route->fn)) {
+ $class = Callback::class;
+ } elseif (isset($route->file)) {
+ $class = PhpScript::class;
+ } else {
+ throw new \InvalidArgumentException("Route has neither 'controller', 'fn' or 'file' defined");
+ }
+
+ return new $class();
+ }
+}
+
diff --git a/tests/Router/Routes/GlobTest.php b/tests/Router/Routes/GlobTest.php
index 30ceddc..3ed4d21 100644
--- a/tests/Router/Routes/GlobTest.php
+++ b/tests/Router/Routes/GlobTest.php
@@ -1,9 +1,16 @@
<?php
+namespace Jasny\Router\Routes;
+
use Jasny\Router\Routes\Glob;
use Psr\Http\Message\ServerRequestInterface;
-class GlobTest extends PHPUnit_Framework_TestCase
+use ArrayObject;
+use BadMethodCallException;
+use InvalidArgumentException;
+use AppendIterator;
+
+class GlobTest extends \PHPUnit_Framework_TestCase
{
/**
* Test creating Glob object
@@ -25,18 +32,22 @@ class GlobTest extends PHPUnit_Framework_TestCase
}
$this->assertEquals(0, $count);
-
+ }
+
+ public function testConstructorWithArguments()
+ {
#Test with params
$values = [
'/foo/bar' => ['controller' => 'value1'],
'/foo/*' => ['fn' => 'value3'],
'/foo/*/bar' => ['file' => 'value5'],
];
- $glob = new Glob($values, ArrayObject::ARRAY_AS_PROPS, 'AppendIterator');
+
+ $glob = new Glob($values, ArrayObject::ARRAY_AS_PROPS, AppendIterator::class);
$this->assertEquals(count($values), $glob->count(), "Routes count is not match");
$this->assertEquals(ArrayObject::ARRAY_AS_PROPS, $glob->getFlags(), "Flags are not correct");
- $this->assertEquals('AppendIterator', $glob->getIteratorClass(), "Iterator class is not correct");
+ $this->assertEquals(AppendIterator::class, $glob->getIteratorClass(), "Iterator class is not correct");
foreach ($values as $pattern => $options) {
$this->assertTrue($glob->offsetExists($pattern), "Key '$pattern' is missing");
@@ -121,6 +132,7 @@ class GlobTest extends PHPUnit_Framework_TestCase
['/foo/*', ['fn' => 'bar'], ''],
['/foo/*', ['file' => 'bar'], ''],
['', ['controller' => 'bar'], BadMethodCallException::class],
+ ['foo', 'bar', InvalidArgumentException::class],
['', '', BadMethodCallException::class]
];
}
@@ -148,7 +160,8 @@ class GlobTest extends PHPUnit_Framework_TestCase
{
$glob = new Glob();
- $this->assertEquals($positive, $glob->fnmatch($pattern, $uri), "Pattern and uri should " . ($positive ? "" : "not") . " match");
+ $this->assertEquals($positive, $glob->fnmatch($pattern, $uri),
+ "Pattern and uri should " . ($positive ? "" : "not") . " match");
}
/**
diff --git a/tests/Router/Runner/CallbackTest.php b/tests/Router/Runner/CallbackTest.php
index b4ee4ef..8a31794 100644
--- a/tests/Router/Runner/CallbackTest.php
+++ b/tests/Router/Runner/CallbackTest.php
@@ -15,12 +15,12 @@ class CallbackTest extends PHPUnit_Framework_TestCase
* @param boolean $positive
*/
public function testCallback($route, $positive)
- {
+ {
$runner = new Callback($route);
- $this->assertEquals($route, $runner->getRoute(), "Route was not set correctly");
$request = $this->createMock(ServerRequestInterface::class);
$response = $this->createMock(ResponseInterface::class);
+ $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route));
if (!$positive) $this->expectException(\RuntimeException::class);
$result = $runner->run($request, $response);
diff --git a/tests/Router/Runner/ControllerTest.php b/tests/Router/Runner/ControllerTest.php
index 1d64622..433d36d 100644
--- a/tests/Router/Runner/ControllerTest.php
+++ b/tests/Router/Runner/ControllerTest.php
@@ -23,10 +23,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
public function testPhpScript($route, $positive)
{
$runner = new Controller($route);
- $this->assertEquals($route, $runner->getRoute(), "Route was not set correctly");
$request = $this->createMock(ServerRequestInterface::class);
$response = $this->createMock(ResponseInterface::class);
+ $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route));
if (!$positive) $this->expectException(\RuntimeException::class);
$result = $runner->run($request, $response);
diff --git a/tests/Router/Runner/PhpScriptTest.php b/tests/Router/Runner/PhpScriptTest.php
index 12b9219..2f31f73 100644
--- a/tests/Router/Runner/PhpScriptTest.php
+++ b/tests/Router/Runner/PhpScriptTest.php
@@ -17,18 +17,14 @@ class PhpScriptTest extends PHPUnit_Framework_TestCase
public function testPhpScript($route, $positive)
{
$runner = new PhpScript($route);
- $this->assertEquals($route, $runner->getRoute(), "Route was not set correctly");
$request = $this->createMock(ServerRequestInterface::class);
$response = $this->createMock(ResponseInterface::class);
+ $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route));
if (!$positive) $this->expectException(\RuntimeException::class);
$result = $runner->run($request, $response);
- if (!$positive) return;
-
- $this->assertEquals($runner->getRoute()->file, (string)$runner);
-
if ($route->type === 'returnTrue') {
$this->assertEquals($response, $result, "Request object was not returned as result");
} else {
diff --git a/tests/Router/Runner/RunnerFactoryTest.php b/tests/Router/Runner/RunnerFactoryTest.php
new file mode 100644
index 0000000..c18656d
--- /dev/null
+++ b/tests/Router/Runner/RunnerFactoryTest.php
@@ -0,0 +1,41 @@
+<?php
+
+use Jasny\Router\Route;
+use Jasny\Router\Runner\RunnerFactory;
+use Jasny\Router\Runner\Controller;
+use Jasny\Router\Runner\Callback;
+use Jasny\Router\Runner\PhpScript;
+
+class RunnerFactoryTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Test creating Runner object using factory
+ *
+ * @dataProvider createProvider
+ * @param Route $route
+ * @param string $class Runner class to use
+ * @param boolean $positive
+ */
+ public function testCreate($route, $class, $positive)
+ {
+ if (!$positive) $this->expectException(\InvalidArgumentException::class);
+
+ $factory = new RunnerFactory();
+ $runner = $factory($route);
+
+ $this->assertInstanceOf($class, $runner, "Runner object has invalid class");
+ }
+
+ /**
+ * Provide data fpr testing 'create' method
+ */
+ public function createProvider()
+ {
+ return [
+ [Route::create(['controller' => 'TestController', 'value' => 'test']), Controller::class, true],
+ [Route::create(['fn' => 'testFunction', 'value' => 'test']), Callback::class, true],
+ [Route::create(['file' => 'some_file.php', 'value' => 'test']), PhpScript::class, true],
+ [Route::create(['test' => 'test']), '', false],
+ ];
+ }
+}
diff --git a/tests/Router/RunnerTest.php b/tests/Router/RunnerTest.php
index 1a2f571..7db4c02 100644
--- a/tests/Router/RunnerTest.php
+++ b/tests/Router/RunnerTest.php
@@ -11,39 +11,6 @@ use Psr\Http\Message\ResponseInterface;
class RunnerTest extends PHPUnit_Framework_TestCase
{
/**
- * Test creating Runner object using factory method
- *
- * @dataProvider createProvider
- * @param Route $route
- * @param string $class Runner class to use
- * @param boolean $positive
- */
- public function testCreate($route, $class, $positive)
- {
- if (!$positive) $this->expectException(\RuntimeException::class);
-
- $runner = Runner::create($route);
-
- if (!$positive) return;
-
- $this->assertInstanceOf($class, $runner, "Runner object has invalid class");
- $this->assertEquals($route, $runner->getRoute(), "Route was not set correctly");
- }
-
- /**
- * Provide data fpr testing 'create' method
- */
- public function createProvider()
- {
- return [
- [Route::create(['controller' => 'TestController', 'value' => 'test']), Controller::class, true],
- [Route::create(['fn' => 'testFunction', 'value' => 'test']), Callback::class, true],
- [Route::create(['file' => 'some_file.php', 'value' => 'test']), PhpScript::class, true],
- [Route::create(['test' => 'test']), '', false],
- ];
- }
-
- /**
* Test runner __invoke method
*/
public function testInvoke()
diff --git a/tests/RouterTest.php b/tests/RouterTest.php
index ef19458..46574c4 100644
--- a/tests/RouterTest.php
+++ b/tests/RouterTest.php
@@ -2,6 +2,7 @@
use Jasny\Router;
use Jasny\Router\Route;
+use Jasny\Router\Runner\RunnerFactory;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
@@ -53,6 +54,8 @@ class RouterTest extends PHPUnit_Framework_TestCase
];
list($request, $response) = $this->getRequests();
+ $this->expectRequestRoute($request, $routes['/foo']);
+
$router = new Router($routes);
$result = $router($request, $response);
@@ -73,6 +76,8 @@ class RouterTest extends PHPUnit_Framework_TestCase
];
list($request, $response) = $this->getRequests();
+ $this->expectRequestRoute($request, $routes['/foo']);
+
$router = new Router($routes);
$result = $router($request, $response, function($arg1, $arg2) {
return ['request' => $arg1, 'response' => $arg2];
@@ -154,6 +159,8 @@ class RouterTest extends PHPUnit_Framework_TestCase
];
list($request, $response) = $this->getRequests();
+ $this->expectRequestRoute($request, $routes['/foo']);
+
$router = new Router($routes);
$router->add([$this, 'getMiddlewareCalledLast'])->add([$this, 'getMiddlewareCalledFirst']);
@@ -166,6 +173,28 @@ class RouterTest extends PHPUnit_Framework_TestCase
}
/**
+ * Test getting and setting runner factory
+ */
+ public function testRunnerFactory()
+ {
+ $router = new Router([]);
+ $factory = $router->getFactory();
+
+ $this->assertEquals(RunnerFactory::class, get_class($factory), "By default 'getFactory' should return 'RunnerFactory' instance, not " . get_class($factory));
+
+ $self = $router->setFactory(function() {
+ return 'test';
+ });
+ $factory = $router->getFactory();
+
+ $this->assertEquals($router, $self, "'setFactory' must return an instance of router");
+ $this->assertEquals('test', $factory(), "Factory was not set or got correctly");
+
+ $this->expectException(\InvalidArgumentException::class);
+ $router->setFactory('test');
+ }
+
+ /**
* Get requests for testing
*
* @return array
@@ -224,4 +253,15 @@ class RouterTest extends PHPUnit_Framework_TestCase
$response->expects($this->once())->method('withBody')->with($this->equalTo($stream))->will($this->returnSelf());
$response->expects($this->once())->method('withStatus')->with($this->equalTo(404), $this->equalTo('Not Found'))->will($this->returnSelf());
}
+
+ /**
+ * Expect that request will return given route
+ *
+ * @param ServerRequestInterface $request
+ * @param Route $route
+ */
+ public function expectRequestRoute(ServerRequestInterface $request, $route)
+ {
+ $request->expects($this->once())->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route));
+ }
}