Restructure AppFactory creation process.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-03-10 23:58:39 -06:00
parent 6b775e1b1d
commit 74df4fccf5
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
5 changed files with 147 additions and 161 deletions

View File

@ -5,20 +5,10 @@ ini_set('display_errors', 1);
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php';
$app = App\AppFactory::create(
$cli = App\AppFactory::createCli(
$autoloader,
[
App\Environment::BASE_DIR => dirname(__DIR__),
]
);
/** @var \Psr\Container\ContainerInterface|\DI\FactoryInterface $di */
$di = $app->getContainer();
/** @var \App\Locale $locale */
$locale = $di->make(\App\Locale::class);
$locale->register();
/** @var App\Console\Application $cli */
$cli = $di->get(App\Console\Application::class);
$cli->run();

View File

@ -2,12 +2,18 @@
namespace App;
use App\Console\Application;
use App\Http\Factory\ResponseFactory;
use App\Http\Factory\ServerRequestFactory;
use DI;
use DI\Bridge\Slim\ControllerInvoker;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Invoker;
use Invoker\Invoker;
use Invoker\ParameterResolver\AssociativeArrayResolver;
use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
use Invoker\ParameterResolver\DefaultValueResolver;
use Invoker\ParameterResolver\ResolverChain;
use Monolog\Registry;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Log\LoggerInterface;
use Slim\App;
@ -19,14 +25,97 @@ use Slim\Interfaces\RouteResolverInterface;
class AppFactory
{
public static function create($autoloader = null, $appEnvironment = [], $diDefinitions = []): App
public static function createApp($autoloader = null, $appEnvironment = [], $diDefinitions = []): App
{
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions);
return self::buildAppFromContainer($di);
}
public static function createCli($autoloader = null, $appEnvironment = [], $diDefinitions = []): Application
{
$di = self::buildContainer($autoloader, $appEnvironment, $diDefinitions);
self::buildAppFromContainer($di);
$locale = $di->make(Locale::class);
$locale->register();
return $di->get(Application::class);
}
public static function buildAppFromContainer(DI\Container $container): App
{
ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);
ServerRequestCreatorFactory::setServerRequestCreator(new ServerRequestFactory());
$responseFactory = $container->has(ResponseFactoryInterface::class)
? $container->get(ResponseFactoryInterface::class)
: new ResponseFactory();
$callableResolver = $container->has(CallableResolverInterface::class)
? $container->get(CallableResolverInterface::class)
: null;
$routeCollector = $container->has(RouteCollectorInterface::class)
? $container->get(RouteCollectorInterface::class)
: null;
$routeResolver = $container->has(RouteResolverInterface::class)
? $container->get(RouteResolverInterface::class)
: null;
$middlewareDispatcher = $container->has(MiddlewareDispatcherInterface::class)
? $container->get(MiddlewareDispatcherInterface::class)
: null;
$app = new App(
$responseFactory,
$container,
$callableResolver,
$routeCollector,
$routeResolver,
$middlewareDispatcher
);
$container->set(App::class, $app);
$routeCollector = $app->getRouteCollector();
// Use the PHP-DI Bridge's action invocation helper.
$resolvers = [
// Inject parameters by name first
new AssociativeArrayResolver(),
// Then inject services by type-hints for those that weren't resolved
new TypeHintContainerResolver($container),
// Then fall back on parameters default values for optional route parameters
new DefaultValueResolver(),
];
$invoker = new Invoker(new ResolverChain($resolvers), $container);
$controllerInvoker = new ControllerInvoker($invoker);
$routeCollector->setDefaultInvocationStrategy($controllerInvoker);
$environment = $container->get(Environment::class);
if ($environment->isProduction()) {
$routeCollector->setCacheFile($environment->getTempDirectory() . '/app_routes.cache.php');
}
$eventDispatcher = $container->get(EventDispatcher::class);
$eventDispatcher->dispatch(new Event\BuildRoutes($app));
return $app;
}
public static function buildContainer(
$autoloader = null,
$appEnvironment = [],
$diDefinitions = []
): DI\Container {
// Register Annotation autoloader
if (null !== $autoloader) {
AnnotationRegistry::registerLoader([$autoloader, 'loadClass']);
}
$environment = new Environment(self::buildEnvironment($appEnvironment));
$environment = self::buildEnvironment($appEnvironment);
Environment::setInstance($environment);
self::applyPhpSettings($environment);
@ -46,45 +135,53 @@ class AppFactory
$plugins = null;
}
$di = self::buildContainer($environment, $diDefinitions);
$containerBuilder = new DI\ContainerBuilder();
$containerBuilder->useAnnotations(true);
$containerBuilder->useAutowiring(true);
if ($environment->isProduction()) {
$containerBuilder->enableCompilation($environment->getTempDirectory());
}
$containerBuilder->addDefinitions($diDefinitions);
// Check for services.php file and include it if one exists.
$config_dir = $environment->getConfigDirectory();
if (file_exists($config_dir . '/services.php')) {
$containerBuilder->addDefinitions($config_dir . '/services.php');
}
$di = $containerBuilder->build();
$logger = $di->get(LoggerInterface::class);
register_shutdown_function(function (LoggerInterface $logger): void {
$error = error_get_last();
$errno = $error["type"] ?? \E_ERROR;
$errfile = $error["file"] ?? 'unknown';
$errline = $error["line"] ?? 0;
$errstr = $error["message"] ?? 'Shutdown';
register_shutdown_function(
function (LoggerInterface $logger): void {
$error = error_get_last();
$errno = $error["type"] ?? \E_ERROR;
$errfile = $error["file"] ?? 'unknown';
$errline = $error["line"] ?? 0;
$errstr = $error["message"] ?? 'Shutdown';
if ($errno &= \E_PARSE | \E_ERROR | \E_USER_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) {
$logger->critical(sprintf(
'Fatal error: %s in %s on line %d',
$errstr,
$errfile,
$errline
));
}
}, $logger);
if ($errno &= \E_PARSE | \E_ERROR | \E_USER_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) {
$logger->critical(
sprintf(
'Fatal error: %s in %s on line %d',
$errstr,
$errfile,
$errline
)
);
}
},
$logger
);
Registry::addLogger($logger, 'app', true);
ServerRequestCreatorFactory::setSlimHttpDecoratorsAutomaticDetection(false);
ServerRequestCreatorFactory::setServerRequestCreator(new ServerRequestFactory());
$app = self::createFromContainer($di);
$di->set(App::class, $app);
self::updateRouteHandling($app);
self::buildRoutes($app);
return $app;
return $di;
}
/**
* @return mixed[]
*/
protected static function buildEnvironment(array $environment): array
protected static function buildEnvironment(array $environment): Environment
{
if (!isset($environment[Environment::BASE_DIR])) {
throw new Exception\BootstrapException('No base directory specified!');
@ -106,7 +203,7 @@ class AppFactory
$environment = array_merge(array_filter($_ENV), $environment);
return $environment;
return new Environment($environment);
}
protected static function applyPhpSettings(Environment $environment): void
@ -135,102 +232,4 @@ class AppFactory
session_cache_limiter('');
}
protected static function buildContainer(Environment $environment, array $diDefinitions = []): DI\Container
{
$containerBuilder = new DI\ContainerBuilder();
$containerBuilder->useAnnotations(true);
$containerBuilder->useAutowiring(true);
if ($environment->isProduction()) {
$containerBuilder->enableCompilation($environment->getTempDirectory());
}
if (!isset($diDefinitions[Environment::class])) {
$diDefinitions[Environment::class] = $environment;
}
$containerBuilder->addDefinitions($diDefinitions);
// Check for services.php file and include it if one exists.
$config_dir = $environment->getConfigDirectory();
if (file_exists($config_dir . '/services.php')) {
$containerBuilder->addDefinitions($config_dir . '/services.php');
}
return $containerBuilder->build();
}
protected static function createFromContainer(ContainerInterface $container): App
{
$responseFactory = $container->has(ResponseFactoryInterface::class)
? $container->get(ResponseFactoryInterface::class)
: new Http\Factory\ResponseFactory();
$callableResolver = $container->has(CallableResolverInterface::class)
? $container->get(CallableResolverInterface::class)
: null;
$routeCollector = $container->has(RouteCollectorInterface::class)
? $container->get(RouteCollectorInterface::class)
: null;
$routeResolver = $container->has(RouteResolverInterface::class)
? $container->get(RouteResolverInterface::class)
: null;
$middlewareDispatcher = $container->has(MiddlewareDispatcherInterface::class)
? $container->get(MiddlewareDispatcherInterface::class)
: null;
return new App(
$responseFactory,
$container,
$callableResolver,
$routeCollector,
$routeResolver,
$middlewareDispatcher
);
}
/**
* @param App $app
*/
protected static function updateRouteHandling(App $app): void
{
$di = $app->getContainer();
$routeCollector = $app->getRouteCollector();
$environment = $di->get(Environment::class);
// Use the PHP-DI Bridge's action invocation helper.
$resolvers = [
// Inject parameters by name first
new Invoker\ParameterResolver\AssociativeArrayResolver(),
// Then inject services by type-hints for those that weren't resolved
new Invoker\ParameterResolver\Container\TypeHintContainerResolver($di),
// Then fall back on parameters default values for optional route parameters
new Invoker\ParameterResolver\DefaultValueResolver(),
];
$invoker = new Invoker\Invoker(new Invoker\ParameterResolver\ResolverChain($resolvers), $di);
$controllerInvoker = new DI\Bridge\Slim\ControllerInvoker($invoker);
$routeCollector->setDefaultInvocationStrategy($controllerInvoker);
if ($environment->isProduction()) {
$routeCollector->setCacheFile($environment->getTempDirectory() . '/app_routes.cache.php');
}
}
/**
* @param App $app
*/
protected static function buildRoutes(App $app): void
{
$di = $app->getContainer();
$dispatcher = $di->get(EventDispatcher::class);
$dispatcher->dispatch(new Event\BuildRoutes($app));
}
}

View File

@ -33,10 +33,13 @@ class Module extends Framework implements DoctrineProvider
$autoloader = $GLOBALS['autoloader'];
$this->app = $container_class::create($autoloader, [
Environment::BASE_DIR => Configuration::projectDir(),
Environment::APP_ENV => Environment::ENV_TESTING,
]);
$this->app = $container_class::createApp(
$autoloader,
[
Environment::BASE_DIR => Configuration::projectDir(),
Environment::APP_ENV => Environment::ENV_TESTING,
]
);
$this->container = $this->app->getContainer();
$this->em = $this->container->get(EntityManagerInterface::class);

View File

@ -8,18 +8,9 @@ ini_set('display_errors', 1);
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php';
$app = App\AppFactory::create(
App\AppFactory::createCli(
$autoloader,
[
App\Environment::BASE_DIR => dirname(__DIR__),
]
);
$di = $app->getContainer();
/** @var \Psr\Container\ContainerInterface|\DI\FactoryInterface $di */
$di = $app->getContainer();
/** @var \App\Locale $locale */
$locale = $di->make(\App\Locale::class);
$locale->register();

View File

@ -4,8 +4,11 @@ ini_set('display_errors', 1);
$autoloader = require dirname(__DIR__) . '/vendor/autoload.php';
$app = App\AppFactory::create($autoloader, [
App\Environment::BASE_DIR => dirname(__DIR__),
]);
$app = App\AppFactory::createApp(
$autoloader,
[
App\Environment::BASE_DIR => dirname(__DIR__),
]
);
$app->run();