*/ private $services = array(); /** * Singleton. */ public static function instance(): Plugin { if ( null === self::$instance ) { self::$instance = new self(); self::$instance->boot(); } return self::$instance; } /** * Resolve a service. * * @param string $class_name Class. * @return mixed */ public function get( string $class_name ) { if ( ! isset( $this->services[ $class_name ] ) ) { $this->services[ $class_name ] = $this->make( $class_name ); } return $this->services[ $class_name ]; } /** * Build. * * @param string $class_name Class. * @return object */ private function make( string $class_name ) { switch ( $class_name ) { case Aggregator::class: return new Aggregator( $this->get( AutoloadAnalyzer::class ), $this->get( CronAnalyzer::class ) ); case SessionRunner::class: return new SessionRunner( $this->get( SessionRepository::class ), $this->get( Loopback::class ), $this->get( Aggregator::class ) ); case BrowseMode::class: return new BrowseMode( $this->get( SessionRepository::class ), $this->get( SessionRunner::class ) ); case SessionController::class: return new SessionController( $this->get( SessionRepository::class ), $this->get( SessionRunner::class ), $this->get( BrowseMode::class ), $this->get( Loopback::class ) ); case ResultsController::class: return new ResultsController( $this->get( AutoloadAnalyzer::class ), $this->get( CronAnalyzer::class ) ); case Commands::class: return new Commands( $this ); case Assets::class: return new Assets( $this->get( SessionRepository::class ), $this->get( BrowseMode::class ) ); case Notices::class: return new Notices( $this->get( SessionRepository::class ) ); case Export::class: return new Export( $this->get( SessionRepository::class ) ); } return new $class_name(); } /** * Hooks. */ private function boot(): void { Schema::maybe_upgrade(); add_action( 'rest_api_init', array( $this, 'register_rest' ) ); // The "recording" admin-bar item must work on the front end too, so browse mode can be stopped from any page. add_action( 'admin_bar_menu', function ( $bar ) { $this->get( Assets::class )->admin_bar( $bar ); }, 100 ); add_action( 'wp_enqueue_scripts', function () { $this->get( Assets::class )->enqueue_bar(); } ); if ( is_admin() ) { $this->get( Menu::class )->register_hooks(); $this->get( Assets::class )->register_hooks(); $this->get( Notices::class )->register_hooks(); $this->get( Export::class )->register_hooks(); $this->get( PrivacyPolicy::class )->register_hooks(); } if ( defined( 'WP_CLI' ) && WP_CLI ) { $this->get( Commands::class )->register(); } } /** * REST controllers. */ public function register_rest(): void { foreach ( array( SessionController::class, ResultsController::class, SettingsController::class ) as $controller ) { $this->get( $controller )->register_routes(); } } /** * Activation: schema and defaults only. The must-use loader is written when the first profiling run starts. */ public static function activate(): void { Schema::install(); Settings::ensure_defaults(); } /** * Deactivation: drop run state; remove the loader unless another site on the network still uses it. * * @param bool $network_wide Network deactivation. */ public static function deactivate( bool $network_wide = false ): void { Signature::clear(); delete_option( 'bloatwright_browse_session' ); delete_option( SessionRunner::LOCK ); if ( $network_wide || ! self::active_elsewhere() ) { LoaderInstaller::remove(); } } /** * Is the plugin active on another site of this network? */ private static function active_elsewhere(): bool { if ( ! is_multisite() ) { return false; } $current = get_current_blog_id(); foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0, ) ) as $blog_id ) { if ( (int) $blog_id === $current ) { continue; } switch_to_blog( (int) $blog_id ); $active = in_array( BLOATWRIGHT_BASENAME, (array) get_option( 'active_plugins', array() ), true ); restore_current_blog(); if ( $active ) { return true; } } return false; } }