From 52b5f92ead71a2afe6f4902104ab58e248a79abd Mon Sep 17 00:00:00 2001 From: Koldo Picaza <1093654+kpicaza@users.noreply.github.com> Date: Sat, 14 Aug 2021 21:04:17 +0200 Subject: [PATCH] add required classes and factories --- composer.json | 8 +- src/Container/ApplicationFactory.php | 47 ++++++++++++ src/Container/Config/ConfigProvider.php | 23 ++++++ src/MiddlewarePipeline.php | 93 +++++++++++++++++++++++ src/RoadRunnerApplication.php | 87 +++++++++++++++++++++ test/Container/ApplicationFactoryTest.php | 43 +++++++++++ 6 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 src/Container/ApplicationFactory.php create mode 100644 src/Container/Config/ConfigProvider.php create mode 100644 src/MiddlewarePipeline.php create mode 100644 src/RoadRunnerApplication.php create mode 100644 test/Container/ApplicationFactoryTest.php diff --git a/composer.json b/composer.json index a206b6a..e5ef321 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "antidot-fw/roadd-runner", + "name": "antidot-fw/road-runner", "description": "Antidot Framework integgration wit Road Runner PHP/GO concurrency server.", "keywords": [ "psr-11", @@ -15,7 +15,9 @@ } ], "require": { - "php": "^7.4.3|^8.0" + "php": "^7.4.3|^8.0", + "antidot-fw/framework": "^0.2.1", + "ramsey/uuid": "^4.2" }, "require-dev": { "infection/infection": "^0.24.0", @@ -41,12 +43,14 @@ "@cs-check", "@test", "@inspect", + "@psalm", "@infection" ], "cs-check": "phpcs src --colors", "cs-fix": "phpcbf src --colors", "inspect": "phpstan analyse src -l7 --ansi", "infection": "XDEBUG_MODE=coverage infection --ansi", + "psalm": "psalm", "test": "phpunit --colors=always" }, "config": { diff --git a/src/Container/ApplicationFactory.php b/src/Container/ApplicationFactory.php new file mode 100644 index 0000000..de7f887 --- /dev/null +++ b/src/Container/ApplicationFactory.php @@ -0,0 +1,47 @@ +get(EmitterStack::class); + /** @var RequestFactory $requestFactory */ + $requestFactory = $container->get(RequestFactory::class); + /** @var ErrorResponseGenerator $errorResponseGenerator */ + $errorResponseGenerator = $container->get(ErrorResponseGenerator::class); + $runner = new RequestHandlerRunner($pipeline, $emitterStack, $requestFactory(), $errorResponseGenerator); + /** @var Router $router */ + $router = $container->get(Router::class); + /** @var MiddlewareFactory $middleware */ + $middleware = $container->get(MiddlewareFactory::class); + /** @var RouteFactory $routeFactory */ + $routeFactory = $container->get(RouteFactory::class); + + return new RoadRunnerApplication( + new WebServerApplication($runner, $pipeline, $router, $middleware, $routeFactory), + $pipeline, + $errorResponseGenerator + ); + } +} diff --git a/src/Container/Config/ConfigProvider.php b/src/Container/Config/ConfigProvider.php new file mode 100644 index 0000000..19f29e7 --- /dev/null +++ b/src/Container/Config/ConfigProvider.php @@ -0,0 +1,23 @@ +> + */ + public function __invoke(): array + { + return [ + 'factories' => [ + Application::class => ApplicationFactory::class, + ] + ]; + } +} diff --git a/src/MiddlewarePipeline.php b/src/MiddlewarePipeline.php new file mode 100644 index 0000000..16dc8b9 --- /dev/null +++ b/src/MiddlewarePipeline.php @@ -0,0 +1,93 @@ + */ + public array $concurrentPipelines; + /** @var array */ + private array $middlewareCollection; + + /** + * @param array $middlewareCollection + * @param array $concurrentPipelines + */ + public function __construct( + array $middlewareCollection = [], + array $concurrentPipelines = [] + ) { + $this->concurrentPipelines = $concurrentPipelines; + $this->middlewareCollection = $middlewareCollection; + } + + public function pipe(MiddlewareInterface $middleware): void + { + $this->middlewareCollection[] = $middleware; + } + + public function handle(ServerRequestInterface $request): ResponseInterface + { + /** @var string $requestId */ + $requestId = $request->getAttribute('request_id'); + $this->setCurrentPipeline($requestId); + + try { + $middleware = $this->concurrentPipelines[$requestId]->dequeue(); + + $response = $middleware->process($request, $this); + unset($this->concurrentPipelines[$requestId]); + + return $response; + } catch (Throwable $exception) { + unset($this->concurrentPipelines[$requestId]); + + throw $exception; + } + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + /** @var ?string $requestId */ + $requestId = $request->getAttribute('request_id'); + if (!$requestId) { + $requestId = Uuid::uuid4()->toString(); + $request = $request->withAttribute('request_id', $requestId); + } + + try { + $queue = $this->concurrentPipelines[$requestId]; + $next = new NextHandler($queue, $handler); + + return $next->handle($request); + } catch (Throwable $exception) { + unset($this->concurrentPipelines[$requestId]); + + throw $exception; + } + } + + private function setCurrentPipeline(string $requestId): void + { + if (empty($this->concurrentPipelines[$requestId])) { + $queue = new SyncMiddlewareQueue(); + foreach ($this->middlewareCollection as $middlewareName) { + $queue->enqueue($middlewareName); + } + $this->concurrentPipelines[$requestId] = $queue; + } + } +} diff --git a/src/RoadRunnerApplication.php b/src/RoadRunnerApplication.php new file mode 100644 index 0000000..64c82f7 --- /dev/null +++ b/src/RoadRunnerApplication.php @@ -0,0 +1,87 @@ +errorResponseGenerator = $errorResponseGenerator; + $this->application = $application; + $this->pipeline = $pipeline; + } + + public function handle(ServerRequestInterface $request): ResponseInterface + { + try { + return $this->pipeline->handle($request->withAttribute('request_id', Uuid::uuid4()->toString())); + } catch (Throwable $exception) { + return $this->errorResponseGenerator->__invoke($exception); + } + } + + public function run(): void + { + throw new RuntimeException('Connot run Road Runner Application out of Road Runner server.'); + } + + public function pipe(string $middlewareName): void + { + $this->application->pipe($middlewareName); + } + + public function get(string $uri, array $middleware, string $name): void + { + $this->application->get($uri, $middleware, $name); + } + + public function post(string $uri, array $middleware, string $name): void + { + $this->application->post($uri, $middleware, $name); + } + + public function patch(string $uri, array $middleware, string $name): void + { + $this->patch($uri, $middleware, $name); + } + + public function put(string $uri, array $middleware, string $name): void + { + $this->application->put($uri, $middleware, $name); + } + + public function delete(string $uri, array $middleware, string $name): void + { + $this->application->delete($uri, $middleware, $name); + } + + public function options(string $uri, array $middleware, string $name): void + { + $this->application->options($uri, $middleware, $name); + } + + public function route(string $uri, array $middleware, array $methods, string $name): void + { + $this->application->route($uri, $middleware, $methods, $name); + } +} diff --git a/test/Container/ApplicationFactoryTest.php b/test/Container/ApplicationFactoryTest.php new file mode 100644 index 0000000..1fd781b --- /dev/null +++ b/test/Container/ApplicationFactoryTest.php @@ -0,0 +1,43 @@ +createMock(ContainerInterface::class); + $container->expects(static::exactly(6)) + ->method('get') + ->withConsecutive( + [EmitterStack::class], + [RequestFactory::class], + [ErrorResponseGenerator::class], + [Router::class], + [MiddlewareFactory::class], + [RouteFactory::class], + ) + ->willReturnOnConsecutiveCalls( + $this->createMock(EmitterStack::class), + $this->createMock(RequestFactory::class), + $this->createMock(ErrorResponseGenerator::class), + $this->createMock(Router::class), + $this->createMock(MiddlewareFactory::class), + $this->createMock(RouteFactory::class), + ); + + + $factory($container); + } +}