diff --git a/composer.json b/composer.json index cc2ced1..b30dcf8 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "ext-asio": "*", "hhvm/hhvm-autoload": "^1.5", "hhvm/hsl": "^1.0.0", + "hhvm/type-assert": "^3.2.0", "hack-psr/psr7-http-message-hhi": "^1.0.0", "ytake/hh-container": "^0.5", "ytake/hh-config-aggregator": "^0.1.1", diff --git a/src/Foundation/Middleware/Dispatcher.php b/src/Foundation/Middleware/Dispatcher.php index 8f6e729..bfb730b 100644 --- a/src/Foundation/Middleware/Dispatcher.php +++ b/src/Foundation/Middleware/Dispatcher.php @@ -5,7 +5,7 @@ use ReflectionMethod; use Ytake\Heredity\Heredity; use Nazg\Foundation\Validation\Attribute; -use Nazg\Foundation\Validation\Validation; +use Nazg\Foundation\Validation\Validator; use Nazg\Foundation\Validation\ValidatorFactory; use Interop\Http\Server\MiddlewareInterface; use Interop\Http\Server\RequestHandlerInterface; @@ -27,11 +27,11 @@ protected function processor( MiddlewareInterface $middleware, ServerRequestInterface $request ): ResponseInterface { - $this->findAttributse($middleware, $request); + $this->validateInterceptor($middleware, $request); return $middleware->process($request, $this); } - protected function findAttributse( + protected function validateInterceptor( MiddlewareInterface $middleware, ServerRequestInterface $request ): void { @@ -39,10 +39,11 @@ protected function findAttributse( $attribute = $rm->getAttribute(Attribute::Named); if (is_array($attribute)) { if(array_key_exists($this->validatorIndex, $attribute)) { - $v = new ValidatorFactory( - $this->container?->get((string)$attribute[$this->validatorIndex]) - ); - var_dump($v->validate()); + $validator = $this->container?->get((string)$attribute[$this->validatorIndex]); + if($validator instanceof Validator) { + $factory = new ValidatorFactory($validator, $request); + $factory->validator()->validate(); + } } } } diff --git a/src/Foundation/Validation/Validation.php b/src/Foundation/Validation/Validation.php deleted file mode 100644 index 96aed86..0000000 --- a/src/Foundation/Validation/Validation.php +++ /dev/null @@ -1,21 +0,0 @@ -> -abstract class Validation { - - public function __construct( - protected ServerRequestInterface $request - ) {} - - public function validate(): bool { - return true; - } -} diff --git a/src/Foundation/Validation/ValidationException.php b/src/Foundation/Validation/ValidationException.php new file mode 100644 index 0000000..5c3fef7 --- /dev/null +++ b/src/Foundation/Validation/ValidationException.php @@ -0,0 +1,21 @@ +validator = $validator; + } + + public function errors(): array { + return $this->validator->errors()->toArray(); + } +} diff --git a/src/Foundation/Validation/Validator.php b/src/Foundation/Validation/Validator.php new file mode 100644 index 0000000..e484869 --- /dev/null +++ b/src/Foundation/Validation/Validator.php @@ -0,0 +1,55 @@ +> +abstract class Validator { + + protected ?ServerRequestInterface $request; + + protected Vector $messages = Vector{}; + + protected Vector $validateMethods = Vector{}; + + protected bool $shouldThrowException = false; + + // disabled type assert for request parameters + protected bool $skipValidateStructure = true; + + public function validateRequest(ServerRequestInterface $request): void { + $this->request = $request; + } + + public function validate(): bool { + if (!is_null($this->request)) { + $this->assertStructure(); + } + if($this->errors()->count()) { + if ($this->shouldThrowException) { + throw new ValidationException($this); + } + return false; + } + return true; + } + + <<__Memoize>> + public function errors(): ImmVector { + return $this->assertValidateResult()->immutable(); + } + + protected function assertStructure(): void { + if(!$this->skipValidateStructure) { + // here + } + } + + abstract protected function assertValidateResult(): Vector; +} diff --git a/src/Foundation/Validation/ValidatorFactory.php b/src/Foundation/Validation/ValidatorFactory.php index e0d514f..b181c47 100644 --- a/src/Foundation/Validation/ValidatorFactory.php +++ b/src/Foundation/Validation/ValidatorFactory.php @@ -2,16 +2,18 @@ namespace Nazg\Foundation\Validation; +use Psr\Http\Message\ServerRequestInterface; + class ValidatorFactory { public function __construct( - protected mixed $validatorName + protected Validator $validatorName, + protected ServerRequestInterface $request ) {} - public function validate(): bool { - if($this->validatorName instanceof Validation) { - return $this->validatorName->validate(); - } - return true; + public function validator(): Validator { + $validator = $this->validatorName; + $validator->validateRequest($this->request); + return $validator; } } diff --git a/tests/Action/ValidateAction.php b/tests/Action/ValidateAction.php index bd190f1..b983e36 100644 --- a/tests/Action/ValidateAction.php +++ b/tests/Action/ValidateAction.php @@ -8,7 +8,8 @@ use Zend\Diactoros\Response\JsonResponse; final class ValidateAction implements MiddlewareInterface { - <> + + <> public function process( ServerRequestInterface $request, RequestHandlerInterface $handler, diff --git a/tests/ApplicationTest.php b/tests/ApplicationTest.php index f6ba72a..2210d32 100644 --- a/tests/ApplicationTest.php +++ b/tests/ApplicationTest.php @@ -42,6 +42,9 @@ public function testShouldReturnQueryParam(): void { ); } + /** + * @expectedException \Nazg\Foundation\Validation\ValidationException + */ public function testShouldBeValidationFaild():void { $aggregator = new ConfigAggreagator([ new PhpFileProvider(__DIR__ . '/config/*.{hh,php}'), diff --git a/tests/Validation/MockValidateActionFaild.php b/tests/Validation/MockValidateActionFaild.php new file mode 100644 index 0000000..0613854 --- /dev/null +++ b/tests/Validation/MockValidateActionFaild.php @@ -0,0 +1,13 @@ + { + return new Vector(['error1']); + } +} diff --git a/tests/Validation/ValidateActionValid.php b/tests/Validation/ValidateActionValid.php deleted file mode 100644 index f51c43c..0000000 --- a/tests/Validation/ValidateActionValid.php +++ /dev/null @@ -1,7 +0,0 @@ -assertNotCount(0, $v->errors()); + $this->assertFalse($v->validate()); + } + + /** + * @expectedException \Facebook\TypeAssert\IncorrectTypeException + */ + public function testShouldThrowTypeException(): void { + $v = new MockStructureValidateFalied(); + $request = ServerRequestFactory::fromGlobals([ + 'REQUEST_URI' => '/testing/12?message=testing&server=hhvm', + 'QUERY_STRING' => 'message=testing&server=hhvm', + 'REQUEST_METHOD' => 'GET' + ], + [ + 'message' => 'testing', + 'server' => 'hhvm', + ]); + $v->validateRequest($request); + $v->validate(); + } + + public function testShouldThrow(): void { + $v = new MockStructureValidateSuccess(); + $request = ServerRequestFactory::fromGlobals([ + 'REQUEST_URI' => '/testing/12?message=testing&server=hhvm', + 'QUERY_STRING' => 'message=testing&server=hhvm', + 'REQUEST_METHOD' => 'GET' + ], + [ + 'message' => 'testing', + 'server' => 'hhvm', + ]); + $v->validateRequest($request); + $this->assertTrue($v->validate()); + } +} + +class MockValidateFalied extends Validator { + + protected bool $shouldThrowException = false; + + protected function assertValidateResult(): Vector { + return new Vector(['error1']); + } +} + +class MockStructureValidateFalied extends Validator { + + const type TestRequest = shape( + 'message' => string, + 'server' => int, + ); + + protected bool $shouldThrowException = false; + protected bool $skipValidateStructure = false; + protected function assertValidateResult(): Vector { + return new Vector(['error1']); + } + + protected function assertStructure(): void { + if(!$this->skipValidateStructure) { + TypeAssert\matches_type_structure( + type_structure(self::class, 'TestRequest'), + $this->request->getQueryParams(), + ); + } + } +} + +class MockStructureValidateSuccess extends Validator { + + const type TestRequest = shape( + 'message' => string, + 'server' => string, + ); + + protected bool $shouldThrowException = false; + protected bool $skipValidateStructure = false; + protected function assertValidateResult(): Vector { + $params = $this->request->getQueryParams(); + $v = Vector{}; + if ($params['message'] !== 'testing') { + $v = $v->concat(['error!']); + } + return $v; + } + + protected function assertStructure(): void { + if(!$this->skipValidateStructure) { + TypeAssert\matches_type_structure( + type_structure(self::class, 'TestRequest'), + $this->request->getQueryParams(), + ); + } + } +}