712a89bdad
Packages that are either of type lotgd-crate or lotgd-module can add daenerys commands and doctrine entity directories by using a lotgd.yml configuration file in their directory root (the same one were composer.json is). All namespaces given in lotgd.yml have to be relative to the packages namespace, without a leading backslash (\). Root namespace is derived from composer.json, either explicitely (via extra.lotgd-namespace) or implicitely via the first psr-4 or the first psr-0 namespace.
234 lines
7.2 KiB
PHP
234 lines
7.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace LotGD\Core;
|
|
|
|
use Doctrine\ORM\ {
|
|
EntityManager,
|
|
EntityManagerInterface,
|
|
Mapping\AnsiQuoteStrategy,
|
|
Tools\Setup,
|
|
Tools\SchemaTool
|
|
};
|
|
use Monolog\ {
|
|
Logger,
|
|
Handler\RotatingFileHandler
|
|
};
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\Console\Application;
|
|
|
|
use LotGD\Core\ {
|
|
ComposerManager,
|
|
BootstrapInterface,
|
|
Exceptions\InvalidConfigurationException
|
|
};
|
|
|
|
class Bootstrap
|
|
{
|
|
private $rootDir;
|
|
private $game;
|
|
private $bootConfigurationManager = [];
|
|
private $annotationDirectories = [];
|
|
|
|
/**
|
|
* Create a new Game object, with all the necessary configuration.
|
|
* @return Game The newly created Game object.
|
|
*/
|
|
public static function createGame(): Game
|
|
{
|
|
$game = new self();
|
|
return $game->getGame();
|
|
}
|
|
|
|
/**
|
|
* Starts the game kernel with the most important classes and returns the object
|
|
* @return Game
|
|
*/
|
|
public function getGame(string $rootDir = null): Game
|
|
{
|
|
$this->rootDir = $rootDir ?? getcwd();
|
|
|
|
$composer = $this->createComposerManager();
|
|
$this->bootConfigurationManager = $this->createBootConfigurationManager($composer, $this->rootDir);
|
|
|
|
$config = $this->createConfiguration();
|
|
$logger = $this->createLogger($config, "lotgd");
|
|
|
|
$pdo = $this->connectToDatabase($config);
|
|
$entityManager = $this->createEntityManager($pdo);
|
|
|
|
$eventManager = $this->createEventManager($entityManager);
|
|
|
|
$this->game = new Game($config, $logger, $entityManager, $eventManager);
|
|
|
|
return $this->game;
|
|
}
|
|
|
|
public function createBootConfigurationManager(
|
|
ComposerManager $composerManager,
|
|
string $cwd
|
|
): BootConfigurationManager {
|
|
return new BootConfigurationManager($composerManager, $cwd);
|
|
}
|
|
|
|
/**
|
|
* Connects to a database using pdo
|
|
* @param \LotGD\Core\Configuration $config
|
|
* @return \PDO
|
|
*/
|
|
protected function connectToDatabase(Configuration $config): \PDO
|
|
{
|
|
return new \PDO($config->getDatabaseDSN(), $config->getDatabaseUser(), $config->getDatabasePassword());
|
|
}
|
|
|
|
/**
|
|
* Creates and returns an instance of ComposerManager
|
|
* @param Logger $logger
|
|
* @return ComposerManager
|
|
*/
|
|
protected function createComposerManager(): ComposerManager
|
|
{
|
|
$composer = new ComposerManager();
|
|
return $composer;
|
|
}
|
|
|
|
/**
|
|
* Returns all bootstrap classes
|
|
* @param ComposerManager $composer
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
protected function initPackageBootstraps(ComposerManager $composer): array
|
|
{
|
|
$packages = $composer->getPackages();
|
|
$classes = [];
|
|
|
|
foreach ($packages as $package) {
|
|
if (isset($package->getExtra()["lotgd-namespace"]) === false) {
|
|
continue;
|
|
}
|
|
|
|
$cn = $package->getExtra()["lotgd-namespace"] . "Bootstrap";
|
|
|
|
// silently ignore that class does not exist, could be one that doesn't need to bootstrap
|
|
if (class_exists($cn, true) === false) {
|
|
continue;
|
|
}
|
|
|
|
$cl = new $cn();
|
|
|
|
if ($cl instanceof BootstrapInterface) {
|
|
$classes[] = $cl;
|
|
}
|
|
else {
|
|
$name = $package->getName() . "@" . $package->getVersion();
|
|
throw new \Exception("Package {$name} does not implement BootstrapInterface in it's Bootstrap class");
|
|
}
|
|
}
|
|
|
|
return $classes;
|
|
}
|
|
|
|
/**
|
|
* Returns a configuration object reading from the file located at the path stored in LOTGD_CONFIG.
|
|
* @return \LotGD\Core\Configuration
|
|
* @throws InvalidConfigurationException
|
|
*/
|
|
protected function createConfiguration(): Configuration
|
|
{
|
|
$configFilePath = getenv('LOTGD_CONFIG') ?? "/config/lotgd.yml";
|
|
|
|
if ($configFilePath === false || strlen($configFilePath) == 0 || is_file($configFilePath) === false) {
|
|
throw new InvalidConfigurationException("Invalid or missing configuration file: {$configFilePath}.");
|
|
}
|
|
|
|
$config = new Configuration($configFilePath, $this->rootDir);
|
|
return $config;
|
|
}
|
|
|
|
/**
|
|
* Returns a logger instance
|
|
* @param type $name
|
|
* @return LoggerInterface
|
|
*/
|
|
protected function createLogger(Configuration $config, string $name): LoggerInterface
|
|
{
|
|
$logger = new Logger($name);
|
|
// Add lotgd as the prefix for the log filenames.
|
|
$logger->pushHandler(new RotatingFileHandler($config->getLogPath() . DIRECTORY_SEPARATOR . $name, 14));
|
|
|
|
$v = Game::getVersion();
|
|
$logger->info("Bootstrap constructing game (Daenerys 🐲{$v}).");
|
|
|
|
return $logger;
|
|
}
|
|
|
|
/**
|
|
* Creates and returns an instance of the EventManager
|
|
* @param EntityManagerInterface $entityManager
|
|
* @return \LotGD\Core\EventManager
|
|
*/
|
|
protected function createEventManager(EntityManagerInterface $entityManager): EventManager
|
|
{
|
|
return new EventManager($entityManager);
|
|
}
|
|
|
|
/**
|
|
* Creates the EntityManager using the pdo connection given in it's argument
|
|
* @param \PDO $pdo
|
|
* @return EntityManagerInterface
|
|
*/
|
|
protected function createEntityManager(\PDO $pdo): EntityManagerInterface
|
|
{
|
|
$this->annotationDirectories = $this->generateAnnotationDirectories();
|
|
$configuration = Setup::createAnnotationMetadataConfiguration($this->annotationDirectories, true);
|
|
|
|
// Set a quote
|
|
$configuration->setQuoteStrategy(new AnsiQuoteStrategy());
|
|
|
|
// Create entity manager
|
|
$entityManager = EntityManager::create(["pdo" => $pdo], $configuration);
|
|
|
|
// Create Schema and update database if needed
|
|
$metaData = $entityManager->getMetadataFactory()->getAllMetadata();
|
|
$schemaTool = new SchemaTool($entityManager);
|
|
$schemaTool->updateSchema($metaData);
|
|
|
|
return $entityManager;
|
|
}
|
|
|
|
/**
|
|
* Is used to get all directories used to generate annotations.
|
|
* @param array $bootstrapClasses
|
|
* @return array
|
|
*/
|
|
protected function generateAnnotationDirectories(): array
|
|
{
|
|
// Read db annotations from our own model files.
|
|
$directories = [__DIR__ . '/Models'];
|
|
|
|
// Get additional annotation directories from bootstrap classes
|
|
$packageDirectories = $this->bootConfigurationManager->getEntityDirectories();
|
|
|
|
return array_merge($directories, $packageDirectories);
|
|
}
|
|
|
|
/**
|
|
* Return all directories used for reading annotations.
|
|
* @return array<string>
|
|
*/
|
|
public function getReadAnnotationDirectories(): array
|
|
{
|
|
return $this->annotationDirectories;
|
|
}
|
|
|
|
/**
|
|
* Adds console commands to a given console application from bootstrapping packages.
|
|
* @param Application $application
|
|
*/
|
|
public function addDaenerysCommands(Application $application)
|
|
{
|
|
$this->bootConfigurationManager->addDaenerysCommands($this->game, $application);
|
|
}
|
|
}
|