Skip to content

create psr factories #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/Container/Config/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
namespace Antidot\React\Container\Config;

use Antidot\Application\Http\Application;
use Antidot\React\LoopFactory;
use Antidot\React\ReactApplicationFactory;
use Antidot\React\ServerFactory;
use Antidot\React\SocketFactory;
use React\EventLoop\LoopInterface;
use React\Http\Server;
use React\Socket\Server as Socket;

class ConfigProvider
{
Expand All @@ -13,8 +19,12 @@ public function __invoke(): array
'dependencies' => [
'factories' => [
Application::class => ReactApplicationFactory::class,
LoopInterface::class => LoopFactory::class,
Server::class => ServerFactory::class,
Socket::class => SocketFactory::class,
],
]
],
'server' => []
];
}
}
17 changes: 17 additions & 0 deletions src/LoopFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Antidot\React;

use Psr\Container\ContainerInterface;
use React\EventLoop\Factory;
use React\EventLoop\LoopInterface;

class LoopFactory
{
public function __invoke(ContainerInterface $container): LoopInterface
{
return Factory::create();
}
}
45 changes: 42 additions & 3 deletions src/ReactApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
namespace Antidot\React;

use Antidot\Application\Http\Application;
use Antidot\Application\Http\Response\ErrorResponseGenerator;
use Antidot\Application\Http\RouteFactory;
use Antidot\Application\Http\Router;
use Antidot\Container\MiddlewareFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Ramsey\Uuid\Uuid;
use React\Promise\PromiseInterface;
use RuntimeException;
use Throwable;
use function React\Promise\resolve;

class ReactApplication implements Application, RequestHandlerInterface, MiddlewareInterface
Expand All @@ -21,17 +25,20 @@ class ReactApplication implements Application, RequestHandlerInterface, Middlewa
private Router $router;
private MiddlewareFactory $middlewareFactory;
private RouteFactory $routeFactory;
private ErrorResponseGenerator $errorResponseGenerator;

public function __construct(
MiddlewarePipeline $pipeline,
Router $router,
MiddlewareFactory $middlewareFactory,
RouteFactory $routeFactory
RouteFactory $routeFactory,
ErrorResponseGenerator $errorResponseGenerator
) {
$this->routeFactory = $routeFactory;
$this->middlewareFactory = $middlewareFactory;
$this->router = $router;
$this->pipeline = $pipeline;
$this->errorResponseGenerator = $errorResponseGenerator;
}

public function pipe(string $middlewareName): void
Expand Down Expand Up @@ -80,15 +87,47 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
{
return new PromiseResponse(resolve($request)
->then(
fn(ServerRequestInterface $request): ResponseInterface => $this->pipeline->process($request, $handler)
function (ServerRequestInterface $request) use ($handler): PromiseInterface {
$response = new PromiseResponse(
resolve($request)
->then(static function (ServerRequestInterface $request): ServerRequestInterface {
return $request->withAttribute('request_id', Uuid::uuid4()->toString());
})
->then(function (ServerRequestInterface $request) use ($handler): ResponseInterface {
try {
return $this->pipeline->process($request, $handler);
} catch (Throwable $exception) {
return $this->errorResponseGenerator->__invoke($exception);
}
})
);

return resolve($response);
}
));
}

public function handle(ServerRequestInterface $request): ResponseInterface
{
return new PromiseResponse(resolve($request)
->then(
fn (ServerRequestInterface $request): ResponseInterface => $this->pipeline->handle($request)
function (ServerRequestInterface $request): PromiseInterface {
$response = new PromiseResponse(
resolve($request)
->then(static function (ServerRequestInterface $request): ServerRequestInterface {
return $request->withAttribute('request_id', Uuid::uuid4()->toString());
})
->then(function (ServerRequestInterface $request): ResponseInterface {
try {
return $this->pipeline->handle($request);
} catch (Throwable $exception) {
return $this->errorResponseGenerator->__invoke($exception);
}
})
);

return resolve($response);
}
));
}

Expand Down
6 changes: 5 additions & 1 deletion src/ReactApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Antidot\React;

use Antidot\Application\Http\Response\ErrorResponseGenerator;
use Antidot\Application\Http\RouteFactory;
use Antidot\Application\Http\Router;
use Antidot\Container\MiddlewareFactory;
Expand All @@ -19,12 +20,15 @@ public function __invoke(ContainerInterface $container): ReactApplication
$middlewareFactory = $container->get(MiddlewareFactory::class);
/** @var RouteFactory $routeFactory */
$routeFactory = $container->get(RouteFactory::class);
/** @var ErrorResponseGenerator $errorResponseGenerator */
$errorResponseGenerator = $container->get(ErrorResponseGenerator::class);

return new ReactApplication(
new MiddlewarePipeline(),
$router,
$middlewareFactory,
$routeFactory
$routeFactory,
$errorResponseGenerator
);
}
}
42 changes: 42 additions & 0 deletions src/ServerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Antidot\React;

use Antidot\Application\Http\Application;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\LoopInterface;
use React\Http\Server;
use React\Http\Middleware\LimitConcurrentRequestsMiddleware;
use React\Http\Middleware\RequestBodyBufferMiddleware;
use React\Http\Middleware\RequestBodyParserMiddleware;
use React\Http\Middleware\StreamingRequestMiddleware;

class ServerFactory
{
public function __invoke(ContainerInterface $container): Server
{
$application = $container->get(Application::class);
assert($application instanceof ReactApplication);
/** @var LoopInterface $loop */
$loop = $container->get(LoopInterface::class);
/** @var array<string, array> $globalConfig */
$globalConfig = $container->get('config');
/** @var array<string, int|null> $config */
$config = $globalConfig['server'];

$server = new Server(
$loop,
new StreamingRequestMiddleware(),
new LimitConcurrentRequestsMiddleware(($config['max_concurrency']) ?? 100),
new RequestBodyBufferMiddleware($config['buffer_size'] ?? 4 * 1024 * 1024), // 4 MiB
new RequestBodyParserMiddleware(),
static fn (ServerRequestInterface $request): ResponseInterface => $application->handle($request)
);

return $server;
}
}
28 changes: 28 additions & 0 deletions src/SocketFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Antidot\React;

use Psr\Container\ContainerInterface;
use React\EventLoop\LoopInterface;
use React\Socket\Server as Socket;

class SocketFactory
{
public function __invoke(ContainerInterface $container): Socket
{
/** @var LoopInterface $loop */
$loop = $container->get(LoopInterface::class);
/** @var array<string, array> $globalConfig */
$globalConfig = $container->get('config');
/** @var array<string, string|null> $config */
$config = $globalConfig['server'];

return new Socket(sprintf(
'%s:%s',
$config['host'] ?? '0.0.0.0',
$config['port'] ?? '8080'
), $loop);
}
}
20 changes: 18 additions & 2 deletions test/Container/Config/ConfigProviderTest.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
<?php

namespace AntidotTest\Tactician\Container\Config;
namespace AntidotTest\React\Container\Config;

use Antidot\Application\Http\Application;
use Antidot\React\Container\Config\ConfigProvider;
use Antidot\React\LoopFactory;
use Antidot\React\ReactApplicationFactory;
use Antidot\React\ServerFactory;
use Antidot\React\SocketFactory;
use PHPUnit\Framework\TestCase;
use React\EventLoop\LoopInterface;
use React\Http\Server;
use React\Socket\Server as Socket;

class ConfigProviderTest extends TestCase
{
Expand All @@ -14,7 +20,17 @@ public function testItShouldReturnTheConfigArray(): void
$configProvider = new ConfigProvider();
$this->assertIsArray($configProvider());
$this->assertSame(
['dependencies' => ['factories' => [Application::class => ReactApplicationFactory::class]]],
[
'dependencies' => [
'factories' => [
Application::class => ReactApplicationFactory::class,
LoopInterface::class => LoopFactory::class,
Server::class => ServerFactory::class,
Socket::class => SocketFactory::class,
]
],
'server' => []
],
$configProvider(),
);
}
Expand Down
20 changes: 20 additions & 0 deletions test/LoopFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace AntidotTest\React;

use Antidot\React\LoopFactory;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use React\EventLoop\StreamSelectLoop;

class LoopFactoryTest extends TestCase
{
public function testItShouldCreateInstancesOfLoopInterface(): void
{
$factory = new LoopFactory();
$loop = $factory($this->createMock(ContainerInterface::class));
$this->assertInstanceOf(StreamSelectLoop::class, $loop);
}
}
7 changes: 5 additions & 2 deletions test/ReactApplicationFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace AntidotTest\React;

use Antidot\Application\Http\Response\ErrorResponseGenerator;
use Antidot\Application\Http\Router;
use Antidot\Application\Http\RouteFactory;
use Antidot\Container\MiddlewareFactory;
Expand All @@ -16,17 +17,19 @@ class ReactApplicationFactoryTest extends TestCase
public function testItShouldCreateInstancesOfReactApplication(): void
{
$container = $this->createMock(ContainerInterface::class);
$container->expects($this->exactly(3))
$container->expects($this->exactly(4))
->method('get')
->withConsecutive(
[Router::class],
[MiddlewareFactory::class],
[RouteFactory::class],
[ErrorResponseGenerator::class],
)
->willReturnOnConsecutiveCalls(
$this->createMock(Router::class),
$this->createMock(MiddlewareFactory::class),
$this->createMock(RouteFactory::class)
$this->createMock(RouteFactory::class),
$this->createMock(ErrorResponseGenerator::class)
);

$factory = new ReactApplicationFactory();
Expand Down
Loading