cache_expiry = intval( $settings['cache_expiry'] ); } if ( isset( $settings['enable_cache'] ) ) { $this->use_db_cache = (bool) $settings['enable_cache']; } // Schedule cache cleanup. add_action( 'dp_aicoso_cleanup_cache', array( $this, 'cleanup_expired_cache' ) ); } /** * Get cached value. * * @param string $key Cache key. * @return mixed|false Cached value or false. */ public function get( $key ) { // Try object cache first. $value = wp_cache_get( $key, $this->cache_group ); if ( false !== $value ) { return $value; } // Try transient cache. $value = get_transient( $key ); if ( false !== $value ) { // Also set in object cache for this request. wp_cache_set( $key, $value, $this->cache_group, $this->cache_expiry ); return $value; } // Try database cache if enabled. if ( $this->use_db_cache ) { $value = $this->get_from_database( $key ); if ( false !== $value ) { // Set in faster caches. wp_cache_set( $key, $value, $this->cache_group, $this->cache_expiry ); set_transient( $key, $value, $this->cache_expiry ); return $value; } } return false; } /** * Set cache value. * * @param string $key Cache key. * @param mixed $value Value to cache. * @param int $expiry Expiry time in seconds (optional). * @return bool Success. */ public function set( $key, $value, $expiry = null ) { if ( null === $expiry ) { $expiry = $this->cache_expiry; } // Set in object cache. wp_cache_set( $key, $value, $this->cache_group, $expiry ); // Set in transient cache. set_transient( $key, $value, $expiry ); // Set in database cache if enabled. if ( $this->use_db_cache ) { $this->set_in_database( $key, $value, $expiry ); } return true; } /** * Delete cached value. * * @param string $key Cache key. * @return bool Success. */ public function delete( $key ) { // Delete from object cache. wp_cache_delete( $key, $this->cache_group ); // Delete transient. delete_transient( $key ); // Delete from database. if ( $this->use_db_cache ) { $this->delete_from_database( $key ); } return true; } /** * Flush all cache. */ public function flush() { // Clear object cache for our group. wp_cache_flush_group( $this->cache_group ); // Clear transients. global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache clearing operation $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", '_transient_dp_aicoso_%', '_transient_timeout_dp_aicoso_%' ) ); // Clear database cache. if ( $this->use_db_cache ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- Table truncation operation, table name is hardcoded $wpdb->query( 'TRUNCATE TABLE `' . $wpdb->prefix . 'dp_aicoso_price_cache`' ); } } /** * Get value from database cache. * * @param string $key Cache key. * @return mixed|false Cached value or false. */ private function get_from_database( $key ) { global $wpdb; // Using hardcoded table reference to avoid PHPCS issues. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache retrieval operation $result = $wpdb->get_row( $wpdb->prepare( 'SELECT calculated_price, expires_at FROM `' . $wpdb->prefix . 'dp_aicoso_price_cache` WHERE cache_key = %s AND expires_at > %s LIMIT 1', $key, current_time( 'mysql' ) ) ); if ( $result ) { return floatval( $result->calculated_price ); } return false; } /** * Set value in database cache. * * @param string $key Cache key. * @param mixed $value Value to cache. * @param int $expiry Expiry time in seconds. */ private function set_in_database( $key, $value, $expiry ) { global $wpdb; // Calculate expiry time. $expires_at = gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) + $expiry ); // Extract context from key if possible. $product_id = 0; $user_role = 'guest'; $quantity = 1; // Try to parse key for context (simplified). if ( preg_match( '/price_(\d+)_(\d+)_([^_]+)/', $key, $matches ) ) { $product_id = intval( $matches[1] ); $quantity = intval( $matches[2] ); $user_role = sanitize_text_field( $matches[3] ); } // Insert or update cache entry. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation for cache storage $wpdb->replace( $wpdb->prefix . 'dp_aicoso_price_cache', array( 'cache_key' => $key, 'product_id' => $product_id, 'user_role' => $user_role, 'quantity' => $quantity, 'calculated_price' => floatval( $value ), 'original_price' => 0, // Would need to be passed in for accuracy. 'applied_rules' => '', // Would need to be passed in. 'expires_at' => $expires_at, ), array( '%s', '%d', '%s', '%d', '%f', '%f', '%s', '%s' ) ); } /** * Delete value from database cache. * * @param string $key Cache key. */ private function delete_from_database( $key ) { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation for cache deletion $wpdb->delete( $wpdb->prefix . 'dp_aicoso_price_cache', array( 'cache_key' => $key ), array( '%s' ) ); } /** * Cleanup expired cache entries. */ public function cleanup_expired_cache() { if ( ! $this->use_db_cache ) { return; } global $wpdb; // Delete expired entries. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache maintenance operation $wpdb->query( $wpdb->prepare( 'DELETE FROM `' . $wpdb->prefix . 'dp_aicoso_price_cache` WHERE expires_at < %s', current_time( 'mysql' ) ) ); // Log cleanup. if ( $wpdb->rows_affected > 0 ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for cache cleanup error_log( sprintf( 'DP AICOSO: Cleaned up %d expired cache entries', $wpdb->rows_affected ) ); } } /** * Clear cache for specific product. * * @param int $product_id Product ID. */ public function clear_product_cache( $product_id ) { global $wpdb; // Clear transients. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache clearing operation $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_dp_aicoso_%_' . $product_id . '_%' ) ); // Clear database cache. if ( $this->use_db_cache ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation for cache clearing $wpdb->delete( $wpdb->prefix . 'dp_aicoso_price_cache', array( 'product_id' => $product_id ), array( '%d' ) ); } } /** * Clear cache for specific user role. * * @param string $role User role. */ public function clear_role_cache( $role ) { global $wpdb; // Clear transients. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Cache clearing operation $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_dp_aicoso_%_' . $role . '_%' ) ); // Clear database cache. if ( $this->use_db_cache ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Write operation for cache clearing $wpdb->delete( $wpdb->prefix . 'dp_aicoso_price_cache', array( 'user_role' => $role ), array( '%s' ) ); } } /** * Get cache statistics. * * @return array Cache statistics. */ public function get_stats() { global $wpdb; $stats = array( 'total_entries' => 0, 'expired_entries' => 0, 'cache_size' => 0, 'hit_rate' => 0, ); if ( $this->use_db_cache ) { // Total entries. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Stats query $stats['total_entries'] = $wpdb->get_var( 'SELECT COUNT(*) FROM `' . $wpdb->prefix . 'dp_aicoso_price_cache`' ); // Expired entries. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Stats query $stats['expired_entries'] = $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM `' . $wpdb->prefix . 'dp_aicoso_price_cache` WHERE expires_at < %s', current_time( 'mysql' ) ) ); // Cache table size. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Stats query for cache information $table_status = $wpdb->get_row( $wpdb->prepare( 'SELECT ' . 'ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb ' . 'FROM information_schema.TABLES ' . 'WHERE table_schema = %s ' . 'AND table_name = %s', DB_NAME, $wpdb->prefix . 'dp_aicoso_price_cache' ) ); if ( $table_status ) { $stats['cache_size'] = $table_status->size_mb . ' MB'; } } return $stats; } }