* }> * }> $collections */ private $collections; /** * @param HttpClientInterface $client The HTTP client. * @param LoggerInterface $logger The logger. * @param Hasher $hasher The hasher. * @param int $cache_duration The cache duration in seconds. * @param int $batch_size How many external cache requests to create before removing them. */ public function __construct( HttpClientInterface $client, LoggerInterface $logger, Hasher $hasher, int $cache_duration, int $batch_size = 500 ) { $this->client = $client; $this->logger = $logger; $this->hasher = $hasher; $this->cache_duration = $cache_duration; $this->batch_size = $batch_size; } /** * Assign which collections will be primed on the WordPress shutdown hook. * * @param array * }> * }> $collections * * @return void */ public function init( array $collections ): void { if ( empty( $collections ) ) { return; } $this->collections = $collections; } /** * Prime cache on shutdown. * * @action shutdown * * @return void * @throws \InvalidArgumentException * @throws \RuntimeException */ public function terminate(): void { $this->execute(); } /** * On shutdown, make asynchronous HEAD requests to all the potential images we'll download * to ensure that Pexels caches their response to make downloading much quicker. * * This allows for 0 blocking. * * @action shutdown * * @return void * * @throws \InvalidArgumentException * @throws \RuntimeException */ private function execute(): void { if ( ! isset( $this->collections ) ) { return; } $batch = 0; $cache_key = $this->hasher->hash( $this->collections ); if ( get_transient( $cache_key ) !== false ) { $this->logger->debug( sprintf( 'Found cache key "%s", skipping image cache priming', $cache_key ) ); unset( $this->collections ); return; } // Search results differ slightly from industry collections, reformat. if ( isset( $this->collections['images'] ) ) { $this->collections = [ $this->collections ]; } foreach ( $this->collections as $collection ) { $this->logger->debug( sprintf( 'Priming image cache for %d images...', count( $collection['images'] ) ) ); foreach ( $collection['images'] as $image ) { $this->logger->debug( sprintf( 'Priming image cache for: %s', $image['url'] ) ); foreach ( $image['sizes'] as $size ) { try { $batch++; $url = $size['src']; // These are async requests; We won't wait for the responses. $promises[ $url ] = $this->client->request( 'HEAD', $url, [ 'timeout' => 0.1, 'max_duration' => 0.1, ] ); } catch ( Throwable $e ) { } // Remove existing promises when batch size reached. if ( $batch >= $this->batch_size ) { $batch = 0; try { unset( $promises ); } catch ( Throwable $e ) { } } } } } // Remove any remaining promises. try { unset( $promises ); } catch ( Throwable $e ) { } // Clear collections state in case this is accessed again in the same request. unset( $this->collections ); $this->logger->debug( sprintf( 'Caching image priming using key "%s" for %d seconds', $cache_key, $this->cache_duration ) ); set_transient( $cache_key, true, $this->cache_duration ); } }