summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Scheller <igor.scheller@igorshp.de>2019-11-27 19:11:37 +0100
committerIgor Scheller <igor.scheller@igorshp.de>2019-12-08 02:12:56 +0100
commit89742ecd5542c740f3625af76c843a7471dbe98a (patch)
tree71c0c78ceb6a3a24b63c51c94a655cf5b33dc044
parentbe39c63f46562eea173747d80cd91ac81e0b8e09 (diff)
Response: Added with and withInput methods and back/redirect functions
-rw-r--r--config/app.php1
-rw-r--r--src/Http/RedirectServiceProvider.php13
-rw-r--r--src/Http/Redirector.php55
-rw-r--r--src/Http/Response.php60
-rw-r--r--src/Middleware/ErrorHandler.php25
-rw-r--r--src/helpers.php40
-rw-r--r--tests/Unit/HelpersTest.php47
-rw-r--r--tests/Unit/Http/RedirectServiceProviderTest.php23
-rw-r--r--tests/Unit/Http/RedirectorTest.php53
-rw-r--r--tests/Unit/Http/ResponseTest.php57
-rw-r--r--tests/Unit/Middleware/ErrorHandlerTest.php9
11 files changed, 353 insertions, 30 deletions
diff --git a/config/app.php b/config/app.php
index bfb66cf3..970f80a5 100644
--- a/config/app.php
+++ b/config/app.php
@@ -26,6 +26,7 @@ return [
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
\Engelsystem\Middleware\SessionHandlerServiceProvider::class,
\Engelsystem\Http\Validation\ValidationServiceProvider::class,
+ \Engelsystem\Http\RedirectServiceProvider::class,
// Additional services
\Engelsystem\Helpers\VersionServiceProvider::class,
diff --git a/src/Http/RedirectServiceProvider.php b/src/Http/RedirectServiceProvider.php
new file mode 100644
index 00000000..70238d91
--- /dev/null
+++ b/src/Http/RedirectServiceProvider.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Engelsystem\Http;
+
+use Engelsystem\Container\ServiceProvider;
+
+class RedirectServiceProvider extends ServiceProvider
+{
+ public function register()
+ {
+ $this->app->bind('redirect', Redirector::class);
+ }
+}
diff --git a/src/Http/Redirector.php b/src/Http/Redirector.php
new file mode 100644
index 00000000..4149a8ec
--- /dev/null
+++ b/src/Http/Redirector.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Engelsystem\Http;
+
+class Redirector
+{
+ /** @var Request */
+ protected $request;
+
+ /** @var Response */
+ protected $response;
+
+ /**
+ * @param Request $request
+ * @param Response $response
+ */
+ public function __construct(Request $request, Response $response)
+ {
+ $this->request = $request;
+ $this->response = $response;
+ }
+
+ /**
+ * @param string $path
+ * @param int $status
+ * @param array $headers
+ * @return Response
+ */
+ public function to(string $path, int $status = 302, array $headers = []): Response
+ {
+ return $this->response->redirectTo($path, $status, $headers);
+ }
+
+ /**
+ * @param int $status
+ * @param array $headers
+ * @return Response
+ */
+ public function back(int $status = 302, array $headers = []): Response
+ {
+ return $this->to($this->getPreviousUrl(), $status, $headers);
+ }
+
+ /**
+ * @return string
+ */
+ protected function getPreviousUrl(): string
+ {
+ if ($header = $this->request->getHeader('referer')) {
+ return array_pop($header);
+ }
+
+ return '/';
+ }
+}
diff --git a/src/Http/Response.php b/src/Http/Response.php
index a6b4ab74..43bcb5d8 100644
--- a/src/Http/Response.php
+++ b/src/Http/Response.php
@@ -6,27 +6,37 @@ use Engelsystem\Renderer\Renderer;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Response extends SymfonyResponse implements ResponseInterface
{
use MessageTrait;
+ /**
+ * @var SessionInterface
+ */
+ protected $session;
+
/** @var Renderer */
protected $renderer;
/**
- * @param string $content
- * @param int $status
- * @param array $headers
- * @param Renderer $renderer
+ * @param string $content
+ * @param int $status
+ * @param array $headers
+ * @param Renderer $renderer
+ * @param SessionInterface $session
*/
public function __construct(
$content = '',
int $status = 200,
array $headers = [],
- Renderer $renderer = null
+ Renderer $renderer = null,
+ SessionInterface $session = null
) {
$this->renderer = $renderer;
+ $this->session = $session;
+
parent::__construct($content, $status, $headers);
}
@@ -155,4 +165,44 @@ class Response extends SymfonyResponse implements ResponseInterface
{
$this->renderer = $renderer;
}
+
+ /**
+ * Sets a session attribute (which is mutable)
+ *
+ * @param string $key
+ * @param mixed|mixed[] $value
+ * @return Response
+ */
+ public function with(string $key, $value)
+ {
+ if (!$this->session instanceof SessionInterface) {
+ throw new InvalidArgumentException('Session not defined');
+ }
+
+ $data = $this->session->get($key);
+ if (is_array($data) && is_array($value)) {
+ $value = array_merge_recursive($data, $value);
+ }
+
+ $this->session->set($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * Sets form data to the mutable session
+ *
+ * @param array $input
+ * @return Response
+ */
+ public function withInput(array $input)
+ {
+ if (!$this->session instanceof SessionInterface) {
+ throw new InvalidArgumentException('Session not defined');
+ }
+
+ $this->session->set('form-data', $input);
+
+ return $this;
+ }
}
diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php
index 46e6e5a8..85f315d4 100644
--- a/src/Middleware/ErrorHandler.php
+++ b/src/Middleware/ErrorHandler.php
@@ -63,17 +63,11 @@ class ErrorHandler implements MiddlewareInterface
} catch (HttpException $e) {
$response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders());
} catch (ValidationException $e) {
- $response = $this->createResponse('', 302, ['Location' => $this->getPreviousUrl($request)]);
+ $response = $this->redirectBack();
+ $response->with('errors', ['validation' => $e->getValidator()->getErrors()]);
if ($request instanceof Request) {
- $session = $request->getSession();
- $errors = array_merge_recursive(
- $session->get('errors', []),
- ['validation' => $e->getValidator()->getErrors()]
- );
- $session->set('errors', $errors);
-
- $session->set('form-data', Arr::except($request->request->all(), $this->formIgnore));
+ $response->withInput(Arr::except($request->request->all(), $this->formIgnore));
}
}
@@ -140,15 +134,12 @@ class ErrorHandler implements MiddlewareInterface
}
/**
- * @param ServerRequestInterface $request
- * @return string
+ * Create a redirect back response
+ *
+ * @return Response
*/
- protected function getPreviousUrl(ServerRequestInterface $request)
+ protected function redirectBack()
{
- if ($header = $request->getHeader('referer')) {
- return array_pop($header);
- }
-
- return '/';
+ return back();
}
}
diff --git a/src/helpers.php b/src/helpers.php
index de140c4e..7cb17ea9 100644
--- a/src/helpers.php
+++ b/src/helpers.php
@@ -4,6 +4,7 @@ use Engelsystem\Application;
use Engelsystem\Config\Config;
use Engelsystem\Helpers\Authenticator;
use Engelsystem\Helpers\Translation\Translator;
+use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Http\UrlGeneratorInterface;
@@ -28,7 +29,7 @@ function app($id = null)
/**
* @return Authenticator
*/
-function auth()
+function auth(): Authenticator
{
return app('authenticator');
}
@@ -37,12 +38,25 @@ function auth()
* @param string $path
* @return string
*/
-function base_path($path = '')
+function base_path($path = ''): string
{
return app('path') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path);
}
/**
+ * @param int $status
+ * @param array $headers
+ * @return Response
+ */
+function back($status = 302, $headers = []): Response
+{
+ /** @var Redirector $redirect */
+ $redirect = app('redirect');
+
+ return $redirect->back($status, $headers);
+}
+
+/**
* Get or set config values
*
* @param string|array $key
@@ -70,12 +84,26 @@ function config($key = null, $default = null)
* @param string $path
* @return string
*/
-function config_path($path = '')
+function config_path($path = ''): string
{
return app('path.config') . (empty($path) ? '' : DIRECTORY_SEPARATOR . $path);
}
/**
+ * @param string $path
+ * @param int $status
+ * @param array $headers
+ * @return Response
+ */
+function redirect(string $path, $status = 302, $headers = []): Response
+{
+ /** @var Redirector $redirect */
+ $redirect = app('redirect');
+
+ return $redirect->to($path, $status, $headers);
+}
+
+/**
* @param string $key
* @param mixed $default
* @return Request|mixed
@@ -97,7 +125,7 @@ function request($key = null, $default = null)
* @param array $headers
* @return Response
*/
-function response($content = '', $status = 200, $headers = [])
+function response($content = '', $status = 200, $headers = []): Response
{
/** @var Response $response */
$response = app('psr7.response');
@@ -155,7 +183,7 @@ function trans($key = null, $replace = [])
* @param array $replace
* @return string
*/
-function __($key, $replace = [])
+function __($key, $replace = []): string
{
/** @var Translator $translator */
$translator = app('translator');
@@ -172,7 +200,7 @@ function __($key, $replace = [])
* @param array $replace
* @return string
*/
-function _e($key, $keyPlural, $number, $replace = [])
+function _e($key, $keyPlural, $number, $replace = []): string
{
/** @var Translator $translator */
$translator = app('translator');
diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php
index 09362a90..710deff5 100644
--- a/tests/Unit/HelpersTest.php
+++ b/tests/Unit/HelpersTest.php
@@ -7,6 +7,7 @@ use Engelsystem\Config\Config;
use Engelsystem\Container\Container;
use Engelsystem\Helpers\Authenticator;
use Engelsystem\Helpers\Translation\Translator;
+use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Http\UrlGeneratorInterface;
@@ -99,6 +100,29 @@ class HelpersTest extends TestCase
}
/**
+ * @covers \back
+ */
+ public function testBack()
+ {
+ $response = new Response();
+ /** @var Redirector|MockObject $redirect */
+ $redirect = $this->createMock(Redirector::class);
+ $redirect->expects($this->exactly(2))
+ ->method('back')
+ ->withConsecutive([302, []], [303, ['test' => 'ing']])
+ ->willReturn($response);
+
+ $app = new Application();
+ $app->instance('redirect', $redirect);
+
+ $return = back();
+ $this->assertEquals($response, $return);
+
+ $return = back(303, ['test' => 'ing']);
+ $this->assertEquals($response, $return);
+ }
+
+ /**
* @covers \config_path
*/
public function testConfigPath()
@@ -118,6 +142,29 @@ class HelpersTest extends TestCase
}
/**
+ * @covers \redirect
+ */
+ public function testRedirect()
+ {
+ $response = new Response();
+ /** @var Redirector|MockObject $redirect */
+ $redirect = $this->createMock(Redirector::class);
+ $redirect->expects($this->exactly(2))
+ ->method('to')
+ ->withConsecutive(['/lorem', 302, []], ['/ipsum', 303, ['test' => 'er']])
+ ->willReturn($response);
+
+ $app = new Application();
+ $app->instance('redirect', $redirect);
+
+ $return = redirect('/lorem');
+ $this->assertEquals($response, $return);
+
+ $return = redirect('/ipsum', 303, ['test' => 'er']);
+ $this->assertEquals($response, $return);
+ }
+
+ /**
* @covers \request
*/
public function testRequest()
diff --git a/tests/Unit/Http/RedirectServiceProviderTest.php b/tests/Unit/Http/RedirectServiceProviderTest.php
new file mode 100644
index 00000000..9c2a07fc
--- /dev/null
+++ b/tests/Unit/Http/RedirectServiceProviderTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Http;
+
+use Engelsystem\Application;
+use Engelsystem\Http\RedirectServiceProvider;
+use Engelsystem\Test\Unit\ServiceProviderTest;
+
+class RedirectServiceProviderTest extends ServiceProviderTest
+{
+ /**
+ * @covers \Engelsystem\Http\RedirectServiceProvider::register
+ */
+ public function testRegister()
+ {
+ $app = new Application();
+
+ $serviceProvider = new RedirectServiceProvider($app);
+ $serviceProvider->register();
+
+ $this->assertTrue($app->has('redirect'));
+ }
+}
diff --git a/tests/Unit/Http/RedirectorTest.php b/tests/Unit/Http/RedirectorTest.php
new file mode 100644
index 00000000..300b0180
--- /dev/null
+++ b/tests/Unit/Http/RedirectorTest.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Engelsystem\Test\Unit\Http;
+
+use Engelsystem\Http\Redirector;
+use Engelsystem\Http\Request;
+use Engelsystem\Http\Response;
+use PHPUnit\Framework\TestCase;
+
+class RedirectorTest extends TestCase
+{
+ /**
+ * @covers \Engelsystem\Http\Redirector::__construct
+ * @covers \Engelsystem\Http\Redirector::to
+ */
+ public function testTo()
+ {
+ $request = new Request();
+ $response = new Response();
+ $redirector = new Redirector($request, $response);
+
+ $return = $redirector->to('/test');
+ $this->assertEquals(['/test'], $return->getHeader('location'));
+ $this->assertEquals(302, $return->getStatusCode());
+
+ $return = $redirector->to('/foo', 303, ['test' => 'data']);
+ $this->assertEquals(['/foo'], $return->getHeader('location'));
+ $this->assertEquals(303, $return->getStatusCode());
+ $this->assertEquals(['data'], $return->getHeader('test'));
+ }
+
+ /**
+ * @covers \Engelsystem\Http\Redirector::back
+ * @covers \Engelsystem\Http\Redirector::getPreviousUrl
+ */
+ public function testBack()
+ {
+ $request = new Request();
+ $response = new Response();
+ $redirector = new Redirector($request, $response);
+
+ $return = $redirector->back();
+ $this->assertEquals(['/'], $return->getHeader('location'));
+ $this->assertEquals(302, $return->getStatusCode());
+
+ $request = $request->withHeader('referer', '/old-page');
+ $redirector = new Redirector($request, $response);
+ $return = $redirector->back(303, ['foo' => 'bar']);
+ $this->assertEquals(303, $return->getStatusCode());
+ $this->assertEquals(['/old-page'], $return->getHeader('location'));
+ $this->assertEquals(['bar'], $return->getHeader('foo'));
+ }
+}
diff --git a/tests/Unit/Http/ResponseTest.php b/tests/Unit/Http/ResponseTest.php
index b8e6e527..56d8410f 100644
--- a/tests/Unit/Http/ResponseTest.php
+++ b/tests/Unit/Http/ResponseTest.php
@@ -10,6 +10,8 @@ use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class ResponseTest extends TestCase
{
@@ -116,4 +118,59 @@ class ResponseTest extends TestCase
$newResponse->getHeaders()
);
}
+
+ /**
+ * @covers \Engelsystem\Http\Response::with
+ */
+ public function testWith()
+ {
+ $session = new Session(new MockArraySessionStorage());
+ $response = new Response('', 200, [], null, $session);
+
+ $response->with('foo', 'bar');
+ $this->assertEquals('bar', $session->get('foo'));
+
+ $response->with('lorem', ['ipsum', 'dolor' => ['foo' => 'bar']]);
+ $this->assertEquals(['ipsum', 'dolor' => ['foo' => 'bar']], $session->get('lorem'));
+
+ $response->with('lorem', ['dolor' => ['test' => 'er']]);
+ $this->assertEquals(['ipsum', 'dolor' => ['foo' => 'bar', 'test' => 'er']], $session->get('lorem'));
+ }
+
+ /**
+ * @covers \Engelsystem\Http\Response::with
+ */
+ public function testWithNoSession()
+ {
+ $this->expectException(InvalidArgumentException::class);
+
+ $response = new Response();
+ $response->with('foo', 'bar');
+ }
+
+ /**
+ * @covers \Engelsystem\Http\Response::withInput
+ */
+ public function testWithInput()
+ {
+ $session = new Session(new MockArraySessionStorage());
+ $response = new Response('', 200, [], null, $session);
+
+ $response->withInput(['some' => 'value']);
+ $this->assertEquals(['some' => 'value'], $session->get('form-data'));
+
+ $response->withInput(['lorem' => 'ipsum']);
+ $this->assertEquals(['lorem' => 'ipsum'], $session->get('form-data'));
+ }
+
+ /**
+ * @covers \Engelsystem\Http\Response::withInput
+ */
+ public function testWithInputNoSession()
+ {
+ $this->expectException(InvalidArgumentException::class);
+
+ $response = new Response();
+ $response->withInput(['some' => 'value']);
+ }
}
diff --git a/tests/Unit/Middleware/ErrorHandlerTest.php b/tests/Unit/Middleware/ErrorHandlerTest.php
index ef2f7be0..91b59507 100644
--- a/tests/Unit/Middleware/ErrorHandlerTest.php
+++ b/tests/Unit/Middleware/ErrorHandlerTest.php
@@ -6,6 +6,7 @@ use Engelsystem\Application;
use Engelsystem\Http\Exceptions\HttpException;
use Engelsystem\Http\Exceptions\ValidationException;
use Engelsystem\Http\Psr7ServiceProvider;
+use Engelsystem\Http\RedirectServiceProvider;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Http\ResponseServiceProvider;
@@ -18,6 +19,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Twig\Loader\LoaderInterface as TwigLoader;
@@ -155,7 +157,7 @@ class ErrorHandlerTest extends TestCase
/**
* @covers \Engelsystem\Middleware\ErrorHandler::process
- * @covers \Engelsystem\Middleware\ErrorHandler::getPreviousUrl
+ * @covers \Engelsystem\Middleware\ErrorHandler::redirectBack
*/
public function testProcessValidationException()
{
@@ -185,11 +187,13 @@ class ErrorHandlerTest extends TestCase
/** @var Application $app */
$app = app();
+ $app->instance(Session::class, $session);
+ $app->bind(SessionInterface::class, Session::class);
(new ResponseServiceProvider($app))->register();
(new Psr7ServiceProvider($app))->register();
+ (new RedirectServiceProvider($app))->register();
$errorHandler = new ErrorHandler($twigLoader);
-
$return = $errorHandler->process($request, $handler);
$this->assertEquals(302, $return->getStatusCode());
@@ -209,6 +213,7 @@ class ErrorHandlerTest extends TestCase
], $session->all());
$request = $request->withAddedHeader('referer', '/foo/batz');
+ $app->instance(Request::class, $request);
$return = $errorHandler->process($request, $handler);
$this->assertEquals('/foo/batz', $return->getHeaderLine('location'));