*/ private array $services = []; private bool $booted = false; public static function instance(): self { return self::$instance ??= new self(); } /** * @template T of object * @param class-string $class_name * @return T */ public function service( string $class_name ): object { /** @var T */ return $this->services[ $class_name ] ??= new $class_name(); } public function boot(): void { if ( $this->booted ) { return; } $this->booted = true; // No load_plugin_textdomain(): the text domain is the WordPress.org slug, so // WordPress loads the translations itself, when they are first needed. // Cheap when nothing has changed: one option comparison. It is here rather than // only on activation because activation hooks do not run on a plugin update, nor // on a site added to a network after the plugin was network-activated. Installer::maybe_upgrade(); // The redirect and the 404 monitor run on the front end, so neither is wrapped in // is_admin() — that is the whole point of them. $this->service( Handler::class )->boot(); $this->service( Recorder::class )->boot(); $this->service( SlugWatcher::class )->boot(); $this->service( Cleanup::class )->boot(); $this->service( RestModule::class )->boot(); $this->service( PostBox::class )->boot(); // The admin bar shortcut appears on the front end too, so the admin module boots // everywhere and decides for itself what each screen needs. $this->service( AdminModule::class )->boot(); if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'codebold-redirect', Command::class ); } /** * Fires once the plugin has attached its hooks. * * @param Plugin $plugin */ do_action( 'codebold_redirect_booted', $this ); } public static function activate( bool $network_wide = false ): void { if ( is_multisite() && $network_wide ) { foreach ( get_sites( [ 'fields' => 'ids', 'number' => 0 ] ) as $site_id ) { switch_to_blog( (int) $site_id ); Installer::install(); restore_current_blog(); } return; } Installer::install(); } public static function deactivate(): void { // The redirects are kept. Deactivating is how people troubleshoot, and coming back // to find five hundred redirects gone would turn a five-minute check into a // broken site. Only the scheduled job is removed, because it has nothing to do. Cleanup::unschedule(); } public function url( string $path = '' ): string { return plugins_url( ltrim( $path, '/' ), \CodeBold\Redirect\FILE ); } }