get_all_settings(); } $hours = isset( $settings['cache_lifespan'] ) ? (int) $settings['cache_lifespan'] : 10; $hours = max( 0, min( 720, $hours ) ); $max_age = $hours > 0 ? $hours * \HOUR_IN_SECONDS : ( 10 * \HOUR_IN_SECONDS ); $rules = array(); if ( class_exists( '\QWSpeedCache\Cache\Exclusions' ) ) { $rules = Exclusions::dropin_rules( is_array( $settings ) ? $settings : array() ); } $data = array( // Bind early cache reads to the configured site, never an arbitrary Host header. 'site_host' => strtolower( (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ) ), 'cache_mobile' => ! empty( $settings['cache_mobile'] ) || ! isset( $settings['cache_mobile'] ), 'max_age' => $max_age, 'reject_uris' => $rules['reject_uris'] ?? array(), 'never_paths' => $rules['never_paths'] ?? array(), 'reject_cookies' => $rules['reject_cookies'] ?? array(), 'ignore_qs' => $rules['ignore_qs'] ?? array(), 'v' => 2, ); $dir = ( defined( 'QWSC_CACHE_DIR' ) ? QWSC_CACHE_DIR : WP_CONTENT_DIR . '/cache/qw-speed-cache/' ); if ( ! is_dir( $dir ) ) { wp_mkdir_p( $dir ); } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents,PluginCheck.CodeAnalysis.WriteFile.PluginDirectoryWrite -- Runtime cache config is stored under wp-content/cache, not the plugin directory. @file_put_contents( $dir . 'dropin-config.json', wp_json_encode( $data ), LOCK_EX ); } /** * Remove our drop-in only. * * @return bool */ public static function uninstall() { if ( ! self::is_installed() ) { return true; } $path = self::dropin_path(); if ( ! file_exists( $path ) ) { return true; } wp_delete_file( $path ); return ! file_exists( $path ); } /** * @param mixed $old Old value. * @param mixed $new New value. */ public function on_settings_update( $old, $new ) { if ( ! is_array( $new ) ) { return; } $enabled = ! empty( $new['advanced_cache_enabled'] ); if ( $enabled ) { self::install(); self::write_dropin_config(); } else { self::uninstall(); } // Keep dropin config in sync when only cache_mobile / lifespan changes. if ( $enabled || self::is_installed() ) { self::write_dropin_config(); } } public function maybe_sync() { if ( ! current_user_can( 'manage_options' ) ) { return; } $s = \QWSpeedCache\Plugin::instance()->get_all_settings(); if ( ! empty( $s['advanced_cache_enabled'] ) ) { if ( ! self::is_installed() ) { self::install(); } } } /** * Locate the active wp-config.php without guessing beyond WordPress' supported layouts. * * @return string Empty string when no unambiguous config file can be found. */ public static function config_path() { $candidates = array( trailingslashit( ABSPATH ) . 'wp-config.php', dirname( untrailingslashit( ABSPATH ) ) . '/wp-config.php', ); $found = array(); foreach ( array_unique( $candidates ) as $candidate ) { if ( file_exists( $candidate ) ) { $found[] = $candidate; } } return 1 === count( $found ) ? $found[0] : ''; } /** * Read the WP_CACHE definition from wp-config.php. * * @return bool|null True/false when explicitly defined, null when absent/unknown. */ public static function config_wp_cache_value() { $path = self::config_path(); if ( ! $path ) { return null; } if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } global $wp_filesystem; if ( ! WP_Filesystem() || ! $wp_filesystem ) { return null; } $contents = $wp_filesystem->get_contents( $path ); if ( false === $contents ) { return null; } if ( preg_match( '/^[\\t ]*define\\s*\\(\\s*[\\\'\"]WP_CACHE[\\\'\"]\\s*,\\s*(true|false|1|0)\\s*\\)\\s*;/mi', $contents, $m ) ) { return in_array( strtolower( $m[1] ), array( 'true', '1' ), true ); } return null; } /** * Safely enable WP_CACHE in wp-config.php using WP_Filesystem. * The original content is retained in memory and restored if verification fails. * * @return array{ok:bool,code:string,message:string} */ public static function repair_wp_cache() { $path = self::config_path(); if ( ! $path ) { return array( 'ok' => false, 'code' => 'config_missing', 'message' => __( 'Could not locate a unique wp-config.php file.', 'qw-optimize' ) ); } if ( ! wp_is_writable( $path ) ) { return array( 'ok' => false, 'code' => 'not_writable', 'message' => __( 'wp-config.php is not writable. Add define( \'WP_CACHE\', true ); manually.', 'qw-optimize' ) ); } if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } global $wp_filesystem; if ( ! WP_Filesystem() || ! $wp_filesystem ) { return array( 'ok' => false, 'code' => 'filesystem', 'message' => __( 'WordPress could not initialize filesystem access for wp-config.php.', 'qw-optimize' ) ); } $original = $wp_filesystem->get_contents( $path ); if ( false === $original || '' === $original ) { return array( 'ok' => false, 'code' => 'read_failed', 'message' => __( 'Could not read wp-config.php safely.', 'qw-optimize' ) ); } // Do not edit ambiguous configurations with multiple WP_CACHE definitions. // Changing only the first occurrence can leave caching disabled or cause warnings. $definition_pattern = '/^[\t ]*define\s*\(\s*[\\\'"]WP_CACHE[\\\'"]\s*,/mi'; $definition_count = preg_match_all( $definition_pattern, $original ); if ( false === $definition_count || $definition_count > 1 ) { return array( 'ok' => false, 'code' => 'ambiguous_definitions', 'message' => __( 'Multiple WP_CACHE definitions were found. Update wp-config.php manually.', 'qw-optimize' ) ); } if ( 1 === $definition_count && ! preg_match( '/^[\t ]*define\s*\(\s*[\\\'"]WP_CACHE[\\\'"]\s*,\s*(?:true|false|1|0)\s*\)\s*;/mi', $original ) ) { return array( 'ok' => false, 'code' => 'unsupported_definition', 'message' => __( 'WP_CACHE uses a custom expression. Update wp-config.php manually.', 'qw-optimize' ) ); } $replacement = "define( 'WP_CACHE', true );"; $pattern = '/^[\\t ]*define\\s*\\(\\s*[\\\'\"]WP_CACHE[\\\'\"]\\s*,\\s*(?:true|false|1|0)\\s*\\)\\s*;/mi'; if ( preg_match( $pattern, $original ) ) { $updated = preg_replace( $pattern, $replacement, $original, 1 ); } else { $markers = array( "/* That's all, stop editing!", "/** Absolute path to the WordPress directory.", "require_once ABSPATH . 'wp-settings.php';", 'require_once ABSPATH . "wp-settings.php";', ); $updated = $original; $inserted = false; foreach ( $markers as $marker ) { $pos = strpos( $updated, $marker ); if ( false !== $pos ) { $updated = substr( $updated, 0, $pos ) . $replacement . "\n\n" . substr( $updated, $pos ); $inserted = true; break; } } if ( ! $inserted ) { return array( 'ok' => false, 'code' => 'layout_unknown', 'message' => __( 'wp-config.php uses an unfamiliar layout, so QW Optimize did not modify it.', 'qw-optimize' ) ); } } if ( ! is_string( $updated ) ) { return array( 'ok' => false, 'code' => 'update_failed', 'message' => __( 'Could not prepare a safe wp-config.php update.', 'qw-optimize' ) ); } if ( $updated === $original ) { return array( 'ok' => true, 'code' => 'already_set', 'message' => __( 'WP_CACHE is already enabled in wp-config.php.', 'qw-optimize' ) ); } $chmod = defined( 'FS_CHMOD_FILE' ) ? FS_CHMOD_FILE : 0644; if ( ! $wp_filesystem->put_contents( $path, $updated, $chmod ) ) { return array( 'ok' => false, 'code' => 'write_failed', 'message' => __( 'Could not update wp-config.php.', 'qw-optimize' ) ); } $verify = $wp_filesystem->get_contents( $path ); if ( false === $verify || ! preg_match( '/^[\\t ]*define\\s*\\(\\s*[\\\'\"]WP_CACHE[\\\'\"]\\s*,\\s*(?:true|1)\\s*\\)\\s*;/mi', $verify ) ) { $wp_filesystem->put_contents( $path, $original, $chmod ); return array( 'ok' => false, 'code' => 'verify_failed', 'message' => __( 'WP_CACHE verification failed; the original wp-config.php content was restored.', 'qw-optimize' ) ); } return array( 'ok' => true, 'code' => 'updated', 'message' => __( 'WP_CACHE was enabled successfully. Reload WordPress to activate early page caching.', 'qw-optimize' ) ); } /** * Admin-post handler for the explicit WP_CACHE repair action. */ public function handle_wp_cache_repair() { \QWSpeedCache\Admin\ActionGuard::require_manage( 'qwsc_fix_wp_cache' ); $result = self::repair_wp_cache(); $key = 'qwsc_wp_cache_fix_' . get_current_user_id(); set_transient( $key, $result, 60 ); wp_safe_redirect( admin_url( 'admin.php?page=qw-speed-cache-cache&qwsc_wp_cache_fix=1' ) ); exit; } /** * Status labels for admin UI. * * @return array{installed:bool,ours:bool,wp_cache:bool} */ public static function status() { $config_path = self::config_path(); $config_value = self::config_wp_cache_value(); return array( 'installed' => file_exists( self::dropin_path() ), 'ours' => self::is_installed(), 'wp_cache' => defined( 'WP_CACHE' ) && WP_CACHE, 'config_found' => (bool) $config_path, 'config_writable' => $config_path ? wp_is_writable( $config_path ) : false, 'config_wp_cache' => $config_value, 'repairable' => (bool) ( $config_path && wp_is_writable( $config_path ) && true !== $config_value ), ); } }