listUseCaseOverride = $listUseCaseOverride; $this->summaryUseCaseOverride = $summaryUseCaseOverride; $this->maskerOverride = $maskerOverride; $this->controllerOverride = $controllerOverride; } /** * Bind the §12.2 / §12.3 use cases plus the composed * {@see LogsController}, then append two * {@see RouteDefinition} values to the bound * {@see RouteRegistrar}. * * @param ServiceProviderRegistry $registry Registry driving * this bootstrap pass. * * @throws LogicException When the persistence or REST provider * has not registered before this one. */ public function register( ServiceProviderRegistry $registry ): void { $logs = $this->resolveLogRepository( $registry ); $masker = $this->maskerOverride ?? new LogMasker(); $listUseCase = $this->listUseCaseOverride ?? new ListLogsUseCase( $logs, $masker ); $summaryUseCase = $this->summaryUseCaseOverride ?? new GetLogsSummaryUseCase( $logs, $masker ); $controller = $this->controllerOverride ?? new LogsController( $listUseCase, $summaryUseCase, new ProviderExceptionMapper(), new CapabilityPolicy(), new RequestValidator(), new RestRequestAdapter() ); $this->resolvedController = $controller; $registry->bind( self::BINDING_LOG_MASKER, $masker ); $registry->bind( self::BINDING_LIST_LOGS_USE_CASE, $listUseCase ); $registry->bind( self::BINDING_LOGS_SUMMARY_USE_CASE, $summaryUseCase ); $registry->bind( self::BINDING_CONTROLLER, $controller ); $registrar = $this->resolveRouteRegistrar( $registry ); $this->appendRoutes( $registrar ); } /** * No-op — routes are registered via * {@see RestServiceProvider}'s `rest_api_init` driver. * * @param HookRegistrar $hooks Shared hook registrar. */ public function boot( HookRegistrar $hooks ): void { unset( $hooks ); } /** * Resolve {@see LogRepository} from the registry. * * @param ServiceProviderRegistry $registry Bootstrap registry. * * @throws LogicException When the binding is missing or the * bound value is not a * {@see LogRepository}. */ private function resolveLogRepository( ServiceProviderRegistry $registry ): LogRepository { $key = PersistenceServiceProvider::BINDING_LOG_REPOSITORY; if ( ! $registry->has( $key ) ) { $message = 'LogsServiceProvider requires PersistenceServiceProvider to register before it' . ' (missing LogRepository binding).'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } $logs = $registry->get( $key ); if ( ! $logs instanceof LogRepository ) { $message = sprintf( 'LogsServiceProvider expected LogRepository under "%s"; got %s.', $key, is_object( $logs ) ? get_class( $logs ) : gettype( $logs ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } return $logs; } /** * Resolve {@see RouteRegistrar} from the registry. * * @param ServiceProviderRegistry $registry Bootstrap registry. * * @throws LogicException When the binding is missing or the * bound value is not a * {@see RouteRegistrar}. */ private function resolveRouteRegistrar( ServiceProviderRegistry $registry ): RouteRegistrar { $key = RestServiceProvider::BINDING_ROUTE_REGISTRAR; if ( ! $registry->has( $key ) ) { $message = 'LogsServiceProvider requires RestServiceProvider to register before it' . ' (missing RouteRegistrar binding).'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } $registrar = $registry->get( $key ); if ( ! $registrar instanceof RouteRegistrar ) { $message = sprintf( 'LogsServiceProvider expected RouteRegistrar under "%s"; got %s.', $key, is_object( $registrar ) ? get_class( $registrar ) : gettype( $registrar ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } return $registrar; } /** * Append the two §12.2 / §12.3 {@see RouteDefinition} values * to the supplied registrar. * * @param RouteRegistrar $registrar Bound registrar. */ private function appendRoutes( RouteRegistrar $registrar ): void { $permissionCallback = static function ( $request = null ): bool { return $request !== false; }; $registrar->add( new RouteDefinition( RestNamespace::FULL, '/logs', RouteDefinition::METHOD_GET, LogsController::ROUTE_TOKEN_LIST, array(), $this->buildRouteCallback( 'list' ), $permissionCallback ) ); $registrar->add( new RouteDefinition( RestNamespace::FULL, '/logs/summary', RouteDefinition::METHOD_GET, LogsController::ROUTE_TOKEN_SUMMARY, array(), $this->buildRouteCallback( 'summary' ), $permissionCallback ) ); } /** * Build a route-callback {@see Closure} that dispatches to the * named controller method and adapts the returned envelope * onto a {@see \WP_REST_Response} (or the pure-unit-mode array * shim). * * @param string $method One of `'list'`, `'summary'`. */ private function buildRouteCallback( string $method ): Closure { return function ( mixed $request ) use ( $method ): mixed { $controller = $this->resolvedController; if ( $controller === null ) { $message = 'LogsServiceProvider route callback invoked before register() ran.'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } $envelope = match ( $method ) { 'list' => $controller->list( $request ), 'summary' => $controller->summary( $request ), default => $this->raiseUnknownMethod( $method ), }; $status = isset( $envelope[ LogsController::META_HTTP_STATUS ] ) && is_int( $envelope[ LogsController::META_HTTP_STATUS ] ) ? $envelope[ LogsController::META_HTTP_STATUS ] : 200; unset( $envelope[ LogsController::META_HTTP_STATUS ] ); if ( class_exists( '\\WP_REST_Response' ) ) { return new \WP_REST_Response( $envelope, $status ); } return array( 'data' => $envelope, 'status' => $status, ); }; } /** * Raise a {@see LogicException} for an unknown route-callback * method name. * * @param string $method Offending method name. * * @return never * * @throws LogicException Always. */ private function raiseUnknownMethod( string $method ): array { $message = sprintf( 'LogsServiceProvider route callback received unknown method "%s".', $method ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new LogicException( $message ); } }