Changes for configuration file approach
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.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace LotGD\Core;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
use LotGD\Core\ComposerManager;
|
||||
|
||||
/**
|
||||
* BootConfiguration
|
||||
* @author sauterb
|
||||
*/
|
||||
class BootConfiguration
|
||||
{
|
||||
/** @var ComposerManager */
|
||||
private $composerManager;
|
||||
/** @var PackageInterface */
|
||||
private $package;
|
||||
/** @var string */
|
||||
private $rootNamespace;
|
||||
/** @var array */
|
||||
private $rawConfig;
|
||||
private $models;
|
||||
private $daenerysCommands;
|
||||
|
||||
public function __construct(ComposerManager $composerManager, PackageInterface $package)
|
||||
{
|
||||
$this->composerManager = $composerManager;
|
||||
$this->package = $package;
|
||||
|
||||
$installationManager = $composerManager->getComposer()->getInstallationManager();
|
||||
$confFile = $installationManager->getInstallPath($package) . "/lotgd.yml";
|
||||
|
||||
$this->rootNamespace = $this->findRootNamespace($package);
|
||||
$this->rawConfig = Yaml::parse(file_get_contents($confFile));
|
||||
|
||||
$this->findEntityDirectory();
|
||||
$this->findDenerysCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a root namespace
|
||||
*
|
||||
* This function searches the package's configuration to find it's root namespace.
|
||||
* For this, it uses the following order:
|
||||
* - look in ["extra"]["lotgd-namespace"]
|
||||
* - check psr-4 autoload configuration. If used, it takes the first element
|
||||
* - check psr-0 autoload configuration. If used, it takes the first element
|
||||
* @param PackageInterface $package
|
||||
* @return string
|
||||
* @throws \Exception if no namespace has been found
|
||||
*/
|
||||
protected function findRootNamespace(PackageInterface $package): string
|
||||
{
|
||||
// if one is defined, we use that.
|
||||
if (isset($package->getExtra()["lotgd-namespace"])) {
|
||||
return $package->getExtra()["lotgd-namespace"];
|
||||
}
|
||||
|
||||
$autoload = $package->getAutoload();
|
||||
if (isset($autoload["psr-4"]) && count($autoload["psr-4"]) > 0) {
|
||||
return $autoload["psr-4"][0];
|
||||
}
|
||||
|
||||
if (isset($autoload["psr-0"]) && count($autoload["psr-0"]) > 0) {
|
||||
return $autoload["psr-0"][0];
|
||||
}
|
||||
|
||||
$name = $package->getName();
|
||||
throw new \Exception("{$name} has no valid namespace.");
|
||||
}
|
||||
|
||||
protected function getSubKeyIfItExists(array $arguments)
|
||||
{
|
||||
$parent = $this->rawConfig;
|
||||
|
||||
foreach ($arguments as $argument){
|
||||
if (isset($parent[$argument])) {
|
||||
$parent = $parent[$argument];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to iterate an array element given by the arguments
|
||||
* @param scalar $argument1,... array keys, by increasing depth
|
||||
*/
|
||||
protected function iterateKey(...$arguments)
|
||||
{
|
||||
$result = $this->getSubKeyIfItExists($arguments);
|
||||
|
||||
if (is_array($result)) {
|
||||
foreach ($result as $key => $val) {
|
||||
yield $key => $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subkey of an array if it exists or null
|
||||
* @param scalar $argument1,... array keys, by increasing depth
|
||||
* @return type
|
||||
*/
|
||||
protected function getConfig(...$arguments)
|
||||
{
|
||||
$result = $this->getSubKeyIfItExists($arguments);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* internal function. Adds models to the boot configuration.
|
||||
*/
|
||||
protected function findEntityDirectory()
|
||||
{
|
||||
$this->entityDirectory = null;
|
||||
|
||||
$entityNamespace = $this->getConfig("bootstrap", "entityNamespace");
|
||||
$entityNamespace = $this->rootNamespace . $entityNamespace;
|
||||
|
||||
if (is_null($entityNamespace) === false) {
|
||||
$entityDirectory = $this->composerManager->getComposer()->translateNamespaceToPath($entityNamespace);
|
||||
|
||||
if (is_dir($entityDirectory) === false) {
|
||||
throw new \Exception("{$entityDirectory}, generated from {$entityNamespace}, is not a valid directory.");
|
||||
}
|
||||
|
||||
$this->entityDirectory = $entityDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any models to add.
|
||||
* @return type
|
||||
*/
|
||||
public function hasEntityDirectory(): bool
|
||||
{
|
||||
return $this->entityDirectory === null ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of fqcn for all models added by packages.
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getEntityDirectory(): string
|
||||
{
|
||||
return $this->entityDirectory;
|
||||
}
|
||||
|
||||
protected function findDenerysCommands()
|
||||
{
|
||||
$list = $this->iterateKey("bootstrap", "daenerysCommands");
|
||||
$this->daenerysCommands = [];
|
||||
|
||||
if (is_array($list) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($list as $command) {
|
||||
$this->daenerysCommands = $this->rootNamespace . $command;
|
||||
}
|
||||
}
|
||||
|
||||
public function addDaenerysCommands(Game $game, Application $application)
|
||||
{
|
||||
foreach ($this->daenerysCommands as $command) {
|
||||
$application->addCommands(new $command($game));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace LotGD\Core;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
use LotGD\Core\ComposerManager;
|
||||
|
||||
class BootConfigurationManager
|
||||
{
|
||||
private $cwd;
|
||||
/** @var array<BootConfiguration> */
|
||||
private $configurations = null;
|
||||
|
||||
public function __construct(ComposerManager $composerManager, string $cwd)
|
||||
{
|
||||
$this->cwd = $cwd;
|
||||
|
||||
$packages = $composerManager->getPackages();
|
||||
$this->configurations = [];
|
||||
|
||||
foreach($packages as $package)
|
||||
{
|
||||
if ($package->getType() === "lotgd-crate" || $package->getType() === "lotgd-module") {
|
||||
$this->configurations[] = new BootConfiguration($composerManager, $package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all entity directories from lotgd packages
|
||||
* @return array
|
||||
*/
|
||||
public function getEntityDirectories(): array
|
||||
{
|
||||
$entityDirectories = [];
|
||||
|
||||
foreach ($this->configurations as $config) {
|
||||
if ($config->hasEntityDirectory()) {
|
||||
$entityDirectories[] = $config->getEntityDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
return $entityDirectories;
|
||||
}
|
||||
|
||||
public function addDaenerysCommands(Game $game, Application $application)
|
||||
{
|
||||
foreach ($this->configurations as $config) {
|
||||
if ($config->hasDaenerysCommands()) {
|
||||
$this->addDaenerysCommands($game, $application);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-39
@@ -25,8 +25,9 @@ use LotGD\Core\ {
|
||||
|
||||
class Bootstrap
|
||||
{
|
||||
private $rootDir;
|
||||
private $game;
|
||||
private $bootstrapClasses = [];
|
||||
private $bootConfigurationManager = [];
|
||||
private $annotationDirectories = [];
|
||||
|
||||
/**
|
||||
@@ -43,10 +44,12 @@ class Bootstrap
|
||||
* Starts the game kernel with the most important classes and returns the object
|
||||
* @return Game
|
||||
*/
|
||||
public function getGame(): Game
|
||||
public function getGame(string $rootDir = null): Game
|
||||
{
|
||||
$this->rootDir = $rootDir ?? getcwd();
|
||||
|
||||
$composer = $this->createComposerManager();
|
||||
$this->bootstrapClasses = $this->initPackageBootstraps($composer);
|
||||
$this->bootConfigurationManager = $this->createBootConfigurationManager($composer, $this->rootDir);
|
||||
|
||||
$config = $this->createConfiguration();
|
||||
$logger = $this->createLogger($config, "lotgd");
|
||||
@@ -61,28 +64,11 @@ class Bootstrap
|
||||
return $this->game;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public function createBootConfigurationManager(
|
||||
ComposerManager $composerManager,
|
||||
string $cwd
|
||||
): BootConfigurationManager {
|
||||
return new BootConfigurationManager($composerManager, $cwd);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,8 +88,7 @@ class Bootstrap
|
||||
*/
|
||||
protected function createComposerManager(): ComposerManager
|
||||
{
|
||||
$composer = new ComposerManager();
|
||||
|
||||
$composer = new ComposerManager();
|
||||
return $composer;
|
||||
}
|
||||
|
||||
@@ -151,12 +136,13 @@ class Bootstrap
|
||||
*/
|
||||
protected function createConfiguration(): Configuration
|
||||
{
|
||||
$configFilePath = getenv('LOTGD_CONFIG');
|
||||
$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);
|
||||
$config = new Configuration($configFilePath, $this->rootDir);
|
||||
return $config;
|
||||
}
|
||||
|
||||
@@ -186,6 +172,30 @@ class Bootstrap
|
||||
{
|
||||
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.
|
||||
@@ -198,13 +208,9 @@ class Bootstrap
|
||||
$directories = [__DIR__ . '/Models'];
|
||||
|
||||
// Get additional annotation directories from bootstrap classes
|
||||
foreach ($this->bootstrapClasses as $bootstrap) {
|
||||
if ($bootstrap->hasEntityPath()) {
|
||||
$directories[] = $bootstrap->getEntityPath();
|
||||
}
|
||||
}
|
||||
$packageDirectories = $this->bootConfigurationManager->getEntityDirectories();
|
||||
|
||||
return $directories;
|
||||
return array_merge($directories, $packageDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,9 +228,6 @@ class Bootstrap
|
||||
*/
|
||||
public function addDaenerysCommands(Application $application)
|
||||
{
|
||||
foreach ($this->bootstrapClasses as $bootstrap)
|
||||
{
|
||||
$bootstrap->addDaenerysCommand($this->game, $application);
|
||||
}
|
||||
$this->bootConfigurationManager->addDaenerysCommands($this->game, $application);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LotGD\Core;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
interface BootstrapInterface
|
||||
{
|
||||
public function hasEntityPath(): bool;
|
||||
public function getEntityPath(): string;
|
||||
public function addDaenerysCommand(Game $game, Application $application);
|
||||
}
|
||||
+60
-3
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
namespace LotGD\Core\Tests;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Installer\InstallationManager;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\NullHandler;
|
||||
@@ -15,7 +17,7 @@ use LotGD\Core\Tests\FakeModule\Models\UserEntity;
|
||||
class BootstrapTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $logger;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->logger = new Logger('test');
|
||||
@@ -48,8 +50,63 @@ class BootstrapTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertStringEndsNotWith("/tests", getcwd());
|
||||
$this->assertStringStartsNotWith(".././", getenv("LOTGD_CONFIG"));
|
||||
}
|
||||
|
||||
public function testBootstrapLoadsPackageModels()
|
||||
{
|
||||
$installationManager = $this->getMockBuilder(InstallationManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(["getInstallPath"])
|
||||
->getMock();
|
||||
$installationManager->method("getInstallPath")->willReturn(__DIR__ . "/FakeModule");
|
||||
|
||||
$composer = $this->getMockBuilder(\Composer\Composer::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(["getInstallationManager", "translateNamespaceToPath"])
|
||||
->getMock();
|
||||
$composer->method("getInstallationManager")->willReturn($installationManager);
|
||||
$composer
|
||||
->expects($this->exactly(1))
|
||||
->method("translateNamespaceToPath")
|
||||
->with("LotGD\\Core\\Tests\\FakeModule\\Models")
|
||||
->willReturn(__DIR__ . "/FakeModule/Models");
|
||||
|
||||
$fakeModulePackage = $this->getMockBuilder(AliasPackage::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(["getType", "getExtra"])
|
||||
->getMock();
|
||||
$fakeModulePackage->method("getType")->willReturn("lotgd-module");
|
||||
$fakeModulePackage->method("getExtra")->willReturn(["lotgd-namespace" => "LotGD\\Core\\Tests\\FakeModule\\"]);
|
||||
|
||||
$composerManager = $this->getMockBuilder(ComposerManager::class)
|
||||
->setMethods(["getPackages", "getComposer"])
|
||||
->getMock();
|
||||
$composerManager->method("getPackages")->willReturn([$fakeModulePackage]);
|
||||
$composerManager->method("getComposer")->willReturn($composer);
|
||||
|
||||
$bootstrap = $this->getMockBuilder(Bootstrap::class)
|
||||
->setMethods(["createComposerManager"])
|
||||
->getMock();
|
||||
$bootstrap->method("createComposerManager")->willReturn($composerManager);
|
||||
|
||||
// run tests
|
||||
$game = $bootstrap->getGame();
|
||||
|
||||
$this->assertGreaterThanorEqual(3, $bootstrap->getReadAnnotationDirectories());
|
||||
|
||||
/*$user = new UserEntity();
|
||||
$user->setName("Monthy");
|
||||
$game->getEntityManager()->persist($user);
|
||||
$game->getEntityManager()->flush();
|
||||
$id = $user->getId();
|
||||
$this->assertInternalType("int", $id);
|
||||
$game->getEntityManager()->clear();
|
||||
$user = $game->getEntityManager()->getRepository(UserEntity::class)->find($id);
|
||||
$this->assertInternalType("int", $user->getId());
|
||||
$this->assertInternalType("string", $user->getName());
|
||||
$this->assertSame("Monthy", $user->getName());*/
|
||||
}
|
||||
|
||||
public function testGenerateAnnotationDirectories()
|
||||
/*public function testGenerateAnnotationDirectories()
|
||||
{
|
||||
$composerManager = $this->getMockBuilder(ComposerManager::class)
|
||||
->disableOriginalConstructor()
|
||||
@@ -83,5 +140,5 @@ class BootstrapTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertInternalType("int", $user->getId());
|
||||
$this->assertInternalType("string", $user->getName());
|
||||
$this->assertSame("Monthy", $user->getName());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
bootstrap:
|
||||
entityNamespace: "Models"
|
||||
Reference in New Issue
Block a user