container = new ServiceContainer(); $this->register_services(); } /** * Run the plugin and register WordPress hooks. * * @since 1.0.0 */ public function run(): void { add_action(self::CRON_HOOK, [$this, 'cleanup_expired_rules']); add_action('rest_api_init', $this->lazy('rest_api_controller', 'register_routes')); if (!is_admin()) { return; } add_action('admin_init', [$this, 'bootstrap_admin'], 0); // Selector anchors for submenu items. The menu is complete only at the // very end of wp-admin/includes/menu.php, which is before `admin_init`, // so this cannot be deferred like the other services. add_filter('add_menu_classes', $this->lazy('menu_anchor_service', 'add_anchor_classes'), 9999); // Settings screen. add_action('admin_menu', $this->lazy('settings_page', 'add_admin_menu')); add_action('admin_post_adsd_update_general', $this->lazy('settings_page', 'handle_update_general')); add_action('admin_post_adsd_reset_rules', $this->lazy('settings_page', 'handle_reset_rules')); // Assets and early hiding CSS. add_action('admin_enqueue_scripts', $this->lazy('assets_manager', 'enqueue_admin_assets')); add_action('admin_head', $this->lazy('inline_css_generator', 'inject_inline_css'), 1); // Admin bar toggle. add_action('admin_bar_menu', $this->lazy('admin_bar_controller', 'add_admin_bar_button'), 999); add_action('admin_enqueue_scripts', $this->lazy('admin_bar_controller', 'enqueue_admin_bar_assets')); add_action('admin_enqueue_scripts', $this->lazy('admin_bar_controller', 'enqueue_pointer_scripts')); add_action('wp_ajax_adsd_dismiss_pointer', $this->lazy('admin_bar_controller', 'handle_dismiss_pointer')); // Review notice. add_action('admin_init', $this->lazy('review_notice_manager', 'init')); add_action('wp_ajax_adsd_dismiss_review_notice', $this->lazy('review_notice_manager', 'handle_dismiss_notice')); // HTML processing and notice interception. add_action('admin_init', $this->lazy('output_buffer_service', 'start_output_buffering'), 20); add_action('admin_init', $this->lazy('notice_id_manager', 'init'), 1); } /** * Get the service container instance. * * @since 1.0.0 * @return ServiceContainer */ public function get_container(): ServiceContainer { return $this->container; } /** * Bootstrap admin-related services and options. * * @since 1.0.0 */ public function bootstrap_admin(): void { /** @var OptionsRepository $options */ $options = $this->container->get('options_repository'); $options->maybe_migrate(); // Installs updated from an older version never ran the activation hook. if (!wp_next_scheduled(self::CRON_HOOK)) { wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', self::CRON_HOOK); } } /** * Clean up expired rules. * * @since 1.0.0 */ public function cleanup_expired_rules(): void { /** @var OptionsRepository $options */ $options = $this->container->get('options_repository'); $options->cleanup_expired_rules(); } /** * Build a callback that resolves a service only when the hook fires. * * @since 1.0.29 * @param string $service Service identifier. * @param string $method Method to call on the service. * @return callable Hook callback. */ private function lazy(string $service, string $method): callable { return function ($argument = null) use ($service, $method) { $instance = $this->container->get($service); return $argument === null ? $instance->$method() : $instance->$method($argument); }; } /** * Register all services in the container. * * @since 1.0.0 */ private function register_services(): void { // Core services $this->container->set('logger', static function (): Logger { return new Logger(); }); $this->container->set('nonce_service', static function (): NonceService { return new NonceService('adsd'); }); $this->container->set('options_repository', static function (): OptionsRepository { return new OptionsRepository(); }); $this->container->set('xpath_engine', function (): XPathEngine { return new XPathEngine($this->container->get('logger')); }); // Dependent services $this->container->set('capabilities_service', function (): CapabilitiesService { return new CapabilitiesService($this->container->get('options_repository')); }); $this->container->set('inline_css_generator', function (): InlineCssGenerator { return new InlineCssGenerator( $this->container->get('options_repository'), $this->container->get('logger') ); }); $this->container->set('assets_manager', function (): AssetsManager { return new AssetsManager($this->container->get('capabilities_service')); }); $this->container->set('admin_bar_controller', function (): AdminBarController { return new AdminBarController( $this->container->get('capabilities_service'), $this->container->get('options_repository'), $this->container->get('nonce_service') ); }); $this->container->set('output_buffer_service', function (): OutputBufferService { return new OutputBufferService( $this->container->get('xpath_engine'), $this->container->get('options_repository'), $this->container->get('capabilities_service'), $this->container->get('logger') ); }); $this->container->set('settings_page', function (): SettingsPage { return new SettingsPage( $this->container->get('options_repository'), $this->container->get('logger') ); }); $this->container->set('rest_api_controller', function (): RestApiController { return new RestApiController( $this->container->get('options_repository'), $this->container->get('nonce_service'), $this->container->get('capabilities_service'), $this->container->get('logger'), $this->container->get('xpath_engine'), $this->container->get('inline_css_generator') ); }); $this->container->set('review_notice_manager', function (): ReviewNoticeManager { return new ReviewNoticeManager($this->container->get('capabilities_service')); }); $this->container->set('notice_id_manager', function (): NoticeIdManager { return new NoticeIdManager( $this->container->get('options_repository'), $this->container->get('capabilities_service') ); }); $this->container->set('menu_anchor_service', function (): MenuAnchorService { return new MenuAnchorService( $this->container->get('options_repository'), $this->container->get('capabilities_service') ); }); } }