PATH:
home
/
ediuae
/
petalwellnessspa.com
/
wp-content
/
plugins
/
simply-schedule-appointments
/
includes
<?php /** * Simply Schedule Appointments Availability Cache. * * @since 4.0.1 * @package Simply_Schedule_Appointments */ use League\Period\Period; /** * Simply Schedule Appointments Availability Cache. * * @since 4.0.1 */ class SSA_Availability_Cache { /** * Parent plugin class. * * @since 4.0.1 * * @var Simply_Schedule_Appointments */ protected $plugin = null; const CACHE_MODE_DISABLED = 0; const CACHE_MODE_ENABLED = 10; /** * Constructor. * * @since 4.0.1 * * @param Simply_Schedule_Appointments $plugin Main plugin object. */ public function __construct( $plugin ) { $this->plugin = $plugin; $this->hooks(); } /** * Initiate our hooks. * * @since 4.0.1 */ public function hooks() { // add_action( 'ssa_populate_cache', array( $this, 'async_process_populate_cache' ), 10, 2 ); } public function generate_cache_key( $args ) { $args = $this->get_args( $args ); $args['start_date'] = '1970-01-01'; $args['end_date'] = '1970-01-01'; $availability_id = $this->plugin->availability_model->insert( $args ); $obj_cache_key = 'args:'.$args['cache_args_hash'].'/latest_cache_key'; ssa_cache_set( $obj_cache_key, $availability_id ); return $availability_id; } public function get_cache_args_hash( $args ) { return ssa_int_hash( json_encode( $args ) ); } public function get_args( $args ) { $args = shortcode_atts( array( 'appointment_type_id' => 0, 'appointment_id' => 0, 'staff_id' => 0, 'resource_group_id' => 0, 'type' => '', 'subtype' => '', 'skip_appointment_id' => '', 'cache_key' => '', 'cache_args_hash' => '', 'cache_force' => false, 'cache_level_read' => 1, 'cache_level_write' => 1, ), $args ); $args_to_hash = $args; $args_to_hash['cache_key'] = ''; $args_to_hash['cache_args_hash'] = ''; $args_to_hash['cache_level_read'] = ''; $args_to_hash['cache_level_write'] = ''; $args_to_hash['cache_force'] = false; $args['cache_args_hash'] = $this->get_cache_args_hash( $args_to_hash ); return $args; } public function get_cache_mode() { $developer_settings = $this->plugin->developer_settings->get(); if ( ! empty( $developer_settings['disable_availability_caching'] ) ) { return self::CACHE_MODE_DISABLED; } return self::CACHE_MODE_ENABLED; } public function is_cache_mode( $mode ) { return $mode === $this->get_cache_mode(); } public function is_enabled() { return ! $this->is_cache_mode( self::CACHE_MODE_DISABLED ); } public function get_latest_cache_key( $args ) { $args = $this->get_args( $args ); $obj_cache_key = 'args:'.$args['cache_args_hash'].'/latest_cache_key'; $latest_cache_key = ssa_cache_get( $obj_cache_key ); if ( false !== $latest_cache_key ) { return $latest_cache_key; } global $wpdb; $sql = 'SELECT cache_key FROM '.$this->plugin->availability_model->get_table_name().' WHERE cache_args_hash=%d ORDER BY cache_key DESC LIMIT 1'; $sql = $wpdb->prepare( $sql, $args['cache_args_hash'] ); $latest_cache_key = $wpdb->get_row( $sql, ARRAY_A ); $latest_cache_key = $latest_cache_key['cache_key']; ssa_cache_set( $obj_cache_key, $latest_cache_key ); return $latest_cache_key; } public function query( SSA_Appointment_Type_Object $appointment_type, Period $query_period, $args ) { if ( ! $this->is_enabled() ) { return; } if ( empty( $args['cache_level_read'] ) || $args['cache_level_read'] > 2 ) { return; } $appointment_type_id = ( empty( $appointment_type ) ) ? 0 : $appointment_type->id; if ( ! empty( $appointment_type_id ) || empty( $args['appointment_type_id'] ) ) { $args['appointment_type_id'] = $appointment_type_id; } $args = $this->get_args( $args ); $query_args = array( 'number' => -1, 'cache_args_hash' => $args['cache_args_hash'], 'intersects_period' => $query_period, 'order' => 'ASC', 'orderby' => 'start_date', ); // $latest_cache_key = $this->get_latest_cache_key( $args ); // if ( ! empty( $latest_cache_key ) ) { // $query_args['cache_key'] = $latest_cache_key; // } $availability_rows = $this->plugin->availability_model->query( $query_args ); $schedule = new SSA_Availability_Schedule(); $availability_blocks = array(); foreach ($availability_rows as $availability_row) { if ( $availability_row['start_date'] > $availability_row['end_date'] ) { ssa_debug_log( 'availability-cache query()', 100 ); ssa_debug_log( 'Invalid Period returned in query', 100 ); $this->plugin->availability_model->truncate(); return; } unset( $availability_row['id'] ); $start_date = $availability_row['start_date']; $availability_row = shortcode_atts( array( 'capacity_available' => '', 'capacity_reserved' => '', 'buffer_available' => '', 'buffer_reserved' => '', 'period' => new Period( $availability_row['start_date'], $availability_row['end_date'] ), ), $availability_row ); $availability_block = SSA_Availability_Block_Factory::create( $availability_row ); $availability_blocks[$start_date] = $availability_block; } $schedule = $schedule->set_blocks( $availability_blocks, true ); // added for performance October 19, 2023 // $schedule = $schedule->pushmerge( $availability_blocks ); // removed October 19, 2023 if ( $schedule->is_empty() ) { return; } $boundaries = $schedule->boundaries(); if ( empty( $boundaries ) || ! $boundaries instanceof Period ) { return; } if ( ! $boundaries->contains( $query_period ) ) { return; } if ( ! $schedule->is_continuous() ) { return; } return $schedule; } private function insert( SSA_Availability_Block $block, $args = array() ) { if ( ! $this->is_enabled() ) { return; } // $args = $this->get_args( $args ); // <--- not needed if insert() remains a private function $args = array_merge( $args, array( 'start_date' => $block->get_period()->getStartDate()->format( 'Y-m-d H:i:s' ), 'end_date' => $block->get_period()->getEndDate()->format( 'Y-m-d H:i:s' ), 'capacity_reserved' => $block->capacity_reserved, 'capacity_available' => $block->capacity_available, ) ); $availability_id = $this->plugin->availability_model->db_insert( $args ); } public function insert_schedule( SSA_Availability_Schedule $schedule, $args = array() ) { if ( ! $this->is_enabled() ) { return; } if ( empty( $args['cache_level_write'] ) || $args['cache_level_write'] > 2 ) { return; } $args = $this->get_args( $args ); unset( $args['cache_level_read'] ); unset( $args['cache_level_write'] ); $args['cache_key'] = $this->generate_cache_key( $args ); $availability_rows = array(); foreach ($schedule->get_blocks() as $block) { $availability_rows[] = array_merge( $args, array( 'start_date' => $block->get_period()->getStartDate()->format( 'Y-m-d H:i:s' ), 'end_date' => $block->get_period()->getEndDate()->format( 'Y-m-d H:i:s' ), 'capacity_reserved' => $block->capacity_reserved, 'capacity_available' => $block->capacity_available, 'buffer_reserved' => $block->buffer_reserved, 'buffer_available' => $block->buffer_available, ) ); } $this->plugin->availability_model->db_bulk_insert( $availability_rows ); $this->delete_schedule( $schedule, $args['cache_args_hash'], $args['cache_key'] ); } public function delete_schedule( SSA_Availability_Schedule $schedule, $cache_args_hash, $below_this_cache_key = null ) { $boundaries = $schedule->boundaries(); if ( empty( $boundaries ) ) { return; } global $wpdb; $start_date_string = $boundaries->getStartDate()->format( 'Y-m-d H:i:s' ); $end_date_string = $boundaries->getEndDate()->format( 'Y-m-d H:i:s' ); $sql = 'DELETE FROM '.$this->plugin->availability_model->get_table_name()." WHERE cache_args_hash=%d AND ( (end_date > '{$start_date_string}' AND end_date < '{$end_date_string}' ) OR (start_date < '{$end_date_string}' AND start_date > '{$start_date_string}' ) OR (start_date < '{$start_date_string}' AND end_date > '{$end_date_string}' ) )"; // same as intersects_period, except we use < rather than <= (so we don't delete the neighboring availability). This will delete any availability cache that starts in the period OR ends in the period OR contains the entire period $sql = $wpdb->prepare( $sql, array( $cache_args_hash, ) ); if ( empty( $below_this_cache_key ) ) { $this->wpdb_query_while_preventing_deadlock($sql); return; } $sql .= $wpdb->prepare( ' AND cache_key < %d', $below_this_cache_key ); $this->wpdb_query_while_preventing_deadlock($sql); $sql = 'DELETE FROM '.$this->plugin->availability_model->get_table_name().' WHERE id=%d'; $sql = $wpdb->prepare( $sql, $below_this_cache_key ); $this->wpdb_query_while_preventing_deadlock( $sql ); } private function wpdb_query_while_preventing_deadlock( $sql ) { global $wpdb; // This query can potentially be deadlocked if there is // high concurrency, in which case DB will abort the query which has done less work to resolve deadlock. // We will try up to 3 times before giving up. for ($count = 0; $count < 10; $count++) { $result = $wpdb->query( $sql ); // WPCS: unprepared SQL ok. if ( false !== $result ) { if( $count > 0 ) { ssa_debug_log("Deadlock successfully resolved after trying " . ( $count + 1 ) . " times"); } break; } // sleep 0, 1 or 2 seconds randomly sleep( rand( 0, 2 ) ); } if ( false === $result ) { ssa_debug_log("Deadlock could not be resolved after trying 10 times. Query: " . $sql); } return $result; } public static function object_cache_get( $key, $group = '', $force = false, &$found = null ) { if ( ! ssa()->availability_cache->is_enabled() ) { return false; } $group .= SSA_Availability_Cache_Invalidation::get_cache_group(); $key = 'ssa/' . $group . '/' . $key; return get_transient( $key ); } public static function object_cache_set( $key, $data, $group = '', $expire = 0 ) { if ( ! ssa()->availability_cache->is_enabled() ) { return false; } $group .= SSA_Availability_Cache_Invalidation::get_cache_group(); $key = 'ssa/' . $group . '/' . $key; if ( empty( $expire ) ) { $expire = WEEK_IN_SECONDS; } set_transient( $key, $data, $expire ); return true; } public function remember_recent( $key, $value, $number_to_remember = 10 ) { $key = 'ssa/recent_'.$key; $recent_values = get_transient( $key ); if ( empty( $recent_values ) ) { $recent_values = array(); } $recent_values[] = $value; if ( count( $recent_values ) > $number_to_remember ) { array_shift( $recent_values ); } set_transient( $key, $recent_values, MONTH_IN_SECONDS ); } public function async_process_populate_cache( $payload, $async_action ) { // this won't be called until re-enabled in hooks() $start = microtime(true); $this->populate_cache(); $end = microtime(true); ssa_complete_action( $async_action['id'], 'Execution time: '.( $end - $start ) . 's' ); } public function populate_cache() { return; // disable if ( ! $this->is_enabled() ) { return; } $developer_settings = $this->plugin->developer_settings->get(); if ( empty( $developer_settings['populate_cache'] ) ) { return; } $recent_availability_query_args = get_transient( 'ssa/recent_availability_query_args' ); if ( empty( $recent_availability_query_args ) ) { return; } $recent_availability_query_args = array_reverse( $recent_availability_query_args ); // so start with the most recent queries $hashes_to_process = array(); $availability_query_args_to_process = array(); foreach ($recent_availability_query_args as $value) { if ( in_array( $value['query_hash'], $hashes_to_process ) ) { continue; } $query_hash = $value['query_hash']; $hashes_to_process[] = $query_hash; set_transient( 'ssa/cache/lock_'.$query_hash, true, 30 ); unset( $value['query_hash'] ); $availability_query_args_to_process[$query_hash] = $value; if ( count( $availability_query_args_to_process ) >= 5 ) { break; } } set_transient( 'ssa/cache/lock_global', false, 0 ); // once we've locked individual queries, unlock the global foreach ($availability_query_args_to_process as $query_hash => $value) { $appointment_type = new SSA_Appointment_Type_Object( $value['appointment_type_id'] ); $availability_query = new SSA_Availability_Query( $appointment_type, $value['period'], $value['args'] ); $bookable_start_datetime_strings = $availability_query->get_bookable_appointment_start_datetime_strings(); set_transient( 'ssa/cache/lock_'.$query_hash, false, 5 ); } } public static function delete_expired_transients( $force_db = false ) { global $wpdb; if ( ! $force_db && wp_using_ext_object_cache() ) { return; } $wpdb->query( $wpdb->prepare( "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b WHERE a.option_name LIKE %s AND a.option_name NOT LIKE %s AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) AND b.option_value < %d", $wpdb->esc_like( '_transient_ssa/' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) ); if ( ! is_multisite() ) { // Single site stores site transients in the options table. $wpdb->query( $wpdb->prepare( "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b WHERE a.option_name LIKE %s AND a.option_name NOT LIKE %s AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) ) AND b.option_value < %d", $wpdb->esc_like( '_site_transient_ssa/' ) . '%', $wpdb->esc_like( '_site_transient_timeout_' ) . '%', time() ) ); } elseif ( is_multisite() && is_main_site() && is_main_network() ) { // Multisite stores site transients in the sitemeta table. $wpdb->query( $wpdb->prepare( "DELETE a, b FROM {$wpdb->sitemeta} a, {$wpdb->sitemeta} b WHERE a.meta_key LIKE %s AND a.meta_key NOT LIKE %s AND b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING( a.meta_key, 17 ) ) AND b.meta_value < %d", $wpdb->esc_like( '_site_transient_ssa/' ) . '%', $wpdb->esc_like( '_site_transient_timeout_' ) . '%', time() ) ); } } public static function delete_all_transients( $force_db = false ) { global $wpdb; if ( ! $force_db && wp_using_ext_object_cache() ) { return; } $wpdb->query( $wpdb->prepare( "DELETE a FROM {$wpdb->options} a WHERE (a.option_name LIKE %s OR a.option_name LIKE %s)", $wpdb->esc_like( '_transient_ssa/v' ) . '%', $wpdb->esc_like( '_transient_timeout_ssa/v' ) . '%' ) ); if ( ! is_multisite() ) { // Single site stores site transients in the options table. $wpdb->query( $wpdb->prepare( "DELETE a FROM {$wpdb->options} a WHERE a.option_name LIKE %s OR a.option_name LIKE %s", $wpdb->esc_like( '_site_transient_ssa/v' ) . '%', $wpdb->esc_like( '_site_transient_timeout_ssa/v' ) . '%' ) ); } elseif ( is_multisite() && is_main_site() && is_main_network() ) { // Multisite stores site transients in the sitemeta table. $wpdb->query( $wpdb->prepare( "DELETE a FROM {$wpdb->sitemeta} a WHERE a.meta_key LIKE %s OR a.meta_key LIKE %s", $wpdb->esc_like( '_site_transient_ssa/v' ) . '%', $wpdb->esc_like( '_site_transient_timeout_ssa/v' ) . '%' ) ); } } }
[-] class-settings.php
[edit]
[-] class-hooks.php
[edit]
[-] class-availability-query.php
[edit]
[-] class-appointment-model.php
[edit]
[-] class-beaver-builder.php
[edit]
[-] class-web-meetings.php
[edit]
[-] class-availability-model.php
[edit]
[-] class-recipient-admin.php
[edit]
[-] class-recipient-customer.php
[edit]
[-] class-constants.php
[edit]
[-] class-async-action-model.php
[edit]
[-] class-support.php
[edit]
[-] class-google-calendar-client.php
[edit]
[+]
..
[-] class-capabilities.php
[edit]
[-] class-encryption.php
[edit]
[-] class-availability-cache-invalidation.php
[edit]
[-] class-calendar-events-settings.php
[edit]
[-] class-settings-global.php
[edit]
[-] class-translation.php
[edit]
[-] class-cli.php
[edit]
[-] class-missing.php
[edit]
[-] class-action-scheduler.php
[edit]
[-] class-templates-api.php
[edit]
[-] class-blackout-dates.php
[edit]
[-] class-calendar-events-object.php
[edit]
[-] class-divi.php
[edit]
[-] class-templates.php
[edit]
[-] class-wp-admin.php
[edit]
[-] class-appointment-factory.php
[edit]
[-] class-scheduling-max-per-day.php
[edit]
[-] class-notifications-api.php
[edit]
[-] class-shortcodes.php
[edit]
[-] class-validation.php
[edit]
[-] class-resource-group-object-factory.php
[edit]
[-] class-payment-object.php
[edit]
[-] class-locale.php
[edit]
[-] class-ics-exporter.php
[edit]
[-] class-filesystem.php
[edit]
[-] class-recipient-shared.php
[edit]
[-] class-availability-schedule.php
[edit]
[-] class-block-upcoming-appointments.php
[edit]
[-] class-elementor.php
[edit]
[-] class-appointment-meta-model.php
[edit]
[-] class-revision-meta-model.php
[edit]
[+]
divi
[-] class-notifications.php
[edit]
[-] class-upgrade.php
[edit]
[-] class-utils.php
[edit]
[-] class-availability-functions.php
[edit]
[-] class-external-calendar-api.php
[edit]
[-] class-csv-exporter.php
[edit]
[-] class-external-google-calendar-api.php
[edit]
[-] class-twig-extension.php
[edit]
[-] class-bootstrap.php
[edit]
[-] class-notices.php
[edit]
[-] class-availability-schedule-factory.php
[edit]
[-] class-blackout-dates-settings.php
[edit]
[-] class-support-status.php
[edit]
[-] class-block-booking.php
[edit]
[-] class-advanced-scheduling-availability.php
[edit]
[-] class-exception.php
[edit]
[-] class-styles-settings.php
[edit]
[-] class-forms.php
[edit]
[-] class-recipient-staff.php
[edit]
[+]
beaver-builder
[-] class-users.php
[edit]
[-] class-sequence-factory.php
[edit]
[+]
third-party
[-] class-locales.php
[edit]
[-] class-db-model.php
[edit]
[-] class-db.php
[edit]
[-] class-availability-default.php
[edit]
[-] class-staff-settings.php
[edit]
[-] class-notification-model.php
[edit]
[-] class-settings-installed.php
[edit]
[-] class-appointment-object.php
[edit]
[-] class-notices-api.php
[edit]
[-] class-translation-settings.php
[edit]
[-] class-support-status-api.php
[edit]
[-] class-notices-data.php
[edit]
[-] class-appointment-type-object.php
[edit]
[-] class-appointment-type-object-factory.php
[edit]
[-] class-availability-block-factory.php
[edit]
[-] class-recipient.php
[edit]
[-] class-revision-model.php
[edit]
[-] class-customers.php
[edit]
[-] class-settings-api.php
[edit]
[-] class-cache.php
[edit]
[-] class-availability-detective-case.php
[edit]
[-] class-styles.php
[edit]
[-] class-capacity-settings.php
[edit]
[-] class-gcal-exporter.php
[edit]
[-] class-advanced-scheduling-settings.php
[edit]
[-] class-appointment-type-model.php
[edit]
[-] class-notifications-settings.php
[edit]
[-] class-period.php
[edit]
[-] class-external-api.php
[edit]
[-] class-error-notices.php
[edit]
[-] class-availability-cache.php
[edit]
[-] class-developer-settings.php
[edit]
[-] class-dashboard-upcoming-appointments-widget.php
[edit]
[-] class-availability-external-model.php
[edit]
[-] class-availability-block.php
[edit]
[-] class-customer-information.php
[edit]
[+]
elementor
[-] class-appointment-type-label-model.php
[edit]
[-] class-embed-booking-app-api.php
[edit]
[+]
lib
[-] class-sequence.php
[edit]
[-] class-debug.php
[edit]
[-] class-appointment-types-db.php
[edit]