summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorminstel <minstel@yandex.ru>2016-10-18 23:53:02 +0300
committerminstel <minstel@yandex.ru>2016-10-18 23:53:02 +0300
commitfb563442e535c71b935582ce8d90470ffc958ad5 (patch)
tree2ab8aca36355b12d79455e7ef0a5995b4d815613
parent298f38a4347b8b29e3d145f8b5a0a24ea5775640 (diff)
downloadcontroller-fb563442e535c71b935582ce8d90470ffc958ad5.zip
controller-fb563442e535c71b935582ce8d90470ffc958ad5.tar.gz
controller-fb563442e535c71b935582ce8d90470ffc958ad5.tar.bz2
RouteAction trait
-rw-r--r--composer.json3
-rw-r--r--src/Controller/RouteAction.php85
-rw-r--r--tests/Controller/RouteActionTest.php63
-rw-r--r--tests/support/TestController.php32
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;
+ }
+}