<?php declare( strict_types=1 ); namespace WPDesk\Init; use DI\Container; use DI\ContainerBuilder as DiBuilder; use WPDesk\Init\Bundle\Bundle; use WPDesk\Init\Configuration\Configuration; use WPDesk\Init\DependencyInjection\ContainerBuilder; use WPDesk\Init\Dumper\PhpFileDumper; use WPDesk\Init\HookDriver\HookDriver; use WPDesk\Init\HookDriver\CallbackDriver; use WPDesk\Init\Loader\PhpFileLoader; /** * Plugin builder class responsible for our initialization system. * * @api */ final class PluginInit { /** @var Bundle[] */ private $bundles = []; /** @var string|null Plugin filename. */ private $filename; /** @var Configuration */ private $config; /** @var PhpFileLoader */ private $loader; /** @var HookDriver */ private $driver; /** @var HeaderParser */ private $parser; /** * @param string|array|Configuration $config */ public function __construct( $config, ?HookDriver $driver = null, ?HeaderParser $parser = null ) { $this->loader = new PhpFileLoader(); if ( $config instanceof Configuration ) { $this->config = $config; } elseif ( \is_array( $config ) ) { $this->config = new Configuration( $config ); } elseif ( \is_string( $config ) ) { $this->config = new Configuration( $this->loader->load( $config ) ); } else { throw new \InvalidArgumentException( sprintf( 'Configuration must be either path to configuration file, array of configuration data or %s instance', Configuration::class ) ); } $this->driver = $driver ?? new CallbackDriver(); $this->parser = $parser ?? new DefaultHeaderParser(); } /** * Build and return a plugin. * * @return Plugin|null If plugin failed to build (e.g. requirements are not fulfilled), * initialization process returns null. There are no exceptions thrown on foreseeable issues * as those cases should be handled gracefully, by displaying admin notice if possible and * preventing to initialize plugin functions without disrupting a website. */ public function init(): ?Plugin { if ( empty( $this->filename ) ) { // TODO: We have to fina a better way, as you can either call it directly or use Init::from_config(). $backtrace = \debug_backtrace( \DEBUG_BACKTRACE_IGNORE_ARGS, 1 ); $this->filename = $backtrace[0]['file']; } $cache_path = $this->get_cache_dir() . '/plugin.php'; try { $plugin_data = $this->loader->load( $cache_path ); } catch ( \Exception $e ) { $dumper = new PhpFileDumper(); $dumper->dump( $this->parser->parse( $this->filename ), $cache_path ); $plugin_data = $this->loader->load( $cache_path ); } $plugin = $this->create_plugin( $plugin_data ); $container = $this->initialize_container( $plugin ); $container->set( Plugin::class, $plugin ); $this->driver->register_hooks( $this->config, $this->bundles, $container ); return $plugin; } private function get_cache_dir(): string { return $this->filename . '/' . $this->config->get( 'cache_path', 'generated' ); } private function initialize_container( Plugin $plugin ): Container { $original_builder = new DiBuilder(); $builder = new ContainerBuilder( $original_builder ); $builder->add_definitions( __DIR__ . '/Resources/services.inc.php', $this->config->get( 'services', [] ) ); return $builder->build(); } /** * @param array{Name: string, Version?: string, TextDomain: string} $plugin_data */ private function create_plugin( array $plugin_data ): Plugin { return new Plugin( $this->filename, $plugin_data['Name'], $plugin_data['Version'] ?? '0.0.0', $plugin_data['TextDomain'], ); } }