diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 80a71a1..1e51573 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['5.6', '7.0', '7.1', '7.2', '7.3'] + php-versions: ['7.2', '7.3'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: - uses: actions/checkout@v2 @@ -27,9 +27,7 @@ jobs: run: composer install --prefer-dist --no-progress - name: PHP Unit tests - run: | - vendor/bin/phpunit --testsuite travis-ci - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover + run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover - name: Upload code coverage data - run: php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover \ No newline at end of file + run: php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover diff --git a/README.md b/README.md index bf6031f..95c242e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Via Composer $ composer require davidepastore/slim-validation ``` -Requires Slim 3.0.0 or newer. +Requires Slim 4.0.0 or newer. ## Usage @@ -43,8 +43,11 @@ as it is middleware, you can also register it for all routes. ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; -$app = new \Slim\App(); +require __DIR__ . '/../vendor/autoload.php'; + +$app = AppFactory::create(); //Create the validators $usernameValidator = v::alnum()->noWhitespace()->length(1, 10); @@ -84,8 +87,11 @@ $app->run(); ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; + +require __DIR__ . '/../vendor/autoload.php'; -$app = new \Slim\App(); +$app = AppFactory::create(); //Create the validators $usernameValidator = v::alnum()->noWhitespace()->length(1, 10); @@ -138,8 +144,11 @@ $app->run(); ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; + +require __DIR__ . '/../vendor/autoload.php'; -$app = new \Slim\App(); +$app = AppFactory::create(); //Create the validators $routeParamValidator = v::numeric()->positive(); @@ -193,8 +202,11 @@ and you want to validate the `email.name` key. You can do it in this way: ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; -$app = new \Slim\App(); +require __DIR__ . '/../vendor/autoload.php'; + +$app = AppFactory::create(); //Create the validators $typeValidator = v::alnum()->noWhitespace()->length(3, 5); @@ -251,8 +263,11 @@ and you want to validate the `email.name` key. You can do it in this way: ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; + +require __DIR__ . '/../vendor/autoload.php'; -$app = new \Slim\App(); +$app = AppFactory::create(); //Create the validators $typeValidator = v::alnum()->noWhitespace()->length(3, 5); @@ -292,8 +307,11 @@ You can provide a callable function to translate the errors. ```php use Respect\Validation\Validator as v; +use Slim\Factory\AppFactory; + +require __DIR__ . '/../vendor/autoload.php'; -$app = new \Slim\App(); +$app = AppFactory::create(); //Create the validators $usernameValidator = v::alnum()->noWhitespace()->length(1, 10); diff --git a/composer.json b/composer.json index 96e32d4..8c31320 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,15 @@ } ], "require": { - "php": ">=5.5.0", + "php": "^7.2", "psr/http-message": "^1.0", - "respect/validation": "^1.1 >=1.1.23" + "respect/validation": "^1.1 >=1.1.23", + "slim/http": "^1.1", + "slim/psr7": "^0.3|^1.6" }, "require-dev": { - "slim/slim": "~3.0", - "phpunit/phpunit": "^4.0", + "slim/slim": "~4.0", + "phpunit/phpunit": "^8.0", "scrutinizer/ocular": "1.5.*" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8259dc4..f43c285 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="tests/bootstrap.php" > diff --git a/src/Validation.php b/src/Validation.php index 21fda11..0950629 100644 --- a/src/Validation.php +++ b/src/Validation.php @@ -2,6 +2,7 @@ namespace DavidePastore\Slim\Validation; +use Psr\Http\Server\RequestHandlerInterface; use Respect\Validation\Exceptions\NestedValidationException; /** @@ -89,16 +90,15 @@ public function __construct($validators = null, $translator = null, $options = [ * Validation middleware invokable class. * * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request - * @param \Psr\Http\Message\ResponseInterface $response PSR7 response - * @param callable $next Next middleware + * @param RequestHandlerInterface $handler RequestHandler * * @return \Psr\Http\Message\ResponseInterface */ - public function __invoke($request, $response, $next) + public function __invoke($request, RequestHandlerInterface $handler) { $this->errors = []; $params = $request->getParams(); - $params = array_merge((array) $request->getAttribute('routeInfo')[2], $params); + $params = array_merge((array)$request->getAttribute('routeInfo')[2], $params); $this->validate($params, $this->validators); $request = $request->withAttribute($this->errors_name, $this->getErrors()); @@ -106,7 +106,7 @@ public function __invoke($request, $response, $next) $request = $request->withAttribute($this->validators_name, $this->getValidators()); $request = $request->withAttribute($this->translator_name, $this->getTranslator()); - return $next($request, $response); + return $handler->handle($request); } /** @@ -119,7 +119,7 @@ public function __invoke($request, $response, $next) */ private function validate($params = [], $validators = [], $actualKeys = []) { - //Validate every parameters in the validators array + //Validate every parameter in the validators array foreach ($validators as $key => $validator) { $actualKeys[] = $key; $param = $this->getNestedParam($params, $actualKeys); @@ -155,7 +155,7 @@ private function getNestedParam($params = [], $keys = []) return $params; } else { $firstKey = array_shift($keys); - if ($this->isArrayLike($params) && array_key_exists($firstKey, $params)) { + if ($this->isArrayLike($params) && array_key_exists($firstKey, (array)$params)) { $params = (array) $params; $paramValue = $params[$firstKey]; diff --git a/tests/ValidationTest.php b/tests/ValidationTest.php index cb3e7f3..8533e6b 100644 --- a/tests/ValidationTest.php +++ b/tests/ValidationTest.php @@ -2,31 +2,34 @@ namespace DavidePastore\Slim\Validation\Tests; -use ReflectionProperty; -use Slim\Collection; -use Slim\Http\Body; -use Slim\Http\Environment; -use Slim\Http\Headers; -use Slim\Http\Request; -use Slim\Http\RequestBody; -use Slim\Http\Response; -use Slim\Http\Uri; +use Psr\Http\Server\RequestHandlerInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; +use Slim\Http\Factory\DecoratedServerRequestFactory; +use Slim\Http\ServerRequest; +use Slim\Psr7\Stream; +use Slim\Psr7\Environment; +use Slim\Psr7\Headers; +use Slim\Psr7\Request; +use Slim\Psr7\Response; +use Slim\Psr7\Factory\UriFactory; +use Slim\Psr7\Factory\ServerRequestFactory; use DavidePastore\Slim\Validation\Validation; use Respect\Validation\Validator as v; -class ValidationTest extends \PHPUnit_Framework_TestCase +class ValidationTest extends \PHPUnit\Framework\TestCase { /** * PSR7 request object. * - * @var Psr\Http\Message\RequestInterface + * @var ServerRequestInterface */ protected $request; /** * PSR7 response object. * - * @var Psr\Http\Message\ResponseInterface + * @var ResponseInterface */ protected $response; @@ -35,13 +38,13 @@ class ValidationTest extends \PHPUnit_Framework_TestCase */ public function setupGet() { - $uri = Uri::createFromString('https://example.com:443/foo/bar?username=davidepastore&age=89&optional=value'); - $headers = new Headers(); - $cookies = []; + $uriFactory = new UriFactory(); + $uri = $uriFactory->createUri('https://example.com:443/foo/bar?username=davidepastore&age=89&optional=value'); $env = Environment::mock(); - $serverParams = $env->all(); - $body = new Body(fopen('php://temp', 'r+')); - $this->request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body); + $serverParams = $env; + $requestFactory = new ServerRequestFactory(); + $serverRequestFactory = new DecoratedServerRequestFactory($requestFactory); + $this->request = $serverRequestFactory->createServerRequest('GET', $uri, $serverParams); $this->response = new Response(); } @@ -52,19 +55,23 @@ public function setupGet() */ public function setupJson($json) { - $uri = Uri::createFromString('https://example.com:443/foo'); + $uriFactory = new UriFactory(); + $uri = $uriFactory->createUri('https://example.com:443/foo'); $headers = new Headers(); - $headers->set('Content-Type', 'application/json;charset=utf8'); + $headers->setHeader('Content-Type', 'application/json;charset=utf8'); $cookies = []; - $env = Environment::mock([ + $serverParams = Environment::mock([ 'SCRIPT_NAME' => '/index.php', 'REQUEST_URI' => '/foo', 'REQUEST_METHOD' => 'POST', ]); - $serverParams = $env->all(); - $body = new RequestBody(); + $stream = fopen('php://temp', 'w+'); + stream_copy_to_stream(fopen('php://input', 'r'), $stream); + rewind($stream); + $body = new Stream($stream); $body->write(json_encode($json)); - $this->request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); + $request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); + $this->request = new ServerRequest($request); $this->response = new Response(); } @@ -75,19 +82,23 @@ public function setupJson($json) */ public function setupXml($xml) { - $uri = Uri::createFromString('https://example.com:443/foo'); + $uriFactory = new UriFactory(); + $uri = $uriFactory->createUri('https://example.com:443/foo'); $headers = new Headers(); - $headers->set('Content-Type', 'application/xml;charset=utf8'); + $headers->setHeader('Content-Type', 'application/xml;charset=utf8'); $cookies = []; - $env = Environment::mock([ + $serverParams = Environment::mock([ 'SCRIPT_NAME' => '/index.php', 'REQUEST_URI' => '/foo', 'REQUEST_METHOD' => 'POST', ]); - $serverParams = $env->all(); - $body = new RequestBody(); + $stream = fopen('php://temp', 'w+'); + stream_copy_to_stream(fopen('php://input', 'r'), $stream); + rewind($stream); + $body = new Stream($stream); $body->write($xml); - $this->request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); + $request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body); + $this->request = new ServerRequest($request); $this->response = new Response(); } @@ -115,20 +126,14 @@ public function testValidation($expectedValidators, $expectedTranslator, $expect $mw = new Validation($expectedValidators, $expectedTranslator, $options); } - $errors = null; - $hasErrors = null; - $validators = null; - $translator = null; - $next = function ($req, $res) use (&$errors, &$hasErrors, &$validators, &$translator) { - $errors = $req->getAttribute('errors'); - $hasErrors = $req->getAttribute('has_errors'); - $validators = $req->getAttribute('validators'); - $translator = $req->getAttribute('translator'); - - return $res; - }; + $handler = $this->setupHandler(); + + $response = $mw($this->request, $handler); - $response = $mw($this->request, $this->response, $next); + $errors = $handler->getErrors(); + $hasErrors = $handler->getHasErrors(); + $validators = $handler->getValidators(); + $translator = $handler->getTranslator(); $this->assertEquals($expectedHasErrors, $hasErrors); $this->assertEquals($expectedErrors, $errors); @@ -574,17 +579,6 @@ public function testSetValidators() ); $mw = new Validation($expectedValidators); - $errors = null; - $hasErrors = null; - $validators = []; - $next = function ($req, $res) use (&$errors, &$hasErrors, &$validators) { - $errors = $req->getAttribute('errors'); - $hasErrors = $req->getAttribute('has_errors'); - $validators = $req->getAttribute('validators'); - - return $res; - }; - $newUsernameValidator = v::alnum()->noWhitespace()->length(1, 10); $newAgeValidator = v::numeric()->positive()->between(1, 20); $newValidators = array( @@ -594,7 +588,13 @@ public function testSetValidators() $mw->setValidators($newValidators); - $response = $mw($this->request, $this->response, $next); + $handler = $this->setupHandler(); + + $response = $mw($this->request, $handler); + + $errors = $handler->getErrors(); + $hasErrors = $handler->getHasErrors(); + $validators = $handler->getValidators(); $expectedErrors = array( 'username' => array( @@ -630,19 +630,6 @@ public function testSetTranslator() $mw = new Validation($expectedValidators, $translator); - $errors = null; - $hasErrors = null; - $translator = null; - $validators = []; - $next = function ($req, $res) use (&$errors, &$hasErrors, &$translator, &$validators) { - $errors = $req->getAttribute('errors'); - $hasErrors = $req->getAttribute('has_errors'); - $validators = $req->getAttribute('validators'); - $translator = $req->getAttribute('translator'); - - return $res; - }; - $newTranslator = function ($message) { $messages = [ 'These rules must pass for {{name}}' => 'Queste regole devono passare per {{name}} (nuovo)', @@ -655,7 +642,14 @@ public function testSetTranslator() $mw->setTranslator($newTranslator); - $response = $mw($this->request, $this->response, $next); + $handler = $this->setupHandler(); + + $response = $mw($this->request, $handler); + + $errors = $handler->getErrors(); + $hasErrors = $handler->getHasErrors(); + $validators = $handler->getValidators(); + $translator = $handler->getTranslator(); $this->assertTrue($hasErrors); $expectedErrors = array( @@ -668,19 +662,6 @@ public function testSetTranslator() $this->assertEquals($newTranslator, $translator); } - public function requestFactory($envData = []) - { - $env = Environment::mock($envData); - $uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123'); - $headers = Headers::createFromEnvironment($env); - $cookies = []; - $serverParams = $env->all(); - $body = new RequestBody(); - $request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body); - - return $request; - } - /** * Test for validation. * @@ -689,28 +670,21 @@ public function requestFactory($envData = []) public function testRouteParamValidation($expectedValidators, $expectedHasErrors, $expectedErrors, $attributes) { $this->setupGet(); - $attrProp = new ReflectionProperty($this->request, 'attributes'); - $attrProp->setAccessible(true); - $attrProp->setValue($this->request, new Collection(array('routeInfo' => array( - 0, - 1, - $attributes, - )))); + $this->request = $this->request->withAttribute('routeInfo', [ + 0, + 1, + $attributes + ]); $mw = new Validation($expectedValidators); - $errors = null; - $hasErrors = null; - $validators = []; - $next = function ($req, $res) use (&$errors, &$hasErrors, &$validators) { - $errors = $req->getAttribute('errors'); - $hasErrors = $req->getAttribute('has_errors'); - $validators = $req->getAttribute('validators'); + $handler = $this->setupHandler(); - return $res; - }; + $response = $mw($this->request, $handler); - $response = $mw($this->request, $this->response, $next); + $errors = $handler->getErrors(); + $hasErrors = $handler->getHasErrors(); + $validators = $handler->getValidators(); $this->assertEquals($expectedValidators, $validators); $this->assertEquals($expectedHasErrors, $hasErrors); @@ -747,4 +721,50 @@ public function routeParamValidationProvider() ), ); } + + /** + * Create a new RequestHandler that can pull necessary attributes from the $request after validation. + * + * @return RequestHandlerInterface + */ + private function setupHandler() : RequestHandlerInterface + { + return new class implements RequestHandlerInterface { + protected $errors = null; + protected $hasErrors = null; + protected $translator = null; + protected $validators = []; + + public function getErrors() + { + return $this->errors; + } + public function getHasErrors() + { + return $this->hasErrors; + } + public function getTranslator() + { + return $this->translator; + } + public function getValidators() + { + return $this->validators; + } + + public function handle(ServerRequestInterface $request): ResponseInterface + { + $getRequestAttributes = function ($req) { + $this->errors = $req->getAttribute('errors'); + $this->hasErrors = $req->getAttribute('has_errors'); + $this->validators = $req->getAttribute('validators'); + $this->translator = $req->getAttribute('translator'); + }; + + $getRequestAttributes($request); + + return new Response(); + } + }; + } }