summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnold Daniels <arnold@jasny.net>2016-11-03 23:12:33 +0100
committerArnold Daniels <arnold@jasny.net>2016-11-03 23:12:33 +0100
commitbd4cb77fbf04923fa31a08fdd1f33f2c0db87864 (patch)
tree846c5e559360e530d200d897f67b5af02f8795e5
parentc6bb6b4495804d0f546b5298ad1ac4d66898b351 (diff)
downloadrouter-origin/fix-tests.zip
router-origin/fix-tests.tar.gz
router-origin/fix-tests.tar.bz2
Move route binding (used in Glob) to a traitorigin/fix-tests
Added tests for Route Added missing @covers
-rw-r--r--src/Router/Route.php26
-rw-r--r--src/Router/Routes/Glob.php198
-rw-r--r--src/Router/Routes/RouteBinding.php314
-rw-r--r--tests/Router/RouteTest.php42
-rw-r--r--tests/Router/Routes/GlobTest.php216
-rw-r--r--tests/Router/Routes/RouteBindingTest.php263
6 files changed, 636 insertions, 423 deletions
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 4c6d93c..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,7 @@ use Psr\Http\Message\ServerRequestInterface;
class Glob extends ArrayObject implements Routes
{
use UrlParsing;
+ use RouteBinding;
/**
* Class constructor
@@ -47,7 +49,7 @@ class Glob extends ArrayObject implements Routes
throw new \InvalidArgumentException("Unable to create a Route from value " . var_export($value, true));
}
- return Route::create($value);
+ return new Route($value);
}
/**
@@ -162,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/tests/Router/RouteTest.php b/tests/Router/RouteTest.php
new file mode 100644
index 0000000..4a72d27
--- /dev/null
+++ b/tests/Router/RouteTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Jasny\Router;
+
+use Jasny\Router\Route;
+
+/**
+ * @covers Jasny\Router\Route
+ */
+class RouteTest extends \PHPUnit_Framework_TestCase
+{
+ public function provider()
+ {
+ return [
+ [['foo' => '$1', 'color' => 'red', 'number' => 42]],
+ [(object)['foo' => '$1', 'color' => 'red', 'number' => 42]]
+ ];
+ }
+
+ /**
+ * @dataProvider provider
+ *
+ * @param array|stdClass $values
+ */
+ public function testConstructionWithObject($values)
+ {
+ $route = new Route($values);
+
+ $this->assertAttributeSame('$1', 'foo', $route);
+ $this->assertAttributeSame('red', 'color', $route);
+ $this->assertAttributeSame(42, 'number', $route);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Route values should be an array, not a string
+ */
+ public function testConstructionInvalidArgument()
+ {
+ new Route('foo');
+ }
+}
diff --git a/tests/Router/Routes/GlobTest.php b/tests/Router/Routes/GlobTest.php
index a6767a8..5efa77f 100644
--- a/tests/Router/Routes/GlobTest.php
+++ b/tests/Router/Routes/GlobTest.php
@@ -10,6 +10,10 @@ use ArrayObject;
use BadMethodCallException;
use InvalidArgumentException;
+/**
+ * @covers Jasny\Router\Routes\Glob
+ * @covers Jasny\Router\UrlParsing
+ */
class GlobTest extends \PHPUnit_Framework_TestCase
{
/**
@@ -326,218 +330,6 @@ class GlobTest extends \PHPUnit_Framework_TestCase
}
/**
- * Test binding simple string when getting route
- */
- public function testBindVarString()
- {
- $uri = '/foo/bar';
- $values = [$uri => ['controller' => 'value1', 'check' => 'value1']];
-
- $glob = new Glob($values);
- $request = $this->getServerRequest($uri);
- $route = $glob->getRoute($request);
-
- $this->assertEquals($values[$uri]['check'], $route->check);
- }
-
- /**
- * Provide uri's and corresponding patterns for testBindVarSingleUrlPart()
- */
- public function bindVarSingleUrlPartProvider()
- {
- return [
- ['/*', '/test', ['check' => '$1'], 'test'],
- ['/', '/', ['check' => '$1|test'], 'test'],
- ['/foo/*/bar', '/foo/test/bar', ['check' => '$2'], 'test'],
- ['/foo/bar/*', '/foo/bar/test', ['check' => '$3'], 'test'],
- ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '$3', 'checkB' => '$5'], 'test1', 'test2'],
- ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '~$3~/~$5~'], 'test1/test2'],
- ['/', '/', ['check' => '$foo'], null]
- ];
- }
-
- /**
- * Test binding single url part to route option
- * @dataProvider bindVarSingleUrlPartProvider
- *
- * @param string $pattern
- * @param string $uri
- * @param array $options Route options
- * @param string $check Expected value for `check`
- * @param string $checkB Expected value for `checkB`
- */
- public function testBindVarSingleUrlPart($pattern, $uri, $options, $check, $checkB = null)
- {
- $values = [$pattern => $options];
-
- $glob = new Glob($values);
- $request = $this->getServerRequest($uri);
- $route = $glob->getRoute($request);
-
- $this->assertNotNull($route, "Route not found");
- $this->assertInstanceOf(Route::class, $route);
-
- $this->assertEquals($check, $route->check);
-
- if (isset($checkB)) {
- $this->assertEquals($checkB, $route->checkB);
- }
- }
-
- public function testBindVarWithObject()
- {
- $object = new \Exception(); // Could be anything, just not stdClass
- $glob = new Glob(['/' => ['object' => $object]]);
-
- $request = $this->getServerRequest('/');
- $route = $glob->getRoute($request);
-
- $this->assertNotNull($route, "Route not found");
- $this->assertInstanceOf(Route::class, $route);
-
- $this->assertSame($object, $route->object);
- }
-
- public function bindVarWithSubProvider()
- {
- return [
- [['group' => ['check' => '$1']], 'array'],
- [['group' => (object)['check' => '$1']], 'object'],
- [['group' => ['sub' => (object)['check' => '$1']]], 'array', 'object'],
- [['group' => (object)['sub' => ['check' => '$1']]], 'object', 'array']
- ];
- }
-
- /**
- * @dataProvider bindVarWithSubProvider
- *
- * @param array $options
- * @param string $type
- * @param string $subtype
- */
- public function testBindVarWithSub(array $options, $type, $subtype = null)
- {
- $glob = new Glob(['/*' => $options]);
-
- $request = $this->getServerRequest('/test');
- $route = $glob->getRoute($request);
-
- $this->assertNotNull($route, "Route not found");
- $this->assertInstanceOf(Route::class, $route);
-
- $this->assertInternalType($type, $route->group);
-
- $group = (array)$route->group;
-
- if (isset($subtype)) {
- $this->assertArrayHasKey('sub', $group);
- $this->assertInternalType($subtype, $group['sub']);
-
- $group = (array)$group['sub'];
- }
-
- $this->assertEquals($group, ['check' => 'test']);
- }
-
-
- /**
- * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts()
- */
- public function bindVarMultipleUrlPartsProvider()
- {
- return [
- ['/foo', ['check' => '$1...'], false, InvalidArgumentException::class],
- ['/', ['check' => ['$1...']], false],
- ['/foo', ['check' => ['$1...']], true],
- ['/foo/bar', ['check' => ['$1...'], 'checkB' => ['$2...']],
- InvalidArgumentException::class]
- ];
- }
-
- /**
- * Test binding multyple url parts to route option
- * @dataProvider bindVarMultipleUrlPartsProvider
- *
- * @param string $uri
- * @param array $options Route options
- * @param boolean $positive
- * @param string $exception
- */
- public function testBindVarMultipleUrlParts($uri, $options, $positive, $exception = false)
- {
- if ($exception) {
- $this->expectException($exception);
- }
-
- $glob = new Glob([$uri => $options]);
- $request = $this->getServerRequest($uri);
- $route = $glob->getRoute($request);
-
- if ($exception) return;
-
- $this->assertNotNull($route, "Route not found");
- $this->assertInstanceOf(Route::class, $route);
-
- $values = explode('/', trim($uri, '/'));
-
- $positive ?
- $this->assertArraysEqual($values, $route->check, "Multyple url parts are not picked correctly") :
- $this->assertEmpty($route->check, "Multyple parts element should be empty");
-
- if (!empty($options->checkB)) {
- array_shift($values);
- $this->assertArraysEqual($values, $route->checkB, "Secondary multyple url parts are not picked correctly");
- }
- }
-
- /**
- * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts()
- */
- public function bindVarSuperGlobalProvider()
- {
- return [
- ['/foo', ['check' => '$_GET[check]'], 'get'],
- ['/foo', ['check' => '$_POST[check]'], 'post'],
- ['/foo', ['check' => '$_COOKIE[check]'], 'cookie']
- ];
- }
-
- /**
- * Test binding element of superglobal array to route option
- * @dataProvider bindVarSuperGlobalProvider
- *
- * @param string $uri
- * @param array $options
- * @param string $type ('get', 'post', 'cookie')
- */
- public function testBindVarSuperGlobal($uri, $options, $type)
- {
- $test = ['check' => 'test'];
- $glob = new Glob([$uri => $options]);
- $request = $this->getServerRequest($uri, 'GET', [$type => $test]);
- $route = $glob->getRoute($request);
-
- $this->assertEquals($test['check'], $route->check, "Did not obtaine value for superglobal '$type'");
- }
-
- /**
- * Test binding element of superglobal array to route option
- */
- public function testBindVarRequestHeader()
- {
- $uri = '/foo/bar';
- $test = 'test_header_value';
-
- $glob = new Glob([$uri => ['check' => '$HTTP_REFERER']]);
- $request = $this->getServerRequest($uri, 'GET', [], $test);
-
- $route = $glob->getRoute($request);
- $this->assertNotNull($route, "Route not found");
-
- $this->assertEquals($test, $route->check);
- }
-
- /**
* Get ServerRequestInterface object
*
* @param string $uri
diff --git a/tests/Router/Routes/RouteBindingTest.php b/tests/Router/Routes/RouteBindingTest.php
new file mode 100644
index 0000000..96536ac
--- /dev/null
+++ b/tests/Router/Routes/RouteBindingTest.php
@@ -0,0 +1,263 @@
+<?php
+
+namespace Jasny\Router\Routes;
+
+use Jasny\Router\Routes\Glob;
+use Jasny\Router\Route;
+use Psr\Http\Message\ServerRequestInterface;
+
+use InvalidArgumentException;
+
+/**
+ * @covers Jasny\Router\Routes\RouteBinding
+ */
+class RouteBindingTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * Test binding simple string when getting route
+ */
+ public function testBindVarString()
+ {
+ $uri = '/foo/bar';
+ $values = [$uri => ['controller' => 'value1', 'check' => 'value1']];
+
+ $glob = new Glob($values);
+ $request = $this->getServerRequest($uri);
+ $route = $glob->getRoute($request);
+
+ $this->assertEquals($values[$uri]['check'], $route->check);
+ }
+
+ /**
+ * Provide uri's and corresponding patterns for testBindVarSingleUrlPart()
+ */
+ public function bindVarSingleUrlPartProvider()
+ {
+ return [
+ ['/*', '/test', ['check' => '$1'], 'test'],
+ ['/', '/', ['check' => '$1|test'], 'test'],
+ ['/foo/*/bar', '/foo/test/bar', ['check' => '$2'], 'test'],
+ ['/foo/bar/*', '/foo/bar/test', ['check' => '$3'], 'test'],
+ ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '$3', 'checkB' => '$5'], 'test1', 'test2'],
+ ['/foo/bar/*/zet/*', '/foo/bar/test1/zet/test2', ['check' => '~$3~/~$5~'], 'test1/test2'],
+ ['/', '/', ['check' => '$foo'], null],
+ ['/', '/', ['check' => 'test', 'checkB' => null], 'test', null]
+ ];
+ }
+
+ /**
+ * Test binding single url part to route option
+ * @dataProvider bindVarSingleUrlPartProvider
+ *
+ * @param string $pattern
+ * @param string $uri
+ * @param array $options Route options
+ * @param string $check Expected value for `check`
+ * @param string $checkB Expected value for `checkB`
+ */
+ public function testBindVarSingleUrlPart($pattern, $uri, $options, $check, $checkB = null)
+ {
+ $values = [$pattern => $options];
+
+ $glob = new Glob($values);
+ $request = $this->getServerRequest($uri);
+ $route = $glob->getRoute($request);
+
+ $this->assertNotNull($route, "Route not found");
+ $this->assertInstanceOf(Route::class, $route);
+
+ $this->assertEquals($check, $route->check);
+
+ if (isset($checkB)) {
+ $this->assertEquals($checkB, $route->checkB);
+ } else {
+ $this->assertObjectNotHasAttribute('checkB', $route);
+ }
+ }
+
+ public function testBindVarWithObject()
+ {
+ $object = new \Exception(); // Could be anything, just not stdClass
+ $glob = new Glob(['/' => ['object' => $object]]);
+
+ $request = $this->getServerRequest('/');
+ $route = $glob->getRoute($request);
+
+ $this->assertNotNull($route, "Route not found");
+ $this->assertInstanceOf(Route::class, $route);
+
+ $this->assertSame($object, $route->object);
+ }
+
+ public function bindVarWithSubProvider()
+ {
+ return [
+ [['group' => ['check' => '$1']], 'array'],
+ [['group' => (object)['check' => '$1']], 'object'],
+ [['group' => ['sub' => (object)['check' => '$1']]], 'array', 'object'],
+ [['group' => (object)['sub' => ['check' => '$1']]], 'object', 'array']
+ ];
+ }
+
+ /**
+ * @dataProvider bindVarWithSubProvider
+ *
+ * @param array $options
+ * @param string $type
+ * @param string $subtype
+ */
+ public function testBindVarWithSub(array $options, $type, $subtype = null)
+ {
+ $glob = new Glob(['/*' => $options]);
+
+ $request = $this->getServerRequest('/test');
+ $route = $glob->getRoute($request);
+
+ $this->assertNotNull($route, "Route not found");
+ $this->assertInstanceOf(Route::class, $route);
+
+ $this->assertInternalType($type, $route->group);
+
+ $group = (array)$route->group;
+
+ if (isset($subtype)) {
+ $this->assertArrayHasKey('sub', $group);
+ $this->assertInternalType($subtype, $group['sub']);
+
+ $group = (array)$group['sub'];
+ }
+
+ $this->assertEquals($group, ['check' => 'test']);
+ }
+
+
+ /**
+ * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts()
+ */
+ public function bindVarMultipleUrlPartsProvider()
+ {
+ return [
+ ['/foo', ['check' => '$1...'], false, InvalidArgumentException::class],
+ ['/', ['check' => ['$1...']], false],
+ ['/foo', ['check' => ['$1...']], true],
+ ['/foo/bar', ['check' => ['$1...'], 'checkB' => ['$2...']],
+ InvalidArgumentException::class]
+ ];
+ }
+
+ /**
+ * Test binding multyple url parts to route option
+ * @dataProvider bindVarMultipleUrlPartsProvider
+ *
+ * @param string $uri
+ * @param array $options Route options
+ * @param boolean $positive
+ * @param string $exception
+ */
+ public function testBindVarMultipleUrlParts($uri, $options, $positive, $exception = false)
+ {
+ if ($exception) {
+ $this->expectException($exception);
+ }
+
+ $glob = new Glob([$uri => $options]);
+ $request = $this->getServerRequest($uri);
+ $route = $glob->getRoute($request);
+
+ if ($exception) return;
+
+ $this->assertNotNull($route, "Route not found");
+ $this->assertInstanceOf(Route::class, $route);
+
+ $values = explode('/', trim($uri, '/'));
+
+ $positive ?
+ $this->assertArraysEqual($values, $route->check, "Multyple url parts are not picked correctly") :
+ $this->assertEmpty($route->check, "Multyple parts element should be empty");
+
+ if (!empty($options->checkB)) {
+ array_shift($values);
+ $this->assertArraysEqual($values, $route->checkB, "Secondary multyple url parts are not picked correctly");
+ }
+ }
+
+ /**
+ * Provide uri's and corresponding patterns for testBindVarMultipleUrlParts()
+ */
+ public function bindVarSuperGlobalProvider()
+ {
+ return [
+ ['/foo', ['check' => '$_GET[check]'], 'get'],
+ ['/foo', ['check' => '$_POST[check]'], 'post'],
+ ['/foo', ['check' => '$_COOKIE[check]'], 'cookie']
+ ];
+ }
+
+ /**
+ * Test binding element of superglobal array to route option
+ * @dataProvider bindVarSuperGlobalProvider
+ *
+ * @param string $uri
+ * @param array $options
+ * @param string $type ('get', 'post', 'cookie')
+ */
+ public function testBindVarSuperGlobal($uri, $options, $type)
+ {
+ $test = ['check' => 'test'];
+ $glob = new Glob([$uri => $options]);
+ $request = $this->getServerRequest($uri, 'GET', [$type => $test]);
+ $route = $glob->getRoute($request);
+
+ $this->assertEquals($test['check'], $route->check, "Did not obtaine value for superglobal '$type'");
+ }
+
+ /**
+ * Test binding element of superglobal array to route option
+ */
+ public function testBindVarRequestHeader()
+ {
+ $uri = '/foo/bar';
+ $test = 'test_header_value';
+
+ $glob = new Glob([$uri => ['check' => '$HTTP_REFERER']]);
+ $request = $this->getServerRequest($uri, 'GET', [], $test);
+
+ $route = $glob->getRoute($request);
+ $this->assertNotNull($route, "Route not found");
+
+ $this->assertEquals($test, $route->check);
+ }
+
+ /**
+ * Get ServerRequestInterface object
+ *
+ * @param string $uri
+ * @param string $method Http query method
+ * @return ServerRequestInterface
+ */
+ public function getServerRequest($uri, $method = 'GET', $globals = [], $header = '')
+ {
+ $request = $this->createMock(ServerRequestInterface::class);
+ $request->method('getUri')->willReturn($uri);
+ $request->method('getMethod')->willReturn($method);
+ $request->method('getQueryParams')->willReturn(isset($globals['get']) ? $globals['get'] : []);
+ $request->method('getParsedBody')->willReturn(isset($globals['post']) ? $globals['post'] : []);
+ $request->method('getCookieParams')->willReturn(isset($globals['cookie']) ? $globals['cookie'] : []);
+ $request->method('getHeaderLine')->willReturn($header);
+
+ return $request;
+ }
+
+ /**
+ * Assert that two 1-dimensional arrays are equal.
+ * Use if array elements are scalar values, or objects with __toString() method
+ *
+ * @param array $array1
+ * @param array $array2
+ */
+ public function assertArraysEqual(array $array1, array $array2)
+ {
+ $this->assertEmpty(array_diff($array2, $array1), 'Missing items');
+ $this->assertEmpty(array_diff($array1, $array2), 'Additional items');
+ }
+}