<?php declare( strict_types=1 ); namespace WPDesk\Init; use DI\Container; use DI\ContainerBuilder as DiBuilder; use Psr\Container\ContainerInterface; use WPDesk\Init\Binding\Binder\CallableBinder; use WPDesk\Init\Binding\Binder\CompositeBinder; use WPDesk\Init\Binding\Binder\HookableBinder; use WPDesk\Init\Binding\Binder\StoppableBinder; use WPDesk\Init\Binding\Loader\ClusteredLoader; use WPDesk\Init\Binding\Loader\CompositeBindingLoader; use WPDesk\Init\Binding\Loader\DebugBindingLoader; use WPDesk\Init\Binding\Loader\OrderedBindingLoader; use WPDesk\Init\Configuration\Configuration; use WPDesk\Init\DependencyInjection\ContainerBuilder; use WPDesk\Init\Extension\ExtensionsSet; use WPDesk\Init\HookDriver\CompositeDriver; use WPDesk\Init\HookDriver\GenericDriver; use WPDesk\Init\HookDriver\HookDriver; use WPDesk\Init\HookDriver\LegacyDriver; use WPDesk\Init\Util\PhpFileDumper; use WPDesk\Init\Util\PhpFileLoader; use WPDesk\Init\Plugin\Header; use WPDesk\Init\Util\Path; use WPDesk\Init\Plugin\DefaultHeaderParser; use WPDesk\Init\Plugin\HeaderParser; use WPDesk\Init\Plugin\Plugin; final class Kernel { /** @var string|null Plugin filename. */ private ?string $filename; private Configuration $config; private PhpFileLoader $loader; private HeaderParser $parser; private ExtensionsSet $extensions; private PhpFileDumper $dumper; public function __construct( string $filename, Configuration $config, ExtensionsSet $extensions ) { $this->filename = $filename; $this->config = $config; $this->extensions = $extensions; $this->loader = new PhpFileLoader(); $this->parser = new DefaultHeaderParser(); $this->dumper = new PhpFileDumper(); } public function boot(): void { $cache_path = $this->get_cache_path( 'plugin.php' ); try { $plugin_data = $this->loader->load( $cache_path ); } catch ( \Exception $e ) { try { $this->dumper->dump( $this->parser->parse( $this->filename ), $cache_path ); $plugin_data = $this->loader->load( $cache_path ); } catch ( \Exception $e ) { $plugin_data = $this->parser->parse( $this->filename ); } } $plugin = new Plugin( $this->filename, new Header( $plugin_data ) ); $container = $this->initialize_container( $plugin ); $container->set( Plugin::class, $plugin ); $container->set( Configuration::class, $this->config ); $this->prepare_driver( $container )->register_hooks(); } private function get_cache_path( string $path = '' ): string { return (string) ( new Path( $this->config->get( 'cache_path', 'generated' ) ) )->join( $path )->absolute( rtrim( plugin_dir_path( $this->filename ), '/' ) . '/' ); } private function get_container_name( Plugin $plugin ): string { return preg_replace( '/[^\w_]/', '_', implode("_", [ $plugin->get_slug(), $plugin->get_version(), 'container' ]) ); } private function initialize_container( Plugin $plugin, bool $useCache = true ): Container { $original_builder = new DiBuilder(); if ( $this->is_prod() && $useCache ) { $original_builder->enableCompilation( $this->get_cache_path(), $this->get_container_name( $plugin ) ); } $builder = new ContainerBuilder( $original_builder ); if ( ! function_exists( 'WPDesk\Init\DI\create' ) ) { require __DIR__ . '/di-functions.php'; } foreach ( $this->extensions as $extension ) { $extension->build( $builder, $plugin, $this->config ); } try { return $builder->build(); } catch ( \Exception $e ) { return $this->initialize_container( $plugin, false ); } } private function prepare_driver( ContainerInterface $container ): HookDriver { $loader = new CompositeBindingLoader(); foreach ( $this->extensions as $extension ) { $loader->add( $extension->bindings( $container ) ); } $loader = new OrderedBindingLoader( new ClusteredLoader( $loader ) ); if ( $this->is_dev() ) { $loader = new DebugBindingLoader( $loader ); } $driver = new GenericDriver( $loader, new CompositeBinder( new StoppableBinder( new HookableBinder( $container ), $container ), new CallableBinder( $container ) ) ); if ( $this->config->get( 'legacy', false ) ) { $driver = new CompositeDriver( $driver, new LegacyDriver( $container ) ); } return $driver; } private function is_dev(): bool { return $this->config->get( 'debug', false ) || wp_get_environment_type() !== 'development'; } private function is_prod(): bool { return $this->is_dev() === false; } }