diff options
author | minstel <minstel@yandex.ru> | 2016-10-18 23:53:02 +0300 |
---|---|---|
committer | minstel <minstel@yandex.ru> | 2016-10-18 23:53:02 +0300 |
commit | fb563442e535c71b935582ce8d90470ffc958ad5 (patch) | |
tree | 2ab8aca36355b12d79455e7ef0a5995b4d815613 | |
parent | 298f38a4347b8b29e3d145f8b5a0a24ea5775640 (diff) | |
download | controller-fb563442e535c71b935582ce8d90470ffc958ad5.zip controller-fb563442e535c71b935582ce8d90470ffc958ad5.tar.gz controller-fb563442e535c71b935582ce8d90470ffc958ad5.tar.bz2 |
RouteAction trait
-rw-r--r-- | composer.json | 3 | ||||
-rw-r--r-- | src/Controller/RouteAction.php | 85 | ||||
-rw-r--r-- | tests/Controller/RouteActionTest.php | 63 | ||||
-rw-r--r-- | tests/support/TestController.php | 32 |
4 files changed, 182 insertions, 1 deletions
diff --git a/composer.json b/composer.json index 9aaffb9..fab78e9 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ }, "require": { "php": ">=5.6.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "jasny/php-functions": "^2.0" }, "require-dev": { "jasny/php-code-quality": "^2.0" diff --git a/src/Controller/RouteAction.php b/src/Controller/RouteAction.php new file mode 100644 index 0000000..aa6a710 --- /dev/null +++ b/src/Controller/RouteAction.php @@ -0,0 +1,85 @@ +<?php + +namespace Jasny\Controller; + +use Psr\Http\Message\ResponseInterface; + +/** + * Execute controller on given route + */ +trait RouteAction +{ + /** + * Run the controller + * + * @return ResponseInterface + */ + public function run() { + $request = $this->getRequest(); + if (!$request) { + throw new \RuntimeException("Request object is not set for controller"); + } + + $route = $request->getAttribute('route'); + $method = $this->getActionMethod(isset($route->action) ? $route->action : null); + + if (!method_exists($this, $method)) { + throw new \RuntimeException("No method $method in conrtoller to route to"); + } + + $args = isset($route->args) ? + $route->args : + $this->getFunctionArgs($route, new \ReflectionMethod($this, $method)); + + $response = call_user_func_array([$this, $method], $args); + + return $response ?: $this->getResponse(); + } + + /** + * Get the method name of the action + * + * @param string $action + * @return string + */ + protected function getActionMethod($action) + { + return \Jasny\camelcase($action) . 'Action'; + } + + /** + * Get the arguments for a function from a route using reflection + * + * @param object $route + * @param \ReflectionFunctionAbstract $refl + * @return array + */ + protected function getFunctionArgs($route, \ReflectionFunctionAbstract $refl) + { + $args = []; + $params = $refl->getParameters(); + + foreach ($params as $param) { + $key = $param->name; + + if (property_exists($route, $key)) { + $value = $route->{$key}; + } else { + if (!$param->isOptional()) { + $fn = $refl instanceof \ReflectionMethod + ? $refl->getDeclaringClass()->getName() . ':' . $refl->getName() + : $refl->getName(); + + throw new \RuntimeException("Missing argument '$key' for $fn()"); + } + + $value = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null; + } + + $args[$key] = $value; + } + + return $args; + } +} + diff --git a/tests/Controller/RouteActionTest.php b/tests/Controller/RouteActionTest.php new file mode 100644 index 0000000..a0d9739 --- /dev/null +++ b/tests/Controller/RouteActionTest.php @@ -0,0 +1,63 @@ +<?php + +require_once dirname(__DIR__) . '/support/TestController.php'; + +use Jasny\Controller\RouteAction; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; + +class RouteActionTest extends PHPUnit_Framework_TestCase +{ + /** + * Test running controller action + * + * @dataProvider runProvider + * @param [type] $[name] [<description>] + */ + public function testRun($route, $positive) + { + $controller = new TestController(); + $request = $this->createMock(ServerRequestInterface::class); + $response = $this->createMock(ResponseInterface::class); + + $request->method('getAttribute')->with($this->equalTo('route'))->will($this->returnValue($route)); + + if (!$positive) $this->expectException(\RuntimeException::class); + + $result = $controller($request, $response); + $args = !empty($route->args) ? $route->args : [$route->param1, isset($route->param2) ? $route->param2 : 'defaultValue']; + + $this->assertEquals(get_class($response), get_class($result), "Controller should return 'ResponseInterface' instance"); + $this->assertTrue($result->actionCalled, "Controller action was not called"); + $this->assertEquals($args[0], $result->param1, "First route parameter was not passed correctly"); + $this->assertEquals($args[1], $result->param2, "Second route parameter was not passed correctly"); + } + + /** + * Provide data for testing run method + */ + public function runProvider() + { + return [ + [(object)['controller' => 'TestController'], false], + [(object)['controller' => 'TestController', 'action' => 'nonExistMethod'], false], + [(object)['controller' => 'TestController', 'action' => 'test'], false], + [(object)['controller' => 'TestController', 'action' => 'test', 'param2' => 'value2'], false], + [(object)['controller' => 'TestController', 'action' => 'test', 'param1' => 'value1'], true], + [(object)['controller' => 'TestController', 'action' => 'test', 'param1' => 'value1', 'param2' => 'value2'], true], + [(object)['controller' => 'TestController', 'action' => 'test', 'args' => ['value1', 'value2']], true] + ]; + } + + /** + * Test running controller without setting request and response + */ + public function testRunNoRequest() + { + $controller = new TestController(); + + $this->expectException(\RuntimeException::class); + + $controller->run(); + } +} diff --git a/tests/support/TestController.php b/tests/support/TestController.php new file mode 100644 index 0000000..4f8fb41 --- /dev/null +++ b/tests/support/TestController.php @@ -0,0 +1,32 @@ +<?php + +use Jasny\Controller; +use Jasny\Controller\RouteAction; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Class for testing 'RouteAction' trait + */ +class TestController extends Controller +{ + use RouteAction; + + /** + * Test action for executing router + * + * @param mixed $param1 + * @param mixed $param2 + * @return ResponseInterface + */ + public function testAction($param1, $param2 = 'defaultValue') + { + $response = $this->getResponse(); + + $response->actionCalled = true; + $response->param1 = $param1; + $response->param2 = $param2; + + return $response; + } +} |