'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); } }