settings = $settings; $this->registry = $registry; $this->version = $version; } /** * Hook the route. */ public function register(): void { add_action( 'rest_api_init', array( $this, 'register_route' ) ); } /** * Register the route. */ public function register_route(): void { register_rest_route( self::NAMESPACE, self::ROUTE, array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'serve' ), 'permission_callback' => '__return_true', // Public data, the same for every visitor. 'args' => array( 'v' => array( 'type' => 'string', 'default' => '', ), ), ) ); } /** * Delete the cached details. */ public function invalidate(): void { delete_transient( self::TRANSIENT ); } /** * The details and their version, from the cache when it is warm. * * @return array{version: string, services: array>} */ public function get(): array { $hash = md5( (string) wp_json_encode( array( array_keys( Categories::resolve( $this->settings->get() ) ), $this->version, ConfigBuilder::FORMAT ) ) ); $cached = get_transient( self::TRANSIENT ); if ( is_array( $cached ) && ( $cached['hash'] ?? null ) === $hash && is_string( $cached['version'] ?? null ) && is_array( $cached['services'] ?? null ) ) { return array( 'version' => $cached['version'], 'services' => $cached['services'], ); } $services = $this->registry->public_services(); $version = substr( md5( (string) wp_json_encode( $services ) ), 0, 12 ); set_transient( self::TRANSIENT, array( 'hash' => $hash, 'version' => $version, 'services' => $services, ) ); return array( 'version' => $version, 'services' => $services, ); } /** * Where the browser fetches the details: a path on this site, so it works from whichever host serves the page. */ public function url(): string { return add_query_arg( 'v', $this->get()['version'], wp_make_link_relative( rest_url( self::NAMESPACE . self::ROUTE ) ) ); } /** * Answer the route with the details, cacheable. * * @param WP_REST_Request $request Request; `v` is the version from {@see self::url()}. */ public function serve( WP_REST_Request $request ): WP_REST_Response { $details = $this->get(); $response = new WP_REST_Response( $details['services'] ); $response->header( 'Cache-Control', $request->get_param( 'v' ) === $details['version'] ? 'public, max-age=31536000, immutable' : 'public, max-age=300' ); return $response; } }