$transformers HTML transformers applied to cacheable pages. * @param callable(\Throwable): void $on_error Error reporter. * @param bool $store_pages False when only optimizations are on. * @param list $output Transformers applied to HTML pages that are not cached. */ public function __construct( private readonly CacheRules $rules, private readonly PageStore $store, callable $clock, private readonly array $transformers = [], private readonly mixed $on_error = null, private readonly bool $store_pages = true, private readonly array $output = [], ) { $this->clock = \Closure::fromCallable( $clock ); } /** * Captures a response. * * @param RequestContext $request Request. * @param ResponseContext $response Response. */ public function capture( RequestContext $request, ResponseContext $response ): CaptureResult { $policy = new CacheabilityPolicy( $this->rules ); $cacheable = $policy->forRequest( $request )->isCacheable() && $policy->forStorage( $request )->isCacheable() && $policy->forResponse( $response )->isCacheable(); if ( ! $cacheable ) { return new CaptureResult( $policy->isHtmlDocument( $response ) ? $this->applyOutput( $response->body ) : $response->body, false ); } $html = $response->body; try { foreach ( $this->transformers as $transform ) { $html = $transform( $html ); } if ( $this->store_pages ) { $this->store->store( CacheKey::fromRequest( $request, $this->rules ), $html . $this->footprint() ); } } catch ( \Throwable $error ) { if ( is_callable( $this->on_error ) ) { ( $this->on_error )( $error ); } } return new CaptureResult( $html, true ); } /** * Output transformers; the untouched body when one fails. * * @param string $html HTML. */ private function applyOutput( string $html ): string { $result = $html; try { foreach ( $this->output as $transform ) { $result = $transform( $result ); } return $result; } catch ( \Throwable $error ) { if ( is_callable( $this->on_error ) ) { ( $this->on_error )( $error ); } return $html; } } /** * HTML comment identifying cached copies. */ private function footprint(): string { return "\n'; } }