diff options
author | Igor Scheller <igor.scheller@igorshp.de> | 2019-07-09 21:43:18 +0200 |
---|---|---|
committer | Igor Scheller <igor.scheller@igorshp.de> | 2019-07-09 21:43:18 +0200 |
commit | 7414f9b23dbcc66e5f0efda3d0cbfd79372ec780 (patch) | |
tree | 891a11b71d8b25922fc4b343dbc77a01a0646ba1 /src | |
parent | 508695efb253d7bc0caea1fa017ed5608d774596 (diff) |
Implemented Validation for controllers
Diffstat (limited to 'src')
-rw-r--r-- | src/Controllers/BaseController.php | 4 | ||||
-rw-r--r-- | src/Http/Exceptions/ValidationException.php | 37 | ||||
-rw-r--r-- | src/Http/Validation/Validates.php | 154 | ||||
-rw-r--r-- | src/Http/Validation/ValidatesRequest.php | 37 | ||||
-rw-r--r-- | src/Http/Validation/ValidationServiceProvider.php | 28 | ||||
-rw-r--r-- | src/Http/Validation/Validator.php | 76 | ||||
-rw-r--r-- | src/Middleware/ErrorHandler.php | 30 |
7 files changed, 366 insertions, 0 deletions
diff --git a/src/Controllers/BaseController.php b/src/Controllers/BaseController.php index cbc00931..655ed759 100644 --- a/src/Controllers/BaseController.php +++ b/src/Controllers/BaseController.php @@ -2,8 +2,12 @@ namespace Engelsystem\Controllers; +use Engelsystem\Http\Validation\ValidatesRequest; + abstract class BaseController { + use ValidatesRequest; + /** @var string[]|string[][] A list of Permissions required to access the controller or certain pages */ protected $permissions = []; diff --git a/src/Http/Exceptions/ValidationException.php b/src/Http/Exceptions/ValidationException.php new file mode 100644 index 00000000..e48fb0c3 --- /dev/null +++ b/src/Http/Exceptions/ValidationException.php @@ -0,0 +1,37 @@ +<?php + +namespace Engelsystem\Http\Exceptions; + +use Engelsystem\Http\Validation\Validator; +use RuntimeException; +use Throwable; + +class ValidationException extends RuntimeException +{ + /** @var Validator */ + protected $validator; + + /** + * @param Validator $validator + * @param string $message + * @param int $code + * @param Throwable|null $previous + */ + public function __construct( + Validator $validator, + string $message = '', + int $code = 0, + Throwable $previous = null + ) { + $this->validator = $validator; + parent::__construct($message, $code, $previous); + } + + /** + * @return Validator + */ + public function getValidator(): Validator + { + return $this->validator; + } +} diff --git a/src/Http/Validation/Validates.php b/src/Http/Validation/Validates.php new file mode 100644 index 00000000..2e3a1a73 --- /dev/null +++ b/src/Http/Validation/Validates.php @@ -0,0 +1,154 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use InvalidArgumentException; + +class Validates +{ + /** + * @param mixed $value + * @return bool + */ + public function accepted($value): bool + { + return in_array($value, ['true', '1', 'y', 'yes', 'on', 1, true], true); + } + + /** + * @param string $value + * @param array $parameters ['min', 'max'] + * @return bool + */ + public function between($value, $parameters): bool + { + $this->validateParameterCount(2, $parameters, __FUNCTION__); + $size = $this->getSize($value); + + return $size >= $parameters[0] && $size <= $parameters[1]; + } + + /** + * @param mixed $value + * @return bool + */ + public function bool($value): bool + { + return in_array($value, ['1', 1, true, '0', 0, false], true); + } + + /** + * @param mixed $value + * @param array $parameters ['1,2,3,56,7'] + * @return bool + */ + public function in($value, $parameters): bool + { + $this->validateParameterCount(1, $parameters, __FUNCTION__); + + return in_array($value, explode(',', $parameters[0])); + } + + /** + * @param mixed $value + * @return bool + */ + public function int($value): bool + { + return filter_var($value, FILTER_VALIDATE_INT) !== false; + } + + /** + * @param string $value + * @param array $parameters ['max'] + * @return bool + */ + public function max($value, $parameters): bool + { + $this->validateParameterCount(1, $parameters, __FUNCTION__); + $size = $this->getSize($value); + + return $size <= $parameters[0]; + } + + /** + * @param string $value + * @param array $parameters ['min'] + * @return bool + */ + public function min($value, $parameters) + { + $this->validateParameterCount(1, $parameters, __FUNCTION__); + $size = $this->getSize($value); + + return $size >= $parameters[0]; + } + + /** + * @param mixed $value + * @param array $parameters ['1,2,3,56,7'] + * @return bool + */ + public function notIn($value, $parameters): bool + { + $this->validateParameterCount(1, $parameters, __FUNCTION__); + + return !$this->in($value, $parameters); + } + + /** + * @param mixed $value + * @return bool + */ + public function numeric($value): bool + { + return is_numeric($value); + } + + /** + * @param mixed $value + * @return bool + */ + public function required($value): bool + { + if ( + is_null($value) + || (is_string($value) && trim($value) === '') + ) { + return false; + } + + return true; + } + + /** + * @param mixed $value + * @return int|float + */ + protected function getSize($value) + { + if (is_numeric($value)) { + return $value; + } + + return mb_strlen($value); + } + + /** + * @param int $count + * @param array $parameters + * @param string $rule + * + * @throws InvalidArgumentException + */ + protected function validateParameterCount(int $count, array $parameters, string $rule) + { + if (count($parameters) < $count) { + throw new InvalidArgumentException(sprintf( + 'The rule "%s" requires at least %d parameters', + $rule, + $count + )); + } + } +} diff --git a/src/Http/Validation/ValidatesRequest.php b/src/Http/Validation/ValidatesRequest.php new file mode 100644 index 00000000..33ff76af --- /dev/null +++ b/src/Http/Validation/ValidatesRequest.php @@ -0,0 +1,37 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use Engelsystem\Http\Exceptions\ValidationException; +use Engelsystem\Http\Request; + +trait ValidatesRequest +{ + /** @var Validator */ + protected $validator; + + /** + * @param Request $request + * @param array $rules + * @return array + */ + protected function validate(Request $request, array $rules) + { + if (!$this->validator->validate( + (array)$request->getParsedBody(), + $rules + )) { + throw new ValidationException($this->validator); + } + + return $this->validator->getData(); + } + + /** + * @param Validator $validator + */ + public function setValidator(Validator $validator) + { + $this->validator = $validator; + } +} diff --git a/src/Http/Validation/ValidationServiceProvider.php b/src/Http/Validation/ValidationServiceProvider.php new file mode 100644 index 00000000..2f1c6359 --- /dev/null +++ b/src/Http/Validation/ValidationServiceProvider.php @@ -0,0 +1,28 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use Engelsystem\Application; +use Engelsystem\Container\ServiceProvider; +use Engelsystem\Controllers\BaseController; + +class ValidationServiceProvider extends ServiceProvider +{ + public function register() + { + $validates = $this->app->make(Validates::class); + $this->app->instance(Validates::class, $validates); + + $validator = $this->app->make(Validator::class); + $this->app->instance(Validator::class, $validator); + $this->app->instance('validator', $validator); + + $this->app->afterResolving(function ($object, Application $app) { + if (!$object instanceof BaseController) { + return; + } + + $object->setValidator($app->get(Validator::class)); + }); + } +} diff --git a/src/Http/Validation/Validator.php b/src/Http/Validation/Validator.php new file mode 100644 index 00000000..a9235a5f --- /dev/null +++ b/src/Http/Validation/Validator.php @@ -0,0 +1,76 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use Illuminate\Support\Str; +use InvalidArgumentException; + +class Validator +{ + /** @var Validates */ + protected $validate; + + /** @var string[] */ + protected $errors = []; + + /** @var array */ + protected $data = []; + + /** + * @param Validates $validate + */ + public function __construct(Validates $validate) + { + $this->validate = $validate; + } + + /** + * @param array $data + * @param array $rules + * @return bool + */ + public function validate($data, $rules) + { + $this->errors = []; + $this->data = []; + + foreach ($rules as $key => $values) { + foreach (explode('|', $values) as $parameters) { + $parameters = explode(':', $parameters); + $rule = array_shift($parameters); + $rule = Str::camel($rule); + + if (!method_exists($this->validate, $rule)) { + throw new InvalidArgumentException('Unknown validation rule: ' . $rule); + } + + $value = isset($data[$key]) ? $data[$key] : null; + if (!$this->validate->{$rule}($value, $parameters, $data)) { + $this->errors[$key][] = implode('.', ['validation', $key, $rule]); + + continue; + } + + $this->data[$key] = $value; + } + } + + return empty($this->errors); + } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * @return string[] + */ + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php index 29b1fac1..c89edb1a 100644 --- a/src/Middleware/ErrorHandler.php +++ b/src/Middleware/ErrorHandler.php @@ -3,6 +3,8 @@ namespace Engelsystem\Middleware; use Engelsystem\Http\Exceptions\HttpException; +use Engelsystem\Http\Exceptions\ValidationException; +use Engelsystem\Http\Request; use Engelsystem\Http\Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -43,6 +45,21 @@ class ErrorHandler implements MiddlewareInterface $response = $handler->handle($request); } catch (HttpException $e) { $response = $this->createResponse($e->getMessage(), $e->getStatusCode(), $e->getHeaders()); + } catch (ValidationException $e) { + $response = $this->createResponse('', 302, ['Location' => $this->getPreviousUrl($request)]); + + if ($request instanceof Request) { + $session = $request->getSession(); + $session->set( + 'errors', + array_merge_recursive( + $session->get('errors', []), + ['validation' => $e->getValidator()->getErrors()] + ) + ); + + $session->set('form-data', $request->request->all()); + } } $statusCode = $response->getStatusCode(); @@ -106,4 +123,17 @@ class ErrorHandler implements MiddlewareInterface { return response($content, $status, $headers); } + + /** + * @param ServerRequestInterface $request + * @return string + */ + protected function getPreviousUrl(ServerRequestInterface $request) + { + if ($header = $request->getHeader('referer')) { + return array_pop($header); + } + + return '/'; + } } |