'hvac_form_data_', 'venue_search' => 'hvac_venue_search_', 'organizer_data' => 'hvac_organizer_data_', 'event_meta' => 'hvac_event_meta_', 'timezone_list' => 'hvac_timezone_list', 'trainer_opts' => 'hvac_trainer_options', 'cert_levels' => 'hvac_cert_levels', ]; /** * Default cache expiration times (in seconds) * * @var array */ private array $cache_expiration = [ 'form_data' => 300, // 5 minutes (temporary form data) 'venue_search' => 1800, // 30 minutes 'organizer_data' => 3600, // 1 hour 'event_meta' => 1800, // 30 minutes 'timezone_list' => 86400, // 24 hours 'trainer_opts' => 3600, // 1 hour 'cert_levels' => 3600, // 1 hour ]; /** * Constructor */ private function __construct() { $this->init_hooks(); } /** * Initialize WordPress hooks */ private function init_hooks(): void { // Clear caches when events are updated add_action('save_post_tribe_events', [$this, 'clear_event_cache'], 10, 2); add_action('delete_post', [$this, 'clear_event_cache_on_delete'], 10); // Clear venue/organizer caches when those are updated add_action('save_post_tribe_venue', [$this, 'clear_venue_cache'], 10, 2); add_action('save_post_tribe_organizer', [$this, 'clear_organizer_cache'], 10, 2); // AJAX endpoints for cache management add_action('wp_ajax_hvac_clear_cache', [$this, 'ajax_clear_cache']); add_action('wp_ajax_hvac_warm_cache', [$this, 'ajax_warm_cache']); } /** * Get cached data * * @param string $type Cache type from prefixes * @param string $key Unique identifier * @param callable|null $callback Callback to generate data if not cached * @return mixed Cached data or false if not found */ public function get(string $type, string $key, ?callable $callback = null): mixed { if (!isset($this->cache_prefixes[$type])) { return false; } $cache_key = $this->cache_prefixes[$type] . $this->sanitize_cache_key($key); $cached_data = get_transient($cache_key); if ($cached_data !== false) { return $cached_data; } // If callback provided and no cached data, generate and cache it if ($callback && is_callable($callback)) { $data = call_user_func($callback); $this->set($type, $key, $data); return $data; } return false; } /** * Set cached data * * @param string $type Cache type from prefixes * @param string $key Unique identifier * @param mixed $data Data to cache * @param int|null $expiration Custom expiration time * @return bool Success status */ public function set(string $type, string $key, mixed $data, ?int $expiration = null): bool { if (!isset($this->cache_prefixes[$type])) { return false; } $cache_key = $this->cache_prefixes[$type] . $this->sanitize_cache_key($key); $expiration = $expiration ?? $this->cache_expiration[$type]; return set_transient($cache_key, $data, $expiration); } /** * Delete cached data * * @param string $type Cache type from prefixes * @param string $key Unique identifier * @return bool Success status */ public function delete(string $type, string $key): bool { if (!isset($this->cache_prefixes[$type])) { return false; } $cache_key = $this->cache_prefixes[$type] . $this->sanitize_cache_key($key); return delete_transient($cache_key); } /** * Clear all cache entries for a specific type * * @param string $type Cache type to clear * @return bool Success status */ public function clear_type(string $type): bool { if (!isset($this->cache_prefixes[$type])) { return false; } global $wpdb; $prefix = $this->cache_prefixes[$type]; $transient_option = "_transient_{$prefix}%"; $transient_timeout = "_transient_timeout_{$prefix}%"; // Delete all transients matching the prefix $deleted_options = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $transient_option, $transient_timeout ) ); return $deleted_options !== false; } /** * Clear all HVAC event caches * * @return bool Success status */ public function clear_all(): bool { $success = true; foreach (array_keys($this->cache_prefixes) as $type) { if (!$this->clear_type($type)) { $success = false; } } return $success; } /** * Cache form data temporarily (for form repopulation on errors) * * @param string $form_id Unique form identifier * @param array $form_data Form data to cache * @return bool Success status */ public function cache_form_data(string $form_id, array $form_data): bool { // Remove sensitive data before caching $safe_data = $this->sanitize_form_data_for_cache($form_data); return $this->set('form_data', $form_id, $safe_data, 300); // 5 minutes } /** * Get cached form data * * @param string $form_id Unique form identifier * @return array|false Form data or false if not found */ public function get_form_data(string $form_id): array|false { return $this->get('form_data', $form_id); } /** * Cache venue search results * * @param string $search_query Search query * @param array $results Search results * @return bool Success status */ public function cache_venue_search(string $search_query, array $results): bool { return $this->set('venue_search', $search_query, $results); } /** * Get cached venue search results * * @param string $search_query Search query * @return array|false Search results or false if not found */ public function get_venue_search(string $search_query): array|false { return $this->get('venue_search', $search_query); } /** * Cache organizer data * * @param int $organizer_id Organizer post ID * @param array $organizer_data Organizer data * @return bool Success status */ public function cache_organizer_data(int $organizer_id, array $organizer_data): bool { return $this->set('organizer_data', (string)$organizer_id, $organizer_data); } /** * Get cached organizer data * * @param int $organizer_id Organizer post ID * @return array|false Organizer data or false if not found */ public function get_organizer_data(int $organizer_id): array|false { return $this->get('organizer_data', (string)$organizer_id); } /** * Cache timezone list * * @param array $timezone_list WordPress timezone options * @return bool Success status */ public function cache_timezone_list(array $timezone_list): bool { return $this->set('timezone_list', 'wordpress', $timezone_list); } /** * Get cached timezone list * * @return array|false Timezone list or false if not found */ public function get_timezone_list(): array|false { return $this->get('timezone_list', 'wordpress'); } /** * Cache trainer requirement options * * @param array $options Trainer requirement options * @return bool Success status */ public function cache_trainer_options(array $options): bool { return $this->set('trainer_opts', 'requirements', $options); } /** * Get cached trainer requirement options * * @return array|false Options or false if not found */ public function get_trainer_options(): array|false { return $this->get('trainer_opts', 'requirements'); } /** * Cache certification level options * * @param array $options Certification level options * @return bool Success status */ public function cache_cert_levels(array $options): bool { return $this->set('cert_levels', 'levels', $options); } /** * Get cached certification level options * * @return array|false Options or false if not found */ public function get_cert_levels(): array|false { return $this->get('cert_levels', 'levels'); } /** * Warm up frequently used caches * * @return bool Success status */ public function warm_cache(): bool { $success = true; // Warm timezone cache if (!$this->get_timezone_list()) { $zones = wp_timezone_choice('UTC'); $timezone_options = []; if (preg_match_all('/