Current Path : /var/www/ooareogundevinitiative/wp-content/plugins/give/includes/ |
Current File : /var/www/ooareogundevinitiative/wp-content/plugins/give/includes/class-give-background-updater.php |
<?php /** * Background Updater * * Uses https://github.com/A5hleyRich/wp-background-processing to handle DB * updates in the background. * * @class Give_Background_Updater * @version 2.0.0 * @package Give/Classes * @category Class * @author GiveWP */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Give_Background_Updater Class. */ class Give_Background_Updater extends WP_Background_Process { /** * @var string */ protected $action = 'give_db_updater'; /** * Dispatch updater. * * Updater will still run via cron job if this fails for any reason. */ public function dispatch() { if ( give_test_ajax_works() ) { parent::dispatch(); } elseif ( wp_doing_ajax() ) { $this->maybe_handle(); } } /** * Get all batches. * * @since 2.0 * @access public * @return stdClass */ public function get_all_batch() { return $this->get_batch(); } /** * Is queue empty * * @since 2.0.3 * * @return bool */ public function has_queue() { return ( ! $this->is_queue_empty() ); } /** * Lock process * * Lock the process so that multiple instances can't run simultaneously. * Override if applicable, but the duration should be greater than that * defined in the time_exceeded() method. * * @since 2.0.3 */ protected function lock_process() { // Check if admin want to pause upgrade. if ( get_option( 'give_pause_upgrade' ) ) { self::flush_cache(); delete_option( 'give_paused_batches' ); Give_Updates::get_instance()->__pause_db_update( true ); delete_option( 'give_pause_upgrade' ); /** * Fire action when pause db updates * * @since 2.0.1 */ do_action( 'give_pause_db_upgrade', Give_Updates::get_instance() ); wp_die(); } $this->start_time = time(); // Set start time of current process. $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration ); set_transient( $this->identifier . '_process_lock', microtime(), $lock_duration ); } /** * Handle cron healthcheck * * Restart the background process if not already running * and data exists in the queue. */ public function handle_cron_healthcheck() { if ( $this->is_process_running() || $this->is_paused_process() ) { // Background process already running. return; } if ( $this->is_queue_empty() ) { // No data to process. $this->clear_scheduled_event(); return; } $this->handle(); } /** * Schedule fallback event. */ protected function schedule_event() { if ( ! wp_next_scheduled( $this->cron_hook_identifier ) && ! $this->is_paused_process() ) { wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier ); } } /** * Is queue empty * * @since 2.4.5 * * @return bool */ protected function is_queue_empty() { global $wpdb; $table = $wpdb->options; $column = 'option_name'; $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%'; $count = $wpdb->get_var( $wpdb->prepare( " SELECT COUNT(*) FROM {$table} WHERE {$column} LIKE %s ", $key ) ); return ! ( $count > 0 ); } /** * Get batch * * @since 2.4.5 * * @return stdClass Return the first batch from the queue */ protected function get_batch() { global $wpdb; $table = $wpdb->options; $column = 'option_name'; $key_column = 'option_id'; $value_column = 'option_value'; $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%'; $query = $wpdb->get_row( $wpdb->prepare( " SELECT * FROM {$table} WHERE {$column} LIKE %s ORDER BY {$key_column} ASC LIMIT 1 ", $key ) ); $batch = new stdClass(); $batch->key = $query->$column; $batch->data = maybe_unserialize( $query->$value_column ); return $batch; } /** * Save queue * * @since 2.4.5 * * @return $this */ public function save() { $key = $this->generate_key(); if ( ! empty( $this->data ) ) { update_option( $key, $this->data ); } return $this; } /** * Update queue * * @since 2.4.5 * * @param string $key Key. * @param array $data Data. * * @return $this */ public function update( $key, $data ) { if ( ! empty( $data ) ) { update_option( $key, $data ); } return $this; } /** * Delete queue * * @since 2.4.5 * * @param string $key Key. * * @return $this */ public function delete( $key ) { delete_option( $key ); return $this; } /** * Is process running * * @since 2.4.5 * * Check whether the current process is already running * in a background process. */ public function is_process_running() { if ( get_transient( $this->identifier . '_process_lock' ) ) { // Process already running. return true; } return false; } /** * Unlock process * * Unlock the process so that other instances can spawn. * * @since 2.4.5 * * @return $this */ protected function unlock_process() { delete_transient( $this->identifier . '_process_lock' ); return $this; } /** * Task * * Override this method to perform any actions required on each * queue item. Return the modified item for further processing * in the next pass through. Or, return false to remove the * item from the queue. * * @param array $update Update info * * @return mixed */ protected function task( $update ) { // Pause upgrade immediately if admin pausing upgrades. if ( $this->is_paused_process() ) { wp_die(); } if ( empty( $update ) ) { return false; } // Delete cache. self::flush_cache(); /* @var Give_Updates $give_updates */ $give_updates = Give_Updates::get_instance(); $resume_update = get_option( 'give_doing_upgrade', // Default update. array( 'update_info' => $update, 'step' => 1, 'update' => 1, 'heading' => sprintf( 'Update %s of {update_count}', 1 ), 'percentage' => $give_updates->percentage, 'total_percentage' => 0, ) ); // Continuously skip update if previous update does not complete yet. if ( $resume_update['update_info']['id'] !== $update['id'] && ! give_has_upgrade_completed( $resume_update['update_info']['id'] ) ) { return $update; } // Set params. $resume_update['update_info'] = $update; $give_updates->step = absint( $resume_update['step'] ); $give_updates->update = absint( $resume_update['update'] ); $is_parent_update_completed = $give_updates->is_parent_updates_completed( $update ); // Skip update if dependency update does not complete yet. if ( empty( $is_parent_update_completed ) ) { // @todo: set error when you have only one update with invalid dependency if ( ! is_null( $is_parent_update_completed ) ) { return $update; } return false; } // Pause upgrade immediately if found following: // 1. Running update number greater then total update count // 2. Processing percentage greater then 100% if ( ( 101 < $resume_update['total_percentage'] ) || ( $give_updates->get_total_db_update_count() < $resume_update['update'] ) || ! in_array( $resume_update['update_info']['id'], $give_updates->get_update_ids() ) ) { if ( ! $this->is_paused_process() ) { $give_updates->__pause_db_update( true ); } update_option( 'give_upgrade_error', 1, false ); $log_data = 'Update Task' . "\n"; $log_data .= "Total update count: {$give_updates->get_total_db_update_count()}\n"; $log_data .= 'Update IDs: ' . print_r( $give_updates->get_update_ids(), true ); $log_data .= 'Update: ' . print_r( $resume_update, true ); Give()->logs->add( 'Update Error', $log_data, 0, 'update' ); wp_die(); } // Disable cache. Give_Cache::disable(); try { // Run update. if ( is_array( $update['callback'] ) ) { $object = $update['callback'][0]; $method_name = $update['callback'][1]; $object->$method_name(); } else { $update['callback'](); } } catch ( Exception $e ) { if ( ! $this->is_paused_process() ) { $give_updates->__pause_db_update( true ); } $log_data = 'Update Task' . "\n"; $log_data .= print_r( $resume_update, true ) . "\n\n"; $log_data .= "Error\n {$e->getMessage()}"; Give()->logs->add( 'Update Error', $log_data, 0, 'update' ); update_option( 'give_upgrade_error', 1, false ); wp_die(); } // Set update info. $doing_upgrade_args = array( 'update_info' => $update, 'step' => ++ $give_updates->step, 'update' => $give_updates->update, 'heading' => sprintf( 'Update %s of %s', $give_updates->update, get_option( 'give_db_update_count' ) ), 'percentage' => $give_updates->percentage, 'total_percentage' => $give_updates->get_db_update_processing_percentage(), ); // Cache upgrade. update_option( 'give_doing_upgrade', $doing_upgrade_args, false ); // Enable cache. Give_Cache::enable(); // Check if current update completed or not. if ( give_has_upgrade_completed( $update['id'] ) ) { return false; } return $update; } /** * Complete * * Override if applicable, but ensure that the below actions are * performed, or, call parent::complete(). */ public function complete() { if ( $this->is_paused_process() ) { return false; } parent::complete(); delete_option( 'give_pause_upgrade' ); delete_option( 'give_upgrade_error' ); delete_option( 'give_db_update_count' ); delete_option( 'give_doing_upgrade' ); add_option( 'give_show_db_upgrade_complete_notice', 1, '', false ); // Flush cache. Give_Cache::flush_cache( true ); if ( $cache_keys = Give_Cache::get_options_like( '' ) ) { Give_Cache::delete( $cache_keys ); } } /** * Get memory limit * * @return int */ protected function get_memory_limit() { if ( function_exists( 'ini_get' ) ) { $memory_limit = ini_get( 'memory_limit' ); } else { // Sensible default. $memory_limit = '128M'; } if ( ! $memory_limit || '-1' === $memory_limit ) { // Unlimited, set to 32GB. $memory_limit = '32G'; } return give_let_to_num( $memory_limit ); } /** * Maybe process queue * * Checks whether data exists within the queue and that * the process is not already running. */ public function maybe_handle() { // Don't lock up other requests while processing session_write_close(); if ( $this->is_process_running() || $this->is_paused_process() ) { // Background process already running. wp_die(); } if ( $this->is_queue_empty() ) { // No data to process. wp_die(); } check_ajax_referer( $this->identifier, 'nonce' ); $this->handle(); wp_die(); } /** * Handle * * Pass each queue item to the task handler, while remaining * within server memory and time limit constraints. */ protected function handle() { $this->lock_process(); do { $batch = $this->get_batch(); foreach ( $batch->data as $key => $value ) { $task = $this->task( $value ); if ( false !== $task ) { $batch->data[ $key ] = $task; } else { unset( $batch->data[ $key ] ); } if ( $this->time_exceeded() || $this->memory_exceeded() ) { // Batch limits reached. break; } } // Update or delete current batch. if ( ! empty( $batch->data ) ) { $this->update( $batch->key, $batch->data ); } else { $this->delete( $batch->key ); } } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() ); $this->unlock_process(); // Start next batch or complete process. if ( ! $this->is_queue_empty() ) { // Dispatch only if ajax works. if ( give_test_ajax_works() ) { $this->dispatch(); } } else { $this->complete(); } wp_die(); } /** * Check if backgound upgrade paused or not. * * @since 2.0 * @access public * @return bool */ public function is_paused_process() { // Delete cache. wp_cache_delete( 'give_paused_batches', 'options' ); $paused_batches = Give_Cache_Setting::get_option( 'give_paused_batches' ); return ! empty( $paused_batches ); } /** * Get identifier * * @since 2.0 * @access public * @return mixed|string */ public function get_identifier() { return $this->identifier; } /** * Get cron identifier * * @since 2.0 * @access public * @return mixed|string */ public function get_cron_identifier() { return $this->cron_hook_identifier; } /** * Flush background update related cache to prevent task to go to stalled state. * * @since 2.0.3 */ public static function flush_cache() { $options = array( 'give_completed_upgrades', 'give_doing_upgrade', 'give_paused_batches', 'give_upgrade_error', 'give_db_update_count', 'give_pause_upgrade', 'give_show_db_upgrade_complete_notice', ); foreach ( $options as $option ) { wp_cache_delete( $option, 'options' ); } } }