> */ private array $early_providers = [ Log\Provider::class, Memoize\Provider::class, Config\Provider::class, View\Provider::class, Cache_Delivery\Provider::class, Page_Cache\Request_Context\Provider::class, Page_Cache\Meta\Provider::class, Page_Cache\Provider::class, // This should always be last, to ensure dependencies have been built. Update\Provider::class, ]; /** * An array of providers to register within the container after * WordPress is fully bootstrapped. * * @since 0.1.0 * * @var array> */ private array $providers = [ Database\Provider::class, Storage\Provider::class, Lock\Provider::class, Assets\Provider::class, Page_Cache\Purge\Provider::class, Preload\Provider::class, Admin\Provider::class, API\Provider::class, Notices\Provider::class, Pipelines\Provider::class, Telemetry\Provider::class, Image_Transformation\Provider::class, WP_CLI\Provider::class, Shutdown\Provider::class, Dom\Provider::class, Cron\Provider::class, ]; /** * The singleton instance for the plugin. * * @var Core */ private static self $instance; /** * @param string $plugin_file The full server path to the main plugin file. * @param Container $container The container instance. */ private function __construct( string $plugin_file, Container $container ) { $this->plugin_file = $plugin_file; $this->container = $container; $this->container->singleton( ContainerInterface::class, $this->container ); $this->container->singleton( StellarContainerInterface::class, $this->container ); // Set container variables available to pre bootstrap providers. $this->container->setVar( self::PLUGIN_FILE, $this->plugin_file ); $this->container->setVar( self::PLUGIN_VERSION, $this->get_plugin_version() ); $this->container->setVar( self::CACHE_DIR, WP_CONTENT_DIR . '/cache/solid-performance' ); $this->container->setVar( self::ADVANCED_CACHE_PATH, WP_CONTENT_DIR . '/advanced-cache.php' ); // Set the cache dir early so all providers can use it. $this->register_mandatory_definitions(); // Register pre bootstrap providers early. $this->register_early_providers(); } /** * Get the singleton instance of our plugin. * * @param string|null $plugin_file The full server path to the main plugin file. * @param Container|null $container The container instance. * * @throws InvalidArgumentException If no existing instance and no plugin file or container is provided. * * @return Core */ public static function instance( ?string $plugin_file = null, ?Container $container = null ): Core { if ( ! isset( self::$instance ) ) { if ( ! $plugin_file ) { throw new InvalidArgumentException( 'You must provide a $plugin_file path' ); } if ( ! $container ) { throw new InvalidArgumentException( sprintf( 'You must provide a %s instance!', Container::class ) ); } self::$instance = new self( $plugin_file, $container ); } return self::$instance; } /** * Reload service provider container definitions. * * @return Core */ public function reload(): Core { $this->register_mandatory_definitions(); $this->register_early_providers(); return $this->init(); } /** * Initialize the plugin. * * @action plugins_loaded * * @return Core */ public function init(): Core { $this->register_mandatory_definitions(); // Set remaining container variables now that WordPress is fully bootstrapped. $this->container->setVar( self::PLUGIN_DIR, plugin_dir_path( $this->plugin_file ) ); $this->container->setVar( self::PLUGIN_URL, plugin_dir_url( $this->plugin_file ) ); $this->container->setVar( self::PLUGIN_BASENAME, plugin_basename( $this->plugin_file ) ); // Register remaining providers now that WP is fully bootstrapped. foreach ( $this->providers as $class ) { $this->container->get( $class )->register( $this->container ); } return self::$instance; } /** * Returns the container instance. * * @return Container */ public function container(): Container { return $this->container; } /** * Prevent wakeup. * * @throws RuntimeException When attempting to wake up this instance. */ public function __wakeup(): void { throw new RuntimeException( 'method not implemented' ); } /** * Prevent sleep. * * @throws RuntimeException When attempting to sleep this instance. */ public function __sleep(): array { throw new RuntimeException( 'method not implemented' ); } /** * Prevent cloning. * * @throws RuntimeException When attempting to clone this instance. */ private function __clone() { throw new RuntimeException( 'Cloning not allowed' ); } /** * Mandatory container definitions that run both early and on init. * * @return void */ private function register_mandatory_definitions(): void { $this->container->when( Cache_Path::class ) ->needs( '$cache_dir' ) ->give( static fn ( $c ) => $c->get( self::CACHE_DIR ) ); } /** * Register early loaded bootstrap providers. * * @return void */ private function register_early_providers(): void { foreach ( $this->early_providers as $provider ) { $this->container->get( $provider )->register( $this->container ); } } /** * Get the plugin's current version. * * @throws RuntimeException If we are unable to retrieve the plugin version. * * @return string */ private function get_plugin_version(): string { $content = file_get_contents( $this->plugin_file, false, null, 0, 8 * KB_IN_BYTES ); // Prevent race condition if the solid-performance file is already deleted. if ( ! $content ) { return '0.0.0'; } $pattern = '/^ \* Version:\s*(.+)$/m'; if ( preg_match( $pattern, $content, $matches ) ) { return trim( $matches[1] ); } throw new RuntimeException( sprintf( 'Unable to read plugin version from "%s"', $this->plugin_file ) ); } }