diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f2f8a4..14b3c43 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,7 @@ jobs: php: - '7.4' - '8.0' + - '8.1-nightly' steps: - name: Check out code @@ -56,6 +57,7 @@ jobs: --no-progress --no-suggest --prefer-dist + ${{ matrix.php == '8.1-nightly' && '--ignore-platform-reqs' || '' }} - name: Install lowest dependencies if: ${{ matrix.dependencies == 'low' }} @@ -66,10 +68,12 @@ jobs: --no-suggest --prefer-dist --prefer-lowest + ${{ matrix.php == '8.1-nightly' && '--ignore-platform-reqs' || '' }} - name: PHPUnit run: vendor/bin/phpunit --coverage-clover coverage.xml + ${{ matrix.php == '8.1-nightly' && '--debug' || '' }} - name: Submit code coverage if: ${{ always() }} diff --git a/CHANGELOG.md b/CHANGELOG.md index a683938..677788e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Framework-generated files are now auto-detected thanks to the above compile requirement (#84) - `Dispatcher::setEndpointList()` and `Dispatcher::setParserList()` are now internal use only, and are no longer called in the generated front controller (#84) - `Dispatcher::dispatch()` now requires `ServerRequestInterface` as a parameter. This replaces `setRequest` (#101) +- The body parser list (based on MIME-types) is now explicitly hardcoded. + Previously this was tied to a scanned vendor directory, so in practice nothing has changed. + This may become configurable in the future. +- Dispatcher::ENDPOINT_LIST has been marked internal ### Deprecated - Direct use of the HTTPMethod class is considered deprecated. @@ -44,6 +48,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - `Dispatcher::setAuthProviders()` (use setContainer) - `Dispatcher::setErrorHandler()` (use setContainer) - `Dispatcher::setRequest()` (provide the request directly to `::dispatch()`) +- `Dispatcher::PARSER_LIST` constant - `Interfaces\EndpointInterface::authenticate()` - this drops legacy authentication support entirely, and will no longer be used even if still defined in implementing classes - `Traits\Authentication\BearerToken` - `Traits\DeleteRequest` diff --git a/composer.json b/composer.json index fe9d08c..4c019c0 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "An API framework", "require": { "php": "^7.4 || ^8.0", - "firehed/common": "^1.0.5", + "composer/composer": "^2.1", "firehed/input": "^2.1.5", "psr/container": "^1.0 || ^2.0", "psr/http-message": "^1.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5dfb686..8229c26 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,12 +1,12 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\, string given\\.$#" + message: "#^Method Firehed\\\\API\\\\ConfigTest\\:\\:constructProvider\\(\\) should return array\\\\> but returns array\\\\|class\\-string\\>\\>\\.$#" count: 1 path: tests/ConfigTest.php - - message: "#^Method Firehed\\\\API\\\\ConfigTest\\:\\:constructProvider\\(\\) should return array\\\\> but returns array\\\\|class\\-string\\>\\>\\.$#" + message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\, string given\\.$#" count: 1 path: tests/ConfigTest.php @@ -15,3 +15,18 @@ parameters: count: 1 path: tests/ContainerTest.php + - + message: "#^Parameter \\#1 \\$endpointList of method Firehed\\\\API\\\\Dispatcher\\:\\:setEndpointList\\(\\) expects array\\<'DELETE'\\|'GET'\\|'OPTIONS'\\|'PATCH'\\|'POST'\\|'PUT', array\\\\>\\>\\|string, array\\('GET' \\=\\> array\\('/c' \\=\\> 'EP'\\)\\) given\\.$#" + count: 1 + path: tests/DispatcherTest.php + + - + message: "#^Parameter \\#1 \\$endpointList of method Firehed\\\\API\\\\Dispatcher\\:\\:setEndpointList\\(\\) expects array\\<'DELETE'\\|'GET'\\|'OPTIONS'\\|'PATCH'\\|'POST'\\|'PUT', array\\\\>\\>\\|string, array\\('GET' \\=\\> array\\('/cb' \\=\\> 'CBClass'\\)\\) given\\.$#" + count: 1 + path: tests/DispatcherTest.php + + - + message: "#^Parameter \\#1 \\$endpointList of method Firehed\\\\API\\\\Dispatcher\\:\\:setEndpointList\\(\\) expects array\\<'DELETE'\\|'GET'\\|'OPTIONS'\\|'PATCH'\\|'POST'\\|'PUT', array\\\\>\\>\\|string, array\\('GET' \\=\\> array\\('/container' \\=\\> 'ClassThatDoesNotExi…'\\)\\) given\\.$#" + count: 1 + path: tests/DispatcherTest.php + diff --git a/phpstan.neon b/phpstan.neon index 2c98816..4ed514e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,10 +2,10 @@ includes: - phpstan-baseline.neon - vendor/phpstan/phpstan-phpunit/extension.neon parameters: - ignoreErrors: - - '#Call to method setInterface\(\) on an unknown class Firehed\\Common\\this#' excludes_analyse: - vendor - level: 8 + level: max + paths: + - . stubFiles: - tests/stubs/xdebug_get_headers.stub diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9d2221c..b7e819e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,11 +11,11 @@ tests - - - src - - + + + src/Enums/HTTPMethodGTE81.php + + diff --git a/src/Console/CompileAll.php b/src/Console/CompileAll.php index a4a45c0..8e8c9ca 100644 --- a/src/Console/CompileAll.php +++ b/src/Console/CompileAll.php @@ -3,17 +3,26 @@ namespace Firehed\API\Console; +use Composer\Autoload\ClassMapGenerator; use Firehed\API\Config; use Firehed\API\Dispatcher; use Firehed\API\Interfaces\EndpointInterface; use Firehed\Input\Interfaces\ParserInterface; -use Firehed\Common\ClassMapGenerator; +use Firehed\Input\Parsers; +use ReflectionClass; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; +use function array_filter; +use function array_keys; +use function array_reduce; +use function array_values; +use function get_class; +use function gmdate; + class CompileAll extends Command { /** @var Config */ @@ -37,34 +46,51 @@ protected function execute(InputInterface $input, OutputInterface $output): int $logger->debug('Current directory: {cwd}', ['cwd' => getcwd()]); $logger->debug('Building classmap'); - // Build out the endpoint map - (new ClassMapGenerator()) - ->setPath(getcwd().'/'.$this->config->get('source')) - ->setInterface(EndpointInterface::class) - ->addCategory('getMethod') - ->setMethod('getURI') - ->setNamespace($this->config->get(Config::KEY_NAMESPACE)) - ->setOutputFile(Dispatcher::ENDPOINT_LIST) - ->generate(); + + $endpoints = $this->getFilteredClasses( + $this->config->get(Config::KEY_SOURCE), + function (ReflectionClass $rc): bool { + if (!$rc->isInstantiable()) { + return false; + } + if (!$rc->implementsInterface(EndpointInterface::class)) { + return false; + } + return true; + } + ); + $endpointMap = array_reduce($endpoints, function (array $carry, ReflectionClass $rc) { + $instance = $rc->newInstanceWithoutConstructor(); + assert($instance instanceof EndpointInterface); // Filtered above + $carry[$instance->getMethod()][$instance->getUri()] = $rc->getName(); + return $carry; + }, []); + $endpointMap['@gener'.'ated'] = gmdate('c'); + file_put_contents( + Dispatcher::ENDPOINT_LIST, + sprintf("writeln(sprintf( 'Wrote endpoint map to %s', Dispatcher::ENDPOINT_LIST )); - $logger->debug('Building parser map'); - // Also do the parser map - (new ClassMapGenerator()) - ->setPath(getcwd().'/'.'vendor/firehed/input/src/Parsers') - ->setInterface(ParserInterface::class) - ->setMethod('getSupportedMimeTypes') - ->setNamespace('Firehed\Input\Parsers') - ->setOutputFile(Dispatcher::PARSER_LIST) - ->generate(); - $output->writeln(sprintf( - 'Wrote parser map to %s', - Dispatcher::PARSER_LIST - )); return 0; } + + /** + * @param callable(ReflectionClass): bool $filter + * @return ReflectionClass[] + */ + private function getFilteredClasses(string $directory, callable $filter): array + { + /** @var array */ + $cm = ClassMapGenerator::createMap($directory); + $classes = array_keys($cm); + $rcs = array_map(fn ($fqcn) => new ReflectionClass($fqcn), $classes); + $result = array_filter($rcs, $filter); + // Compact the result set + return array_values($result); + } } diff --git a/src/Dispatcher.php b/src/Dispatcher.php index bcb6c95..70825f2 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -10,6 +10,9 @@ use Firehed\API\Interfaces\HandlesOwnErrorsInterface; use Firehed\Common\ClassMapper; use Firehed\Input\Containers\ParsedInput; +use Firehed\Input\Interfaces\ParserInterface; +use Firehed\Input\Parsers; +use InvalidArgumentException; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -19,10 +22,25 @@ use OutOfBoundsException; use UnexpectedValueException; +use function array_key_exists; +use function array_shift; +use function assert; +use function explode; +use function file_exists; +use function is_array; +use function is_int; +use function is_string; +use function parse_str; +use function preg_match; +use function strtoupper; + +/** + * @phpstan-type EndpointMap array>> + */ class Dispatcher implements RequestHandlerInterface { + /** @internal */ const ENDPOINT_LIST = '__endpoint_list__.php'; - const PARSER_LIST = '__parser_list__.php'; /** @var ?ContainerInterface */ private $container; @@ -33,11 +51,14 @@ class Dispatcher implements RequestHandlerInterface /** @var bool */ private $containerHasErrorHandler = false; - /** @var string | string[][] */ + /** @var string | EndpointMap */ private $endpointList = self::ENDPOINT_LIST; - /** @var string | string[] */ - private $parserList = self::PARSER_LIST; + /** @var array> */ + private $parsers = [ + 'application/json' => Parsers\JSON::class, + 'application/x-www-form-urlencoded' => Parsers\URLEncoded::class, + ]; /** @var MiddlewareInterface[] */ private $psrMiddleware = []; @@ -84,22 +105,6 @@ public function setContainer(ContainerInterface $container): self return $this; } - /** - * Set the parser list. Can be an array consumable by ClassMapper or - * a string representing a file parsable by same. The list must map - * MIME-types to Firehed\Input\ParserInterface class names. - * - * @internal Overrides the standard parser list. Used primarily for unit - * testing. - * @param string | string[] $parserList The parser list or its path - * @return self - */ - public function setParserList($parserList): self - { - $this->parserList = $parserList; - return $this; - } - /** * Set the endpoint list. Can be an array consumable by ClassMapper or * a string representing a file parsable by same. The list must be @@ -108,7 +113,7 @@ public function setParserList($parserList): self * * @internal Overrides the standard endpoint list. Used primarily for unit * testing. - * @param string | string[][] $endpointList The endpoint list or its path + * @param string|EndpointMap $endpointList The endpoint list or its path * @return self */ public function setEndpointList($endpointList): self @@ -146,17 +151,49 @@ public function dispatch(ServerRequestInterface $request): ResponseInterface return $mwDispatcher->handle($request); } + /** + * @return array{ + * ?class-string, + * ?array, + * } + */ + private function routeRequest(ServerRequestInterface $request): array + { + $endpoints = self::loadEndpoints($this->endpointList); + $method = strtoupper($request->getMethod()); + if (!array_key_exists($method, $endpoints)) { + return [null, null]; + } + $endpointsForMethod = $endpoints[$method]; + $requestPath = $request->getUri()->getPath(); + foreach ($endpointsForMethod as $uri => $fqcn) { + $pattern = '#^' . $uri . '#'; + if (preg_match($pattern, $requestPath, $matches)) { + // Filter out numeric keys from match output - we only want to + // retain named captures + foreach ($matches as $key => $value) { + if (is_int($key)) { + unset($matches[$key]); + } + } + /** @var array $matches */ + return [$fqcn, $matches]; + } + } + // No match + return [null, null]; + } + private function doDispatch(ServerRequestInterface $request): ResponseInterface { /** @var ?Interfaces\EndpointInterface */ $endpoint = null; try { - [$fqcn, $uriData] = (new ClassMapper($this->endpointList)) - ->filter(strtoupper($request->getMethod())) - ->search($request->getUri()->getPath()); + [$fqcn, $uriData] = $this->routeRequest($request); if (!$fqcn) { throw new OutOfBoundsException('Endpoint not found', 404); } + assert($uriData !== null); if ($this->container && $this->container->has($fqcn)) { $endpoint = $this->container->get($fqcn); } else { @@ -223,12 +260,11 @@ private function parseInput(ServerRequestInterface $request): ParsedInput $mediaType = array_shift($directives); // Future: trim and format directives; e.g. ' charset=utf-8' => // ['charset' => 'utf-8'] - list($parser_class) = (new ClassMapper($this->parserList)) - ->search($mediaType); - if (!$parser_class) { + if (!array_key_exists($mediaType, $this->parsers)) { throw new OutOfBoundsException('Unsupported Content-type', 415); } - $parser = new $parser_class; + $parserClass = $this->parsers[$mediaType]; + $parser = new $parserClass; $data = $parser->parse((string)$request->getBody()); } return new ParsedInput($data); @@ -242,4 +278,22 @@ private function getQueryStringData(ServerRequestInterface $request): ParsedInpu parse_str($query, $data); return new ParsedInput($data); } + + /** + * @param string|EndpointMap $data + * @return EndpointMap + */ + private static function loadEndpoints($data): array + { + if (is_array($data)) { + return $data; + } elseif (is_string($data)) { + if (!file_exists($data)) { + throw new InvalidArgumentException('Invalid file'); + } + return (fn () => include $data)(); + } else { + throw new InvalidArgumentException('Invalid format'); + } + } } diff --git a/src/Enums/HTTPMethod.php b/src/Enums/HTTPMethod.php index 6fa438d..8d2dc42 100644 --- a/src/Enums/HTTPMethod.php +++ b/src/Enums/HTTPMethod.php @@ -4,35 +4,18 @@ namespace Firehed\API\Enums; -use Firehed\Common\Enum; +use function class_alias; +use function version_compare; + +use const PHP_VERSION; /** - * Direct use of this class (via these static methods) is deprecated. Instead, - * implementations should rely on the Request traits. - * - * This will be converted to a native Enum in PHP 8.1. - * - * @method static HTTPMethod DELETE() - * @method static HTTPMethod GET() - * @method static HTTPMethod OPTIONS() - * @method static HTTPMethod PATCH() - * @method static HTTPMethod POST() - * @method static HTTPMethod PUT() + * This file generates an alias for HTTPMethod that uses either native enums + * (on supported PHP versions) or interface constants. The two implementations + * are not to be used directly. */ -class HTTPMethod extends Enum -{ - - // Other methods exist, but these are the only relevant ones for RESTful - // APIs - const GET = 'GET'; - const PATCH = 'PATCH'; - const POST = 'POST'; - const PUT = 'PUT'; - const DELETE = 'DELETE'; - const OPTIONS = 'OPTIONS'; - - public function __toString() - { - return $this->getValue(); - } +if (version_compare(PHP_VERSION, '8.1.0', '>=')) { + class_alias(HTTPMethodGTE81::class, HTTPMethod::class); +} else { + class_alias(HTTPMethodLTE80::class, HTTPMethod::class); } diff --git a/src/Enums/HTTPMethodGTE81.php b/src/Enums/HTTPMethodGTE81.php new file mode 100644 index 0000000..d4cea5e --- /dev/null +++ b/src/Enums/HTTPMethodGTE81.php @@ -0,0 +1,18 @@ +getEndpoint()->getMethod(); - $this->assertInstanceOf( - 'Firehed\API\Enums\HTTPMethod', - $method, - 'getMethod did not return an HTTPMethod enum' - ); + // 8.1: Enum logic + + $rc = new ReflectionClass(HTTPMethod::class); + $constants = $rc->getConstants(); + $this->assertContains($method, $constants, 'Invalid HTTP method'); } } diff --git a/src/Traits/Request/Delete.php b/src/Traits/Request/Delete.php index 3e6e562..a8d5b78 100644 --- a/src/Traits/Request/Delete.php +++ b/src/Traits/Request/Delete.php @@ -7,9 +7,8 @@ trait Delete { - - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::DELETE(); + return HTTPMethod::DELETE; } } diff --git a/src/Traits/Request/Get.php b/src/Traits/Request/Get.php index 08a5d62..bcbd5b1 100644 --- a/src/Traits/Request/Get.php +++ b/src/Traits/Request/Get.php @@ -7,9 +7,8 @@ trait Get { - - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::GET(); + return HTTPMethod::GET; } } diff --git a/src/Traits/Request/Options.php b/src/Traits/Request/Options.php index 580f44d..d419c9a 100644 --- a/src/Traits/Request/Options.php +++ b/src/Traits/Request/Options.php @@ -7,9 +7,8 @@ trait Options { - - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::OPTIONS(); + return HTTPMethod::OPTIONS; } } diff --git a/src/Traits/Request/Patch.php b/src/Traits/Request/Patch.php index c0db3fc..070d4b2 100644 --- a/src/Traits/Request/Patch.php +++ b/src/Traits/Request/Patch.php @@ -7,8 +7,8 @@ trait Patch { - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::PATCH(); + return HTTPMethod::PATCH; } } diff --git a/src/Traits/Request/Post.php b/src/Traits/Request/Post.php index ddc41c1..803b0a0 100644 --- a/src/Traits/Request/Post.php +++ b/src/Traits/Request/Post.php @@ -7,9 +7,8 @@ trait Post { - - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::POST(); + return HTTPMethod::POST; } } diff --git a/src/Traits/Request/Put.php b/src/Traits/Request/Put.php index c4fd40b..9e1a5fb 100644 --- a/src/Traits/Request/Put.php +++ b/src/Traits/Request/Put.php @@ -7,8 +7,8 @@ trait Put { - public function getMethod(): HTTPMethod + public function getMethod(): string { - return HTTPMethod::PUT(); + return HTTPMethod::PUT; } } diff --git a/tests/Console/CompileAllTest.php b/tests/Console/CompileAllTest.php index 91c8d32..9d49fdc 100644 --- a/tests/Console/CompileAllTest.php +++ b/tests/Console/CompileAllTest.php @@ -43,14 +43,10 @@ public function testExecute(): void $tester = new CommandTester($command); $tester->execute([]); $this->assertFileExists(Dispatcher::ENDPOINT_LIST, 'Endpoint list not generated'); - $this->assertFileExists(Dispatcher::PARSER_LIST, 'Parser list not generated'); $endpoints = include Dispatcher::ENDPOINT_LIST; - $parsers = include Dispatcher::PARSER_LIST; $this->validateEndpointList($endpoints); - $this->validateParserList($parsers); } finally { @unlink(Dispatcher::ENDPOINT_LIST); - @unlink(Dispatcher::PARSER_LIST); } } @@ -63,14 +59,4 @@ private function validateEndpointList(array $data): void $this->assertArrayHasKey('GET', $data); $this->assertContains(EndpointFixture::class, $data['GET']); } - - /** - * @param string[] $data - */ - private function validateParserList(array $data): void - { - $this->assertArrayHasKey('@gener'.'ated', $data); - $this->assertArrayHasKey('application/json', $data); - $this->assertArrayHasKey('application/x-www-form-urlencoded', $data); - } } diff --git a/tests/DispatcherTest.php b/tests/DispatcherTest.php index ecd8733..32ad99c 100644 --- a/tests/DispatcherTest.php +++ b/tests/DispatcherTest.php @@ -7,6 +7,7 @@ use Exception; use Firehed\API\Authentication; use Firehed\API\Authorization; +use Firehed\API\Enums\HTTPMethod; use Firehed\API\Interfaces\EndpointInterface; use Firehed\API\Interfaces\HandlesOwnErrorsInterface; use Firehed\API\Errors\HandlerInterface; @@ -80,17 +81,6 @@ public function testSetEndpointListReturnsSelf(): void ); } - /** @covers ::setParserList */ - public function testSetParserListReturnsSelf(): void - { - $d = new Dispatcher(); - $this->assertSame( - $d, - $d->setParserList('list'), - 'setParserList did not return $this' - ); - } - // ----(Success case)------------------------------------------------------- /** @@ -111,7 +101,6 @@ public function testDataReachesEndpoint(): void $response = (new Dispatcher()) ->setEndpointList($this->getEndpointListForFixture()) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->checkResponse($response, 200); $data = json_decode((string)$response->getBody(), true); @@ -142,7 +131,6 @@ public function testQueryStringDataReachesEndpoint(): void $response = (new Dispatcher()) ->setEndpointList($this->getEndpointListForFixture()) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->checkResponse($response, 200); $data = json_decode((string)$response->getBody(), true); @@ -214,13 +202,12 @@ public function testPsr15(): void ->method('execute') ->willReturn($response); - $routes = ['GET' => ['/c' => 'EP']]; + $routes = [HTTPMethod::GET => ['/c' => 'EP']]; $res = $dispatcher ->addMiddleware($mw1) ->addMiddleware($mw2) ->setContainer($this->getMockContainer(['EP' => $endpoint])) ->setEndpointList($routes) - ->setParserList($this->getDefaultParserList()) ->dispatch($request); $this->assertSame($modifiedResponse, $res, 'Dispatcher returned different response'); @@ -251,7 +238,7 @@ function ($caught) use ($execute, $error) { $req = $this->getMockRequestWithUriPath('/cb', 'GET'); $list = [ - 'GET' => [ + HTTPMethod::GET => [ '/cb' => 'CBClass', ], ]; @@ -259,7 +246,6 @@ function ($caught) use ($execute, $error) { $ret = (new Dispatcher()) ->setContainer($this->getMockContainer(['CBClass' => $endpoint])) ->setEndpointList($list) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->fail( "The exception thrown from the error handler's failure should ". @@ -298,7 +284,6 @@ public function testNoRouteMatchReturns404(): void $this->expectExceptionCode(404); $ret = (new Dispatcher()) ->setEndpointList([]) // No routes - ->setParserList([]) ->dispatch($req); } @@ -316,7 +301,6 @@ public function testFailedInputValidationCanReachErrorHandlers(): void try { $response = (new Dispatcher()) ->setEndpointList($this->getEndpointListForFixture()) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->fail('An exception should have been thrown'); } catch (Throwable $e) { @@ -333,7 +317,6 @@ public function testUnsupportedContentTypeCanReachErrorHandlers(): void try { $response = (new Dispatcher()) ->setEndpointList($this->getEndpointListForFixture()) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->fail('An exception should have been thrown'); } catch (Throwable $e) { @@ -353,7 +336,6 @@ public function testMatchingContentTypeWithDirectives(): void $req = $req->withHeader('Content-type', $contentType); $response = (new Dispatcher()) ->setEndpointList($this->getEndpointListForFixture()) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); $this->checkResponse($response, 200); } @@ -448,7 +430,6 @@ public function testErrorHandlerHandles404(): void $request = $this->getMockRequestWithUriPath('/'); $finalResponse = (new Dispatcher()) ->setEndpointList([]) - ->setParserList([]) ->setContainer($container) ->dispatch($request); $this->assertSame($response, $finalResponse); @@ -703,7 +684,7 @@ private function executeMockRequestOnEndpoint( ): ResponseInterface { $req = $this->getMockRequestWithUriPath('/container', 'GET', []); $list = [ - 'GET' => [ + HTTPMethod::GET => [ '/container' => 'ClassThatDoesNotExist', ], ]; @@ -717,30 +698,25 @@ private function executeMockRequestOnEndpoint( $response = $dispatcher ->setContainer($this->getMockContainer($containerValues)) ->setEndpointList($list) - ->setParserList($this->getDefaultParserList()) ->dispatch($req); return $response; } - /** @return string[][] */ + /** + * @return array>> + */ private function getEndpointListForFixture(): array { return [ - 'GET' => [ + HTTPMethod::GET => [ '/user/(?P[1-9]\d*)' => __NAMESPACE__.'\EndpointFixture' ], - 'POST' => [ + HTTPMethod::POST => [ '/user/(?P[1-9]\d*)' => __NAMESPACE__.'\EndpointFixture' ], ]; } - private function getDefaultParserList(): string - { - // This could also be dynamically built - return dirname(__DIR__).'/vendor/firehed/input/src/Parsers/__parser_list__.json'; - } - /** * @param array $values */ diff --git a/tests/EndpointFixture.php b/tests/EndpointFixture.php index c348673..385789d 100644 --- a/tests/EndpointFixture.php +++ b/tests/EndpointFixture.php @@ -15,6 +15,7 @@ class EndpointFixture implements Interfaces\EndpointInterface { + use Traits\Request\Get; const STATUS_ERROR = 999; @@ -54,11 +55,6 @@ public function validate($value): bool ]; } - public function getMethod(): Enums\HTTPMethod - { - return Enums\HTTPMethod::GET(); - } - public function execute(SafeInput $input): Response { // Use PHPUnit mocks outside of the TestCase... the DSL isn't quite as diff --git a/tests/Traits/Request/DeleteTest.php b/tests/Traits/Request/DeleteTest.php index 6713ccf..c0f6ca5 100644 --- a/tests/Traits/Request/DeleteTest.php +++ b/tests/Traits/Request/DeleteTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Delete; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::DELETE(), + \Firehed\API\Enums\HTTPMethod::DELETE, $obj->getMethod(), 'getMethod did not return HTTP DELETE' ); diff --git a/tests/Traits/Request/GetTest.php b/tests/Traits/Request/GetTest.php index fd5754b..07f4a00 100644 --- a/tests/Traits/Request/GetTest.php +++ b/tests/Traits/Request/GetTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Get; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::GET(), + \Firehed\API\Enums\HTTPMethod::GET, $obj->getMethod(), 'getMethod did not return HTTP GET' ); diff --git a/tests/Traits/Request/OptionsTest.php b/tests/Traits/Request/OptionsTest.php index 96d3af9..a39f9e5 100644 --- a/tests/Traits/Request/OptionsTest.php +++ b/tests/Traits/Request/OptionsTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Options; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::OPTIONS(), + \Firehed\API\Enums\HTTPMethod::OPTIONS, $obj->getMethod(), 'getMethod did not return HTTP OPTIONS' ); diff --git a/tests/Traits/Request/PatchTest.php b/tests/Traits/Request/PatchTest.php index 27580d0..b64ccf1 100644 --- a/tests/Traits/Request/PatchTest.php +++ b/tests/Traits/Request/PatchTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Patch; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::PATCH(), + \Firehed\API\Enums\HTTPMethod::PATCH, $obj->getMethod(), 'getMethod did not return HTTP PATCH' ); diff --git a/tests/Traits/Request/PostTest.php b/tests/Traits/Request/PostTest.php index 62d376e..261b2e3 100644 --- a/tests/Traits/Request/PostTest.php +++ b/tests/Traits/Request/PostTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Post; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::POST(), + \Firehed\API\Enums\HTTPMethod::POST, $obj->getMethod(), 'getMethod did not return HTTP POST' ); diff --git a/tests/Traits/Request/PutTest.php b/tests/Traits/Request/PutTest.php index 5018119..a67bde7 100644 --- a/tests/Traits/Request/PutTest.php +++ b/tests/Traits/Request/PutTest.php @@ -14,7 +14,7 @@ public function testGetMethod(): void use Put; }; $this->assertEquals( - \Firehed\API\Enums\HTTPMethod::PUT(), + \Firehed\API\Enums\HTTPMethod::PUT, $obj->getMethod(), 'getMethod did not return HTTP PUT' );