Your IP : 3.145.0.146


Current Path : /var/www/ooareogundevinitiative/wp-content/plugins/give/includes/
Upload File :
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' );
		}
	}
}