Update AppFactory and HttpFactory to support long-running services like Roadrunner.

This commit is contained in:
Buster Neece 2023-12-22 16:21:10 -06:00
parent 8b15a55bad
commit 4e669ab20f
No known key found for this signature in database
7 changed files with 142 additions and 104 deletions

View File

@ -6,8 +6,7 @@ namespace App;
use App\Console\Application;
use App\Enums\SupportedLocales;
use App\Http\Factory\ResponseFactory;
use App\Http\Factory\ServerRequestFactory;
use App\Http\HttpFactory;
use App\Utilities\Logger as AppLogger;
use DI;
use Monolog\ErrorHandler;
@ -21,34 +20,37 @@ use Slim\Handlers\Strategies\RequestResponse;
final class AppFactory
{
public static function createApp(
array $appEnvironment = [],
array $diDefinitions = []
array $appEnvironment = []
): App {
$di = self::buildContainer($appEnvironment, $diDefinitions);
$environment = self::buildEnvironment($appEnvironment);
$diBuilder = self::createContainerBuilder($environment);
$di = self::buildContainer($diBuilder);
return self::buildAppFromContainer($di);
}
public static function createCli(
array $appEnvironment = [],
array $diDefinitions = []
array $appEnvironment = []
): Application {
$di = self::buildContainer($appEnvironment, $diDefinitions);
self::buildAppFromContainer($di);
$environment = self::buildEnvironment($appEnvironment);
$diBuilder = self::createContainerBuilder($environment);
$di = self::buildContainer($diBuilder);
$env = $di->get(Environment::class);
SupportedLocales::createForCli($env);
SupportedLocales::createForCli($environment);
return $di->get(Application::class);
}
public static function buildAppFromContainer(DI\Container $container): App
{
public static function buildAppFromContainer(
DI\Container $container,
?HttpFactory $httpFactory = null
): App {
$httpFactory ??= new HttpFactory();
ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);
ServerRequestCreatorFactory::setServerRequestCreator(new ServerRequestFactory());
ServerRequestCreatorFactory::setServerRequestCreator($httpFactory);
$app = new App(
responseFactory: new ResponseFactory(),
responseFactory: $httpFactory,
container: $container,
);
$container->set(App::class, $app);
@ -67,18 +69,19 @@ final class AppFactory
return $app;
}
public static function buildContainer(
array $appEnvironment = [],
array $diDefinitions = []
): DI\Container {
$environment = self::buildEnvironment($appEnvironment);
/**
* @return DI\ContainerBuilder<DI\Container>
*/
public static function createContainerBuilder(
Environment $environment
): DI\ContainerBuilder {
$diDefinitions = [
Environment::class => $environment,
];
Environment::setInstance($environment);
self::applyPhpSettings($environment);
// Override DI definitions for settings.
$diDefinitions[Environment::class] = $environment;
$plugins = new Plugins($environment->getBaseDirectory() . '/plugins');
$diDefinitions[Plugins::class] = $plugins;
@ -96,6 +99,16 @@ final class AppFactory
$containerBuilder->addDefinitions(dirname(__DIR__) . '/config/services.php');
return $containerBuilder;
}
/**
* @param DI\ContainerBuilder<DI\Container> $containerBuilder
* @return DI\Container
*/
public static function buildContainer(
DI\ContainerBuilder $containerBuilder
): DI\Container {
$di = $containerBuilder->build();
// Monolog setup
@ -109,14 +122,17 @@ final class AppFactory
}
/**
* @param array<string, mixed> $environment
* @param array<string, mixed> $rawEnvironment
*/
public static function buildEnvironment(array $environment = []): Environment
public static function buildEnvironment(array $rawEnvironment = []): Environment
{
$_ENV = getenv();
$environment = array_merge(array_filter($_ENV), $environment);
$rawEnvironment = array_merge(array_filter($_ENV), $rawEnvironment);
$environment = new Environment($rawEnvironment);
return new Environment($environment);
self::applyPhpSettings($environment);
return $environment;
}
private static function applyPhpSettings(Environment $environment): void

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Http\Factory;
use App\Http\Response;
use GuzzleHttp\Psr7\HttpFactory;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Factory\DecoratedResponseFactory;
final class ResponseFactory extends DecoratedResponseFactory
{
public function __construct()
{
$httpFactory = new HttpFactory();
parent::__construct($httpFactory, $httpFactory);
}
/**
* Create a new response.
*
* @param int $code HTTP status code; defaults to 200
* @param string $reasonPhrase Reason phrase to associate with status code
* in generated response; if none is provided implementations MAY use
* the defaults as suggested in the HTTP specification.
*/
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
{
$response = $this->responseFactory->createResponse($code, $reasonPhrase);
return new Response($response, $this->streamFactory);
}
}

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Http\Factory;
use App\Http\ServerRequest;
use GuzzleHttp\Psr7\HttpFactory;
use GuzzleHttp\Psr7\ServerRequest as GuzzleServerRequest;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Interfaces\ServerRequestCreatorInterface;
final class ServerRequestFactory implements ServerRequestFactoryInterface, ServerRequestCreatorInterface
{
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
{
$serverRequest = (new HttpFactory())->createServerRequest($method, $uri, $serverParams);
return $this->decorateServerRequest($serverRequest);
}
/**
* @param ServerRequestInterface $request
*/
public function decorateServerRequest(ServerRequestInterface $request): ServerRequestInterface
{
return new ServerRequest($request);
}
public function createServerRequestFromGlobals(): ServerRequestInterface
{
return $this->decorateServerRequest(GuzzleServerRequest::fromGlobals());
}
}

90
src/Http/HttpFactory.php Normal file
View File

@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Http;
use GuzzleHttp\Psr7\HttpFactory as GuzzleHttpFactory;
use GuzzleHttp\Psr7\ServerRequest as GuzzleServerRequest;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use Slim\Interfaces\ServerRequestCreatorInterface;
final class HttpFactory implements
RequestFactoryInterface,
ResponseFactoryInterface,
ServerRequestFactoryInterface,
StreamFactoryInterface,
UploadedFileFactoryInterface,
UriFactoryInterface,
ServerRequestCreatorInterface
{
private readonly GuzzleHttpFactory $httpFactory;
public function __construct()
{
$this->httpFactory = new GuzzleHttpFactory();
}
public function createUploadedFile(...$args): UploadedFileInterface
{
return $this->httpFactory->createUploadedFile(...$args);
}
public function createStream(string $content = ''): StreamInterface
{
return $this->httpFactory->createStream($content);
}
public function createStreamFromFile(...$args): StreamInterface
{
return $this->httpFactory->createStreamFromFile(...$args);
}
public function createStreamFromResource($resource): StreamInterface
{
return $this->httpFactory->createStreamFromResource($resource);
}
public function createServerRequest(...$args): ServerRequestInterface
{
$serverRequest = $this->httpFactory->createServerRequest(...$args);
return $this->decorateServerRequest($serverRequest);
}
public function createServerRequestFromGlobals(): ServerRequestInterface
{
return $this->decorateServerRequest(GuzzleServerRequest::fromGlobals());
}
private function decorateServerRequest(ServerRequestInterface $request): ServerRequestInterface
{
return new ServerRequest($request);
}
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
{
$response = $this->httpFactory->createResponse($code, $reasonPhrase);
return new Response($response, $this->httpFactory);
}
public function createRequest(string $method, $uri): RequestInterface
{
return $this->httpFactory->createRequest($method, $uri);
}
public function createUri(string $uri = ''): UriInterface
{
return $this->httpFactory->createUri($uri);
}
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Tests;
use App\Http\Factory\ServerRequestFactory;
use App\Http\HttpFactory;
use Codeception\Lib\Connector\Shared\PhpSuperGlobalsConverter;
use Slim\App;
use Symfony\Component\BrowserKit\AbstractBrowser;
@ -53,7 +53,7 @@ class Connector extends AbstractBrowser
$_SERVER['REQUEST_METHOD'] = strtoupper($request->getMethod());
$_SERVER['REQUEST_URI'] = $uri;
$request = (new ServerRequestFactory())->createServerRequestFromGlobals();
$request = (new HttpFactory())->createServerRequestFromGlobals();
$slimResponse = $this->app->handle($request);

View File

@ -10,9 +10,10 @@ require dirname(__DIR__) . '/vendor/autoload.php';
$tempDir = sys_get_temp_dir();
$ci = App\AppFactory::buildContainer([
$app = App\AppFactory::createApp([
App\Environment::TEMP_DIR => $tempDir,
App\Environment::UPLOADS_DIR => $tempDir,
]);
$di = $app->getContainer();
return $ci->get(Doctrine\ORM\EntityManagerInterface::class);
return $di->get(Doctrine\ORM\EntityManagerInterface::class);

View File

@ -8,14 +8,12 @@ use Psr\Log\LogLevel;
require dirname(__DIR__) . '/vendor/autoload.php';
$di = App\AppFactory::buildContainer(
$app = App\AppFactory::createApp(
[
App\Environment::BASE_DIR => dirname(__DIR__),
App\Environment::LOG_LEVEL => LogLevel::DEBUG,
]
);
$app = App\AppFactory::buildAppFromContainer($di);
$di = $app->getContainer();
$env = $di->get(Environment::class);
App\Enums\SupportedLocales::createForCli($env);