diff options
Diffstat (limited to 'src/Http')
-rw-r--r-- | src/Http/Exceptions/ValidationException.php | 37 | ||||
-rw-r--r-- | src/Http/Validation/Rules/In.php | 21 | ||||
-rw-r--r-- | src/Http/Validation/Rules/NotIn.php | 15 | ||||
-rw-r--r-- | src/Http/Validation/ValidatesRequest.php | 37 | ||||
-rw-r--r-- | src/Http/Validation/ValidationServiceProvider.php | 25 | ||||
-rw-r--r-- | src/Http/Validation/Validator.php | 122 |
6 files changed, 257 insertions, 0 deletions
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/Rules/In.php b/src/Http/Validation/Rules/In.php new file mode 100644 index 00000000..d585cc3d --- /dev/null +++ b/src/Http/Validation/Rules/In.php @@ -0,0 +1,21 @@ +<?php + +namespace Engelsystem\Http\Validation\Rules; + +use Respect\Validation\Rules\In as RespectIn; + +class In extends RespectIn +{ + /** + * @param mixed $haystack + * @param bool $compareIdentical + */ + public function __construct($haystack, $compareIdentical = false) + { + if (!is_array($haystack)) { + $haystack = explode(',', $haystack); + } + + parent::__construct($haystack, $compareIdentical); + } +} diff --git a/src/Http/Validation/Rules/NotIn.php b/src/Http/Validation/Rules/NotIn.php new file mode 100644 index 00000000..7f223c42 --- /dev/null +++ b/src/Http/Validation/Rules/NotIn.php @@ -0,0 +1,15 @@ +<?php + +namespace Engelsystem\Http\Validation\Rules; + +class NotIn extends In +{ + /** + * @param mixed $input + * @return bool + */ + public function validate($input) + { + return !parent::validate($input); + } +} 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..14530ae6 --- /dev/null +++ b/src/Http/Validation/ValidationServiceProvider.php @@ -0,0 +1,25 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use Engelsystem\Application; +use Engelsystem\Container\ServiceProvider; +use Engelsystem\Controllers\BaseController; + +class ValidationServiceProvider extends ServiceProvider +{ + public function register() + { + $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..976f5682 --- /dev/null +++ b/src/Http/Validation/Validator.php @@ -0,0 +1,122 @@ +<?php + +namespace Engelsystem\Http\Validation; + +use Illuminate\Support\Str; +use InvalidArgumentException; +use Respect\Validation\Exceptions\ComponentException; +use Respect\Validation\Validator as RespectValidator; + +class Validator +{ + /** @var string[] */ + protected $errors = []; + + /** @var array */ + protected $data = []; + + /** @var array */ + protected $mapping = [ + 'accepted' => 'TrueVal', + 'int' => 'IntVal', + 'required' => 'NotEmpty', + ]; + + /** @var array */ + protected $nestedRules = ['optional', 'not']; + + /** + * @param array $data + * @param array $rules + * @return bool + */ + public function validate($data, $rules) + { + $this->errors = []; + $this->data = []; + + foreach ($rules as $key => $values) { + $v = new RespectValidator(); + $v->with('\\Engelsystem\\Http\\Validation\\Rules', true); + + $value = isset($data[$key]) ? $data[$key] : null; + $values = explode('|', $values); + + $packing = []; + foreach ($this->nestedRules as $rule) { + if (in_array($rule, $values)) { + $packing[] = $rule; + } + } + + $values = array_diff($values, $this->nestedRules); + foreach ($values as $parameters) { + $parameters = explode(':', $parameters); + $rule = array_shift($parameters); + $rule = Str::camel($rule); + $rule = $this->map($rule); + + // To allow rules nesting + $w = $v; + try { + foreach (array_reverse(array_merge($packing, [$rule])) as $rule) { + if (!in_array($rule, $this->nestedRules)) { + call_user_func_array([$w, $rule], $parameters); + continue; + } + + $w = call_user_func_array([new RespectValidator(), $rule], [$w]); + } + } catch (ComponentException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + + if ($w->validate($value)) { + $this->data[$key] = $value; + } else { + $this->errors[$key][] = implode('.', ['validation', $key, $this->mapBack($rule)]); + } + + $v->removeRules(); + } + } + + return empty($this->errors); + } + + /** + * @param string $rule + * @return string + */ + protected function map($rule) + { + return $this->mapping[$rule] ?? $rule; + } + + /** + * @param string $rule + * @return string + */ + protected function mapBack($rule) + { + $mapping = array_flip($this->mapping); + + return $mapping[$rule] ?? $rule; + } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * @return string[] + */ + public function getErrors(): array + { + return $this->errors; + } +} |