You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
marx/src/Instance.php

239 lines
6.2 KiB
PHP

<?php
namespace BitCommunism\Marx;
use DI\Container;
use DI\ContainerBuilder;
use function DI\value;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use function FastRoute\simpleDispatcher;
use function GuzzleHttp\Psr7\copy_to_stream;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\ServerRequest;
use function GuzzleHttp\Psr7\stream_for;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class Instance
{
const CONFIG_PATH = '%s/config/%s.php';
/**
* @var string
*/
protected $directory;
/**
* @var string
*/
protected $environment;
/**
* @var Container
*/
protected $container;
/**
* @var string[]
*/
protected $modules = [];
/**
* @var Dispatcher
*/
protected $router;
/**
* @var \Closure[]
*/
protected $calls = [];
protected $knownCalls = [
'doctrine' => 'doctrine',
'cli' => 'cli',
'http' => 'http',
];
public static function create($directory, $environment = 'development', $overrideConfig = []): Instance
{
$realDir = realpath($directory);
if (file_exists($realDir . '/vendor/autoload.php')) {
include_once $directory . '/vendor/autoload.php';
}
if ($environment === 'dev') {
$environment = 'development';
}
if ($environment === 'prod') {
$environment = 'production';
}
$instance = new Instance(realpath($realDir), $environment);
$instance->load($overrideConfig);
return $instance;
}
public function __construct($directory, $environment = 'development')
{
$this->directory = $directory;
$this->environment = $environment;
}
public function load($overrideConfig = [])
{
$container = $this->buildTemporaryContainer($overrideConfig);
$this->buildContainer($container->get('modules'), $overrideConfig);
$this->registerCalls();
$this->runLoaders();
}
protected function registerCalls() {
$calls = $this->container->get('marx.calls');
foreach ($calls as $name => $call) {
$this->register($name, function (...$arguments) use ($call) {
return $this->container->call($call, [
'arguments' => $arguments
]);
});
}
}
protected function runLoaders() {
$loaders = $this->container->get('marx.loaders');
foreach ($loaders as $loader) {
$this->container->call($loader);
}
}
protected function loadModules($modules, ContainerBuilder $builder) {
$directories = [];
$loadedModules = [];
foreach ($modules as $module) {
if (strpos($module, '/') === false) {
$module = 'bitcommunism/' . $module;
}
$directory = $this->directory;
$modulePath = $directory . '/vendor/' . $module;
if (!is_dir($modulePath)) {
$stderr = fopen('php://stderr', 'w+');
fwrite($stderr, "Warning: Module '{$module}' doesn't exist, did you install it? e.g.:\n\n\tcomposer require {$module}\n\n");
// fclose($stderr);
}
$directories[] = $modulePath;
$loadedModules[$module] = true;
}
$builder->addDefinitions($loadedModules);
$this->loadDefinitions($builder, $directories);
}
public function register($name, $callback) {
$this->calls[$name] = $callback;
}
public function __call($name, $arguments)
{
if (!isset($this->calls[$name])) {
if (isset($this->knownCalls[$name])) {
throw new \Exception("No call {$name} found, did you install and enable the {$this->knownCalls[$name]} module?");
}
throw new \Exception("No call {$name} found. are you missing a module?");
}
return $this->calls[$name](...$arguments);
}
private function buildTemporaryContainer($overrideConfig = []) {
$builder = new ContainerBuilder();
$builder->addDefinitions([
'basedir' => $this->directory,
]);
$this->loadDefinitions($builder, [__DIR__ . '/..', $this->directory]);
$builder->addDefinitions($overrideConfig);
return $builder->build();
}
private function loadDefinitions(ContainerBuilder $builder, $directories = []) {
$configTypes = ['default', 'routes', $this->environment];
foreach ($directories as $directory) {
foreach ($configTypes as $configType) {
$configPath = sprintf(self::CONFIG_PATH, $directory, $configType);
if (!file_exists($configPath)) {
continue;
}
try {
$config = include($configPath);
} catch (\Throwable $e) {
// Failed loading config
continue;
}
if ($configType === 'routes') {
// Routes needs to be put in 'routes'
$config = [
'routes' => $config,
];
}
if (!is_array($config)) {
continue;
}
$builder->addDefinitions($config);
}
}
}
private function buildContainer($modules, $overrideConfig = [])
{
$builder = new ContainerBuilder();
$builder->addDefinitions([
'basedir' => $this->directory,
'marx.instance' => value($this),
self::class => $this,
]);
$this->loadDefinitions($builder, [__DIR__ . '/..']);
$this->loadModules($modules, $builder);
$this->loadDefinitions($builder, [$this->directory]);
$builder->addDefinitions($overrideConfig);
$this->container = $builder->build();
}
public function has(string $key): bool {
return $this->container->has($key);
}
public function get(string $key, $default = null) {
if (!$this->container->has($key)) {
return $default;
}
return $this->container->get($key);
}
}