booted ) { return; } $this->booted = true; foreach ( $this->controllers() as $controller ) { $controller->boot(); } $this->register_cli(); } /** * Everything that registers hooks, in the order it should register them. * * Assembled per request type. The admin screens are not built on a front-end page view, and the * page tag is not built inside wp-admin — an object that cannot do anything useful on this request * should not be constructed on it. * * @return BootableInterface[] */ private function controllers(): array { $controllers = array(); // The queue worker is always registered: background hooks fire on requests that belong to // neither the admin nor the front end, and a job with nothing listening would never drain. $controllers[] = new QueueController( $this->worker() ); if ( is_admin() ) { $renderer = new Renderer( ADPIX_ANALYTICS_PATH . 'src/View/templates' ); $pages = new PageFinder(); $settings_page = new SettingsController( $this->settings(), new SettingsSanitizer( $this->settings() ), $this->queue(), $this->log(), $pages, $renderer ); $stats_page = new StatsController( new StatsService( new ReportClient( $this->settings(), $this->log() ) ), $this->settings(), new Countries(), $renderer ); // The menu is given the screens in sidebar order and knows nothing else about them. The // first is what the top-level entry opens, so Settings stays where anybody who used the // previous version will look for it, with the statistics below. $controllers[] = new MenuController( array( $settings_page, $stats_page ) ); $controllers[] = $settings_page; $controllers[] = $stats_page; $controllers[] = new AjaxController( new ConnectionTester( $this->client() ), $this->queue(), $this->log(), $pages ); $controllers[] = new PluginMetaController( $this->settings(), ADPIX_ANALYTICS_BASENAME ); $controllers[] = new PrivacyController( $this->settings() ); if ( $this->has_woocommerce() ) { $controllers[] = new OrderPanelController( $this->client(), $this->settings(), $renderer ); } } // Registered on both sides. The tag normally belongs only to the front end, but the Admin area // setting exists precisely so a site can measure wp-admin too, and that decision is // TagVisibility's to make rather than something to pre-empt by not registering here. $controllers[] = new TagController( $this->settings(), new TagVisibility() ); // Integrations register on both sides: an order can be paid from a webhook, a user can be // created in wp-admin, and a form can be submitted through the REST API. $controllers[] = new UserController( $this->settings(), $this->recorder() ); $controllers[] = new FormsController( $this->settings(), $this->recorder() ); if ( $this->has_woocommerce() ) { $controllers[] = new WooCommerceController( $this->settings(), $this->recorder(), $this->visitor() ); } return $controllers; } /** * Expose the queue command to WP-CLI. * * @return void */ private function register_cli(): void { if ( ! defined( 'WP_CLI' ) || ! WP_CLI || ! class_exists( '\WP_CLI' ) ) { return; } \WP_CLI::add_command( 'adpix', new QueueCommand( $this->queue(), $this->worker(), new ConnectionTester( $this->client() ) ) ); } // ------------------------------------------------------------------------- // Lazily built collaborators. Each is a singleton for the request, because // they are stateless services and building two of any of them would only // duplicate the per-request caches inside them. // ------------------------------------------------------------------------- /** * @return SettingsRepositoryInterface */ private function settings(): SettingsRepositoryInterface { if ( null === $this->settings ) { $this->settings = new OptionSettingsRepository(); } return $this->settings; } /** * @return ErrorLog */ private function log(): ErrorLog { if ( null === $this->log ) { $this->log = new ErrorLog( $this->settings() ); } return $this->log; } /** * @return Client */ private function client(): Client { if ( null === $this->client ) { $this->client = new Client( $this->settings(), new SignerFactory(), $this->log() ); } return $this->client; } /** * @return QueueInterface */ private function queue(): QueueInterface { if ( null === $this->queue ) { $factory = new QueueFactory(); $this->queue = $factory->create(); } return $this->queue; } /** * @return Worker */ private function worker(): Worker { return new Worker( $this->client(), $this->queue(), new RetryPolicy(), $this->log() ); } /** * @return VisitorContext */ private function visitor(): VisitorContext { if ( null === $this->visitor ) { $this->visitor = new VisitorContext( $this->settings()->load()->flag( 'send_ip' ) ); } return $this->visitor; } /** * @return EventRecorder */ private function recorder(): EventRecorder { if ( null === $this->recorder ) { $this->recorder = new EventRecorder( $this->settings(), $this->queue(), $this->visitor(), new ConsentGate( $this->settings(), $this->visitor() ), $this->log() ); } return $this->recorder; } /** * Whether WooCommerce is active on this request. * * @return bool */ private function has_woocommerce(): bool { return class_exists( 'WooCommerce' ); } }