register(); (new KeptFixesFront())->register(); (new StyleRules())->register(); (new \Sarcio\WordPress\Assistant\Bulk())->register(); (new Account())->register(); (new Routes())->register(); (new \Sarcio\WordPress\Inbox\Routes())->register(); (new \Sarcio\WordPress\Assistant\Review())->register(); (new ConnectNotice())->register(); (new DeactivateFeedback(plugin_basename($this->file)))->register(); (new Stage())->register(); add_action(Report::CRON_HOOK, [Report::class, 'sendIfDue']); add_action('init', [Report::class, 'schedule']); add_action(Scheduled::APPLY_HOOK, [Scheduled::class, 'runApply']); add_action(Scheduled::REVERT_HOOK, [Scheduled::class, 'runRevert']); add_action('init', [Scheduled::class, 'ensureScheduled']); add_action('wp_abilities_api_categories_init', [Catalog::class, 'registerCategory']); add_action('wp_abilities_api_init', [Catalog::class, 'registerWithWordPress']); Journal::installIfBehind(); if (is_admin()) { (new SettingsPage($this))->register(); (new ReviewPage())->register(); (new DashboardWidget())->register(); (new ReportPage())->register(); (new RequestsPage())->register(); (new Connect())->register(); } $shim = $this->shim(); if ($shim !== null) { $scope = (string) Options::get('server_scope'); if ($scope !== 'off') { (new Rest($shim))->register(); } if ($scope === 'all') { (new Request($shim))->register(); } } (new Pull($this))->register(); if (defined('WP_CLI') && WP_CLI) { Cli::register($this); } add_action('init', static fn () => do_action('sarcio_loaded', new Api()), 0); } /** Which route-set cache backend is in play (surfaced on the settings screen). */ public function cacheBackend(): string { if (!Autoloader::shimAvailable()) { return 'none'; } if (ApcuRouteSetCache::isAvailable()) { return 'apcu'; } return function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache() ? 'object-cache' : 'transient'; } public function pluginFile(): string { return $this->file; } /** * The shared shim core, or null when the server tier isn't configured (no * site key, neither a sidecar DSN nor a sidecar key, or the shim library * isn't bundled). */ public function shim(): ?Shim { if ($this->shim !== null) { return $this->shim; } if (!Autoloader::shimAvailable()) { return null; } $siteKey = (string) Options::get('site_key'); $transport = $this->transport($siteKey); $keptOps = class_exists(\Sarcio\Shim\Engine\VerdictEngine::class) ? KeptFixes::serverOps() : []; if ($transport === null && $keptOps === []) { return null; } $routeSetCache = $this->routeSetCache($siteKey); if ($keptOps !== []) { $transport = new KeptTransport($transport, $keptOps); $routeSetCache = new InMemoryRouteSetCache(); } $previewOps = class_exists(\Sarcio\Shim\Engine\VerdictEngine::class) ? Preview::ops() : null; if ($previewOps !== null) { $transport = new PreviewTransport($transport, $previewOps); $routeSetCache = new InMemoryRouteSetCache(); } $this->shim = new Shim( (string) Options::get('sidecar_dsn'), $siteKey, $routeSetCache, max(1, (int) Options::get('evaluate_timeout_ms')) / 1000, (int) Options::get('routeset_ttl_sec'), 1.0, 'wordpress', 65536, (string) Options::get('shim_token'), $transport, ); return $this->shim; } /** * The sidecar when a DSN is set (the shim builds that itself from the DSN); * otherwise the direct pull, which fetches the site's signed fixes from the * control plane with the sidecar key and verifies them here. The verified * set lives in an option so every worker shares one pull per window. */ private function transport(string $siteKey): ?Transport { switch (Options::serverTransport()) { case 'sidecar': return $this->sidecarTransport(); case 'pull': // A Composer install can resolve an older shim than the one the // zip bundles. Without the pull transport, stay inert (the site // runs as if unpatched) rather than fatal on every request. if (!class_exists(PullTransport::class)) { return null; } return new PullTransport( Options::apiUrl(), $siteKey, (string) Options::get('sidecar_key'), new OptionPatchCache($siteKey), new WpHttpClient(), (int) Options::get('pull_refresh_sec'), 5.0, SARCIO_WP_VERSION, ); default: return null; } } private function sidecarTransport(): Transport { return new \Sarcio\Shim\Transport\SidecarTransport( (string) Options::get('sidecar_dsn'), (string) Options::get('site_key'), 1.0, 'wordpress', (string) Options::get('shim_token'), ); } /** * APCu when the host has it (fastest, no database), a transient otherwise — * which is a real memory cache when a persistent object cache is installed, * and the options table when it isn't. WP-CLI and cron are one-shot * processes with no reason to persist a 5-second cache, so they stay * in-memory. */ private function routeSetCache(string $siteKey): RouteSetCache { if (ApcuRouteSetCache::isAvailable()) { return new ApcuRouteSetCache('sarcio.routeset.' . md5($siteKey)); } if ((defined('WP_CLI') && WP_CLI) || (function_exists('wp_doing_cron') && wp_doing_cron())) { return new InMemoryRouteSetCache(); } return new TransientRouteSetCache('sarcio_routeset_' . substr(md5($siteKey), 0, 12)); } }