get( $key ); return ( is_string( $v ) && '' !== $v ) ? $v : null; } catch ( \Exception $e ) { return null; } } if ( 'memcached' === $backend || 'memcached_only' === $backend ) { $m = self::memcached_client( $settings ); if ( ! $m ) { return null; } $v = $m->get( $key ); return ( is_string( $v ) && '' !== $v ) ? $v : null; } return null; } /** * Write HTML to disk + optional memory backend. * * @param array $settings Settings. * @param string $html_path Path. * @param string $html Body. * @param int $ttl Seconds. * @return bool Disk write ok. */ public static function put( array $settings, $html_path, $html, $ttl = 36000 ) { $backend = self::backend( $settings ); $ok = true; if ( 'redis_only' !== $backend && 'memcached_only' !== $backend ) { $ok = self::put_disk( $html_path, $html ); } if ( in_array( $backend, array( 'redis', 'memcached', 'redis_only', 'memcached_only' ), true ) ) { self::put_memory( $settings, $html_path, $html, $ttl ); } return $ok; } /** * @param string $html_path Path. * @param string $html Body. * @return bool */ public static function put_disk( $html_path, $html ) { $dir = dirname( $html_path ); if ( ! is_dir( $dir ) ) { if ( function_exists( 'wp_mkdir_p' ) ) { wp_mkdir_p( $dir ); } else { wp_mkdir_p( $dir ); // phpcs:ignore WordPress.PHP.NoSilencedErrors } } $tmp = $html_path . '.' . uniqid( 'tmp', true ) . '.tmp'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- page cache write. $written = file_put_contents( $tmp, $html, LOCK_EX ); if ( false === $written ) { return false; } // Atomic rename is required so visitors never receive a partially written cache file. // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename, WordPress.PHP.NoSilencedErrors -- Atomic page-cache commit. return @rename( $tmp, $html_path ); } /** * @param array $settings Settings. * @param string $html_path Path. * @param string $html Body. * @param int $ttl TTL. * @return void */ public static function put_memory( array $settings, $html_path, $html, $ttl ) { $key = self::memory_key( $html_path ); $ttl = max( 60, (int) $ttl ); $backend = self::backend( $settings ); if ( 'redis' === $backend || 'redis_only' === $backend ) { $r = self::redis_client( $settings ); if ( $r ) { try { $r->setex( $key, $ttl, $html ); } catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement } } } elseif ( 'memcached' === $backend || 'memcached_only' === $backend ) { $m = self::memcached_client( $settings ); if ( $m ) { $m->set( $key, $html, $ttl ); } } } /** * @param array $settings Settings. * @param string $html_path Path. * @return void */ public static function delete( array $settings, $html_path ) { if ( is_string( $html_path ) && is_file( $html_path ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink unlink( $html_path ); } $key = self::memory_key( $html_path ); $backend = self::backend( $settings ); if ( 'redis' === $backend || 'redis_only' === $backend ) { $r = self::redis_client( $settings ); if ( $r ) { try { $r->del( $key ); } catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement } } } elseif ( 'memcached' === $backend || 'memcached_only' === $backend ) { $m = self::memcached_client( $settings ); if ( $m ) { $m->delete( $key ); } } } /** * Invalidate QW Optimize's own page-cache keys, including Redis-only and * Memcached-only backends. Old keys naturally expire via their existing TTL. * Never FLUSHDB: the database may contain unrelated application data. * * @param array $settings Settings (retained for backward compatibility). * @return void */ public static function flush_memory( array $settings ) { update_option( 'qwsc_page_cache_generation', wp_generate_uuid4(), false ); } /** * @param array $settings Settings. * @return \Redis|null */ public static function redis_client( array $settings ) { if ( ! class_exists( 'Redis', false ) ) { return null; } $host = isset( $settings['page_cache_redis_host'] ) ? (string) $settings['page_cache_redis_host'] : ''; if ( '' === $host ) { $host = '127.0.0.1'; } $port = isset( $settings['page_cache_redis_port'] ) ? (int) $settings['page_cache_redis_port'] : 0; if ( $port <= 0 ) { $port = 6379; } $db = isset( $settings['page_cache_redis_db'] ) ? (int) $settings['page_cache_redis_db'] : 1; try { $r = new \Redis(); if ( ! $r->connect( $host, $port, 1.5 ) ) { return null; } if ( $db >= 0 ) { $r->select( $db ); } return $r; } catch ( \Exception $e ) { return null; } } /** * @param array $settings Settings. * @return \Memcached|null */ public static function memcached_client( array $settings ) { if ( ! class_exists( 'Memcached', false ) ) { return null; } $host = isset( $settings['page_cache_memcached_host'] ) ? (string) $settings['page_cache_memcached_host'] : ''; if ( '' === $host ) { $host = '127.0.0.1'; } $port = isset( $settings['page_cache_memcached_port'] ) ? (int) $settings['page_cache_memcached_port'] : 0; if ( $port <= 0 ) { $port = 11211; } try { $m = new \Memcached(); $m->addServer( $host, $port ); return $m; } catch ( \Exception $e ) { return null; } } }