HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/Schedulers.tar
SchedulerTraits.php000064400000023360151542752070010376 0ustar00<?php
/**
 * Traits for scheduling actions and dependencies.
 */

namespace Automattic\WooCommerce\Admin\Schedulers;

defined( 'ABSPATH' ) || exit;

/**
 * SchedulerTraits class.
 */
trait SchedulerTraits {
	/**
	 * Action scheduler group.
	 *
	 * @var string|null
	 */
	public static $group = 'wc-admin-data';

	/**
	 * Queue instance.
	 *
	 * @var WC_Queue_Interface
	 */
	protected static $queue = null;

	/**
	 * Add all actions as hooks.
	 */
	public static function init() {
		foreach ( self::get_actions() as $action_name => $action_hook ) {
			$method = new \ReflectionMethod( static::class, $action_name );
			add_action( $action_hook, array( static::class, 'do_action_or_reschedule' ), 10, $method->getNumberOfParameters() );
		}
	}

	/**
	 * Get queue instance.
	 *
	 * @return WC_Queue_Interface
	 */
	public static function queue() {
		if ( is_null( self::$queue ) ) {
			self::$queue = WC()->queue();
		}

		return self::$queue;
	}

	/**
	 * Set queue instance.
	 *
	 * @param WC_Queue_Interface $queue Queue instance.
	 */
	public static function set_queue( $queue ) {
		self::$queue = $queue;
	}

	/**
	 * Gets the default scheduler actions for batching and scheduling actions.
	 */
	public static function get_default_scheduler_actions() {
		return array(
			'schedule_action' => 'wc-admin_schedule_action_' . static::$name,
			'queue_batches'   => 'wc-admin_queue_batches_' . static::$name,
		);
	}

	/**
	 * Gets the actions for this specific scheduler.
	 *
	 * @return array
	 */
	public static function get_scheduler_actions() {
		return array();
	}

	/**
	 * Get all available scheduling actions.
	 * Used to determine action hook names and clear events.
	 */
	public static function get_actions() {
		return array_merge(
			static::get_default_scheduler_actions(),
			static::get_scheduler_actions()
		);
	}

	/**
	 * Get an action tag name from the action name.
	 *
	 * @param string $action_name The action name.
	 * @return string|null
	 */
	public static function get_action( $action_name ) {
		$actions = static::get_actions();
		return isset( $actions[ $action_name ] ) ? $actions[ $action_name ] : null;
	}

	/**
	 * Returns an array of actions and dependencies as key => value pairs.
	 *
	 * @return array
	 */
	public static function get_dependencies() {
		return array();
	}

	/**
	 * Get dependencies associated with an action.
	 *
	 * @param string $action_name The action slug.
	 * @return string|null
	 */
	public static function get_dependency( $action_name ) {
		$dependencies = static::get_dependencies();
		return isset( $dependencies[ $action_name ] ) ? $dependencies[ $action_name ] : null;
	}

	/**
	 * Batch action size.
	 */
	public static function get_batch_sizes() {
		return array(
			'queue_batches' => 100,
		);
	}

	/**
	 * Returns the batch size for an action.
	 *
	 * @param string $action Single batch action name.
	 * @return int Batch size.
	 */
	public static function get_batch_size( $action ) {
		$batch_sizes = static::get_batch_sizes();
		$batch_size  = isset( $batch_sizes[ $action ] ) ? $batch_sizes[ $action ] : 25;

		/**
		 * Filter the batch size for regenerating a report table.
		 *
		 * @param int    $batch_size Batch size.
		 * @param string $action Batch action name.
		 */
		return apply_filters( 'woocommerce_analytics_regenerate_batch_size', $batch_size, static::$name, $action );
	}

	/**
	 * Flatten multidimensional arrays to store for scheduling.
	 *
	 * @param array $args Argument array.
	 * @return string
	 */
	public static function flatten_args( $args ) {
		$flattened = array();

		foreach ( $args as $arg ) {
			if ( is_array( $arg ) ) {
				$flattened[] = self::flatten_args( $arg );
			} else {
				$flattened[] = $arg;
			}
		}

		$string = '[' . implode( ',', $flattened ) . ']';
		return $string;
	}

	/**
	 * Check if existing jobs exist for an action and arguments.
	 *
	 * @param string $action_name Action name.
	 * @param array  $args Array of arguments to pass to action.
	 * @return bool
	 */
	public static function has_existing_jobs( $action_name, $args ) {
		$existing_jobs = self::queue()->search(
			array(
				'status'   => 'pending',
				'per_page' => 1,
				'claimed'  => false,
				'hook'     => static::get_action( $action_name ),
				'search'   => self::flatten_args( $args ),
				'group'    => self::$group,
			)
		);

		if ( $existing_jobs ) {
			$existing_job = current( $existing_jobs );

			// Bail out if there's a pending single action, or a pending scheduled actions.
			if (
				( static::get_action( $action_name ) === $existing_job->get_hook() ) ||
				(
					static::get_action( 'schedule_action' ) === $existing_job->get_hook() &&
					in_array( self::get_action( $action_name ), $existing_job->get_args(), true )
				)
			) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Get the next blocking job for an action.
	 *
	 * @param string $action_name Action name.
	 * @return false|ActionScheduler_Action
	 */
	public static function get_next_blocking_job( $action_name ) {
		$dependency = self::get_dependency( $action_name );

		if ( ! $dependency ) {
			return false;
		}

		$blocking_jobs = self::queue()->search(
			array(
				'status'   => 'pending',
				'orderby'  => 'date',
				'order'    => 'DESC',
				'per_page' => 1,
				'search'   => $dependency, // search is used instead of hook to find queued batch creation.
				'group'    => static::$group,
			)
		);

		$next_job_schedule = null;

		if ( is_array( $blocking_jobs ) ) {
			foreach ( $blocking_jobs as $blocking_job ) {
				$next_job_schedule = self::get_next_action_time( $blocking_job );

				// Ensure that the next schedule is a DateTime (it can be null).
				if ( is_a( $next_job_schedule, 'DateTime' ) ) {
					return $blocking_job;
				}
			}
		}

		return false;
	}

	/**
	 * Check for blocking jobs and reschedule if any exist.
	 */
	public static function do_action_or_reschedule() {
		$action_hook = current_action();
		$action_name = array_search( $action_hook, static::get_actions(), true );
		$args        = func_get_args();

		// Check if any blocking jobs exist and schedule after they've completed
		// or schedule to run now if no blocking jobs exist.
		$blocking_job = static::get_next_blocking_job( $action_name );
		if ( $blocking_job ) {
			$after = new \DateTime();
			self::queue()->schedule_single(
				self::get_next_action_time( $blocking_job )->getTimestamp() + 5,
				$action_hook,
				$args,
				static::$group
			);
		} else {
			call_user_func_array( array( static::class, $action_name ), $args );
		}
	}

	/**
	 * Get the DateTime for the next scheduled time an action should run.
	 * This function allows backwards compatibility with Action Scheduler < v3.0.
	 *
	 * @param \ActionScheduler_Action $action Action.
	 * @return DateTime|null
	 */
	public static function get_next_action_time( $action ) {
		if ( method_exists( $action->get_schedule(), 'get_next' ) ) {
			$after             = new \DateTime();
			$next_job_schedule = $action->get_schedule()->get_next( $after );
		} else {
			$next_job_schedule = $action->get_schedule()->next();
		}

		return $next_job_schedule;
	}

	/**
	 * Schedule an action to run and check for dependencies.
	 *
	 * @param string $action_name Action name.
	 * @param array  $args Array of arguments to pass to action.
	 */
	public static function schedule_action( $action_name, $args = array() ) {
		// Check for existing jobs and bail if they already exist.
		if ( static::has_existing_jobs( $action_name, $args ) ) {
			return;
		}

		$action_hook = static::get_action( $action_name );
		if ( ! $action_hook ) {
			return;
		}

		if (
			// Skip scheduling if Action Scheduler tables have not been initialized.
			! get_option( 'schema-ActionScheduler_StoreSchema' ) ||
			apply_filters( 'woocommerce_analytics_disable_action_scheduling', false )
		) {
			call_user_func_array( array( static::class, $action_name ), $args );
			return;
		}

		self::queue()->schedule_single( time() + 5, $action_hook, $args, static::$group );
	}

	/**
	 * Queue a large number of batch jobs, respecting the batch size limit.
	 * Reduces a range of batches down to "single batch" jobs.
	 *
	 * @param int    $range_start Starting batch number.
	 * @param int    $range_end Ending batch number.
	 * @param string $single_batch_action Action to schedule for a single batch.
	 * @param array  $action_args Action arguments.
	 * @return void
	 */
	public static function queue_batches( $range_start, $range_end, $single_batch_action, $action_args = array() ) {
		$batch_size       = static::get_batch_size( 'queue_batches' );
		$range_size       = 1 + ( $range_end - $range_start );
		$action_timestamp = time() + 5;

		if ( $range_size > $batch_size ) {
			// If the current batch range is larger than a single batch,
			// split the range into $queue_batch_size chunks.
			$chunk_size = (int) ceil( $range_size / $batch_size );

			for ( $i = 0; $i < $batch_size; $i++ ) {
				$batch_start = (int) ( $range_start + ( $i * $chunk_size ) );
				$batch_end   = (int) min( $range_end, $range_start + ( $chunk_size * ( $i + 1 ) ) - 1 );

				if ( $batch_start > $range_end ) {
					return;
				}

				self::schedule_action(
					'queue_batches',
					array( $batch_start, $batch_end, $single_batch_action, $action_args )
				);
			}
		} else {
			// Otherwise, queue the single batches.
			for ( $i = $range_start; $i <= $range_end; $i++ ) {
				$batch_action_args = array_merge( array( $i ), $action_args );
				self::schedule_action( $single_batch_action, $batch_action_args );
			}
		}
	}

	/**
	 * Clears all queued actions.
	 */
	public static function clear_queued_actions() {
		if ( version_compare( \ActionScheduler_Versions::instance()->latest_version(), '3.0', '>=' ) ) {
			\ActionScheduler::store()->cancel_actions_by_group( static::$group );
		} else {
			$actions = static::get_actions();
			foreach ( $actions as $action ) {
				self::queue()->cancel_all( $action, null, static::$group );
			}
		}
	}
}
CustomersScheduler.php000064400000007055151545533730011122 0ustar00<?php
/**
 * Customer syncing related functions and actions.
 */

namespace Automattic\WooCommerce\Internal\Admin\Schedulers;

defined( 'ABSPATH' ) || exit;

use Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;

/**
 * CustomersScheduler Class.
 */
class CustomersScheduler extends ImportScheduler {
	/**
	 * Slug to identify the scheduler.
	 *
	 * @var string
	 */
	public static $name = 'customers';

	/**
	 * Attach customer lookup update hooks.
	 *
	 * @internal
	 */
	public static function init() {
		CustomersDataStore::init();
		parent::init();
	}

	/**
	 * Add customer dependencies.
	 *
	 * @internal
	 * @return array
	 */
	public static function get_dependencies() {
		return array(
			'delete_batch_init' => OrdersScheduler::get_action( 'delete_batch_init' ),
		);
	}

	/**
	 * Get the customer IDs and total count that need to be synced.
	 *
	 * @internal
	 * @param int      $limit Number of records to retrieve.
	 * @param int      $page  Page number.
	 * @param int|bool $days Number of days prior to current date to limit search results.
	 * @param bool     $skip_existing Skip already imported customers.
	 */
	public static function get_items( $limit = 10, $page = 1, $days = false, $skip_existing = false ) {
		$customer_roles = apply_filters( 'woocommerce_analytics_import_customer_roles', array( 'customer' ) );
		$query_args     = array(
			'fields'   => 'ID',
			'orderby'  => 'ID',
			'order'    => 'ASC',
			'number'   => $limit,
			'paged'    => $page,
			'role__in' => $customer_roles,
		);

		if ( is_int( $days ) ) {
			$query_args['date_query'] = array(
				'after' => gmdate( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) ),
			);
		}

		if ( $skip_existing ) {
			add_action( 'pre_user_query', array( __CLASS__, 'exclude_existing_customers_from_query' ) );
		}

		$customer_query = new \WP_User_Query( $query_args );

		remove_action( 'pre_user_query', array( __CLASS__, 'exclude_existing_customers_from_query' ) );

		return (object) array(
			'total' => $customer_query->get_total(),
			'ids'   => $customer_query->get_results(),
		);
	}

	/**
	 * Exclude users that already exist in our customer lookup table.
	 *
	 * Meant to be hooked into 'pre_user_query' action.
	 *
	 * @internal
	 * @param WP_User_Query $wp_user_query WP_User_Query to modify.
	 */
	public static function exclude_existing_customers_from_query( $wp_user_query ) {
		global $wpdb;

		$wp_user_query->query_where .= " AND NOT EXISTS (
			SELECT ID FROM {$wpdb->prefix}wc_customer_lookup
			WHERE {$wpdb->prefix}wc_customer_lookup.user_id = {$wpdb->users}.ID
		)";
	}

	/**
	 * Get total number of rows imported.
	 *
	 * @internal
	 * @return int
	 */
	public static function get_total_imported() {
		global $wpdb;
		return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wc_customer_lookup" );
	}

	/**
	 * Imports a single customer.
	 *
	 * @internal
	 * @param int $user_id User ID.
	 * @return void
	 */
	public static function import( $user_id ) {
		CustomersDataStore::update_registered_customer( $user_id );
	}

	/**
	 * Delete a batch of customers.
	 *
	 * @internal
	 * @param int $batch_size Number of items to delete.
	 * @return void
	 */
	public static function delete( $batch_size ) {
		global $wpdb;

		$customer_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT customer_id FROM {$wpdb->prefix}wc_customer_lookup ORDER BY customer_id ASC LIMIT %d",
				$batch_size
			)
		);

		foreach ( $customer_ids as $customer_id ) {
			CustomersDataStore::delete_customer( $customer_id );
		}
	}
}
ImportInterface.php000064400000001306151545533730010363 0ustar00<?php
/**
 * Import related abstract functions.
 */

namespace Automattic\WooCommerce\Internal\Admin\Schedulers;

interface ImportInterface {
	/**
	 * Get items based on query and return IDs along with total available.
	 *
	 * @internal
	 * @param int      $limit Number of records to retrieve.
	 * @param int      $page  Page number.
	 * @param int|bool $days Number of days prior to current date to limit search results.
	 * @param bool     $skip_existing Skip already imported items.
	 */
	public static function get_items( $limit, $page, $days, $skip_existing );

	/**
	 * Get total number of items already imported.
	 *
	 * @internal
	 * @return null
	 */
	public static function get_total_imported();

}
ImportScheduler.php000064400000011457151545533730010411 0ustar00<?php
/**
 * Import related functions and actions.
 */

namespace Automattic\WooCommerce\Internal\Admin\Schedulers;

defined( 'ABSPATH' ) || exit;

use Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use Automattic\WooCommerce\Admin\Schedulers\SchedulerTraits;

/**
 * ImportScheduler class.
 */
abstract class ImportScheduler implements ImportInterface {
	/**
	 * Import stats option name.
	 */
	const IMPORT_STATS_OPTION = 'woocommerce_admin_import_stats';

	/**
	 * Scheduler traits.
	 */
	use SchedulerTraits {
		get_batch_sizes as get_scheduler_batch_sizes;
	}

	/**
	 * Returns true if an import is in progress.
	 *
	 * @internal
	 * @return bool
	 */
	public static function is_importing() {
		$pending_jobs = self::queue()->search(
			array(
				'status'   => 'pending',
				'per_page' => 1,
				'claimed'  => false,
				'search'   => 'import',
				'group'    => self::$group,
			)
		);
		if ( empty( $pending_jobs ) ) {
			$in_progress = self::queue()->search(
				array(
					'status'   => 'in-progress',
					'per_page' => 1,
					'search'   => 'import',
					'group'    => self::$group,
				)
			);
		}

		return ! empty( $pending_jobs ) || ! empty( $in_progress );
	}

	/**
	 * Get batch sizes.
	 *
	 * @internal
	 * @retun array
	 */
	public static function get_batch_sizes() {
		return array_merge(
			self::get_scheduler_batch_sizes(),
			array(
				'delete' => 10,
				'import' => 25,
				'queue'  => 100,
			)
		);

	}

	/**
	 * Get all available scheduling actions.
	 * Used to determine action hook names and clear events.
	 *
	 * @internal
	 * @return array
	 */
	public static function get_scheduler_actions() {
		return array(
			'import_batch_init' => 'wc-admin_import_batch_init_' . static::$name,
			'import_batch'      => 'wc-admin_import_batch_' . static::$name,
			'delete_batch_init' => 'wc-admin_delete_batch_init_' . static::$name,
			'delete_batch'      => 'wc-admin_delete_batch_' . static::$name,
			'import'            => 'wc-admin_import_' . static::$name,
		);
	}

	/**
	 * Queue the imports into multiple batches.
	 *
	 * @internal
	 * @param integer|boolean $days Number of days to import.
	 * @param boolean         $skip_existing Skip exisiting records.
	 */
	public static function import_batch_init( $days, $skip_existing ) {
		$batch_size = static::get_batch_size( 'import' );
		$items      = static::get_items( 1, 1, $days, $skip_existing );

		if ( 0 === $items->total ) {
			return;
		}

		$num_batches = ceil( $items->total / $batch_size );

		self::queue_batches( 1, $num_batches, 'import_batch', array( $days, $skip_existing ) );
	}

	/**
	 * Imports a batch of items to update.
	 *
	 * @internal
	 * @param int      $batch_number Batch number to import (essentially a query page number).
	 * @param int|bool $days Number of days to import.
	 * @param bool     $skip_existing Skip exisiting records.
	 * @return void
	 */
	public static function import_batch( $batch_number, $days, $skip_existing ) {
		$batch_size = static::get_batch_size( 'import' );

		$properties = array(
			'batch_number' => $batch_number,
			'batch_size'   => $batch_size,
			'type'         => static::$name,
		);
		wc_admin_record_tracks_event( 'import_job_start', $properties );

		// When we are skipping already imported items, the table of items to import gets smaller in
		// every batch, so we want to always import the first page.
		$page  = $skip_existing ? 1 : $batch_number;
		$items = static::get_items( $batch_size, $page, $days, $skip_existing );

		foreach ( $items->ids as $id ) {
			static::import( $id );
		}

		$import_stats                              = get_option( self::IMPORT_STATS_OPTION, array() );
		$imported_count                            = absint( $import_stats[ static::$name ]['imported'] ) + count( $items->ids );
		$import_stats[ static::$name ]['imported'] = $imported_count;
		update_option( self::IMPORT_STATS_OPTION, $import_stats );

		$properties['imported_count'] = $imported_count;

		wc_admin_record_tracks_event( 'import_job_complete', $properties );
	}

	/**
	 * Queue item deletion in batches.
	 *
	 * @internal
	 */
	public static function delete_batch_init() {
		global $wpdb;
		$batch_size = static::get_batch_size( 'delete' );
		$count      = static::get_total_imported();

		if ( 0 === $count ) {
			return;
		}

		$num_batches = ceil( $count / $batch_size );

		self::queue_batches( 1, $num_batches, 'delete_batch' );
	}

	/**
	 * Delete a batch by passing the count to be deleted to the child delete method.
	 *
	 * @internal
	 * @return void
	 */
	public static function delete_batch() {
		wc_admin_record_tracks_event( 'delete_import_data_job_start', array( 'type' => static::$name ) );

		$batch_size = static::get_batch_size( 'delete' );
		static::delete( $batch_size );

		ReportsCache::invalidate();

		wc_admin_record_tracks_event( 'delete_import_data_job_complete', array( 'type' => static::$name ) );
	}
}
MailchimpScheduler.php000064400000007160151545533730011036 0ustar00<?php

namespace Automattic\WooCommerce\Internal\Admin\Schedulers;

/**
 * Class MailchimpScheduler
 *
 * @package Automattic\WooCommerce\Admin\Schedulers
 */
class MailchimpScheduler {

	const SUBSCRIBE_ENDPOINT     = 'https://woocommerce.com/wp-json/wccom/v1/subscribe';
	const SUBSCRIBE_ENDPOINT_DEV = 'http://woocommerce.test/wp-json/wccom/v1/subscribe';

	const SUBSCRIBED_OPTION_NAME             = 'woocommerce_onboarding_subscribed_to_mailchimp';
	const SUBSCRIBED_ERROR_COUNT_OPTION_NAME = 'woocommerce_onboarding_subscribed_to_mailchimp_error_count';
	const MAX_ERROR_THRESHOLD                = 3;

	const LOGGER_CONTEXT = 'mailchimp_scheduler';

	/**
	 * The logger instance.
	 *
	 * @var \WC_Logger_Interface|null
	 */
	private $logger;

	/**
	 * MailchimpScheduler constructor.
	 *
	 * @internal
	 * @param \WC_Logger_Interface|null $logger Logger instance.
	 */
	public function __construct( \WC_Logger_Interface $logger = null ) {
		if ( null === $logger ) {
			$logger = wc_get_logger();
		}
		$this->logger = $logger;
	}

	/**
	 * Attempt to subscribe store_email to MailChimp.
	 *
	 * @internal
	 */
	public function run() {
		// Abort if we've already subscribed to MailChimp.
		if ( 'yes' === get_option( self::SUBSCRIBED_OPTION_NAME ) ) {
			return false;
		}

		$profile_data = get_option( 'woocommerce_onboarding_profile' );
		if ( ! isset( $profile_data['is_agree_marketing'] ) || false === $profile_data['is_agree_marketing'] ) {
			return false;
		}

		// Abort if store_email doesn't exist.
		if ( ! isset( $profile_data['store_email'] ) ) {
			return false;
		}

		// Abort if failed requests reaches the threshold.
		if ( intval( get_option( self::SUBSCRIBED_ERROR_COUNT_OPTION_NAME, 0 ) ) >= self::MAX_ERROR_THRESHOLD ) {
			return false;
		}

		$response = $this->make_request( $profile_data['store_email'] );
		if ( is_wp_error( $response ) || ! isset( $response['body'] ) ) {
			$this->handle_request_error();
			return false;
		}

		$body = json_decode( $response['body'] );
		if ( isset( $body->success ) && true === $body->success ) {
			update_option( self::SUBSCRIBED_OPTION_NAME, 'yes' );
			return true;
		}

		$this->handle_request_error( $body );
		return false;
	}

	/**
	 * Make an HTTP request to the API.
	 *
	 * @internal
	 * @param string $store_email Email address to subscribe.
	 *
	 * @return mixed
	 */
	public function make_request( $store_email ) {
		if ( true === defined( 'WP_ENVIRONMENT_TYPE' ) && 'development' === constant( 'WP_ENVIRONMENT_TYPE' ) ) {
			$subscribe_endpoint = self::SUBSCRIBE_ENDPOINT_DEV;
		} else {
			$subscribe_endpoint = self::SUBSCRIBE_ENDPOINT;
		}

		return wp_remote_post(
			$subscribe_endpoint,
			array(
				'user-agent' => 'WooCommerce/' . WC()->version . '; ' . get_bloginfo( 'url' ),
				'method'     => 'POST',
				'body'       => array(
					'email' => $store_email,
				),
			)
		);
	}

	/**
	 * Reset options.
	 *
	 * @internal
	 */
	public static function reset() {
		delete_option( self::SUBSCRIBED_OPTION_NAME );
		delete_option( self::SUBSCRIBED_ERROR_COUNT_OPTION_NAME );
	}

	/**
	 * Handle subscribe API error.
	 *
	 * @internal
	 * @param string $extra_msg  Extra message to log.
	 */
	private function handle_request_error( $extra_msg = null ) {
		// phpcs:ignore
		$msg = isset( $extra_msg ) ? 'Incorrect response from Mailchimp API with: ' . print_r( $extra_msg, true ) : 'Error getting a response from Mailchimp API.';

		$this->logger->error( $msg, array( 'source' => self::LOGGER_CONTEXT ) );

		$accumulated_error_count = intval( get_option( self::SUBSCRIBED_ERROR_COUNT_OPTION_NAME, 0 ) ) + 1;
		update_option( self::SUBSCRIBED_ERROR_COUNT_OPTION_NAME, $accumulated_error_count );
	}
}
OrdersScheduler.php000064400000020553151545533740010373 0ustar00<?php
/**
 * Order syncing related functions and actions.
 */

namespace Automattic\WooCommerce\Internal\Admin\Schedulers;

defined( 'ABSPATH' ) || exit;

use Automattic\WooCommerce\Admin\API\Reports\Coupons\DataStore as CouponsDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\DataStore as OrdersStatsDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Products\DataStore as ProductsDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Taxes\DataStore as TaxesDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache;
use Automattic\WooCommerce\Admin\Overrides\Order;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Utilities\OrderUtil;

/**
 * OrdersScheduler Class.
 */
class OrdersScheduler extends ImportScheduler {
	/**
	 * Slug to identify the scheduler.
	 *
	 * @var string
	 */
	public static $name = 'orders';

	/**
	 * Attach order lookup update hooks.
	 *
	 * @internal
	 */
	public static function init() {
		// Activate WC_Order extension.
		\Automattic\WooCommerce\Admin\Overrides\Order::add_filters();
		\Automattic\WooCommerce\Admin\Overrides\OrderRefund::add_filters();

		// Order and refund data must be run on these hooks to ensure meta data is set.
		add_action( 'woocommerce_update_order', array( __CLASS__, 'possibly_schedule_import' ) );
		add_action( 'woocommerce_create_order', array( __CLASS__, 'possibly_schedule_import' ) );
		add_action( 'woocommerce_refund_created', array( __CLASS__, 'possibly_schedule_import' ) );

		OrdersStatsDataStore::init();
		CouponsDataStore::init();
		ProductsDataStore::init();
		TaxesDataStore::init();

		parent::init();
	}

	/**
	 * Add customer dependencies.
	 *
	 * @internal
	 * @return array
	 */
	public static function get_dependencies() {
		return array(
			'import_batch_init' => \Automattic\WooCommerce\Internal\Admin\Schedulers\CustomersScheduler::get_action( 'import_batch_init' ),
		);
	}

	/**
	 * Get the order/refund IDs and total count that need to be synced.
	 *
	 * @internal
	 * @param int      $limit Number of records to retrieve.
	 * @param int      $page  Page number.
	 * @param int|bool $days Number of days prior to current date to limit search results.
	 * @param bool     $skip_existing Skip already imported orders.
	 */
	public static function get_items( $limit = 10, $page = 1, $days = false, $skip_existing = false ) {
		if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
			return self::get_items_from_orders_table( $limit, $page, $days, $skip_existing );
		} else {
			return self::get_items_from_posts_table( $limit, $page, $days, $skip_existing );
		}
	}

	/**
	 * Helper method to ger order/refund IDS and total count that needs to be synced.
	 *
	 * @internal
	 * @param int      $limit Number of records to retrieve.
	 * @param int      $page  Page number.
	 * @param int|bool $days Number of days prior to current date to limit search results.
	 * @param bool     $skip_existing Skip already imported orders.
	 *
	 * @return object Total counts.
	 */
	private static function get_items_from_posts_table( $limit, $page, $days, $skip_existing ) {
		global $wpdb;
		$where_clause = '';
		$offset       = $page > 1 ? ( $page - 1 ) * $limit : 0;

		if ( is_int( $days ) ) {
			$days_ago      = gmdate( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) );
			$where_clause .= " AND post_date_gmt >= '{$days_ago}'";
		}

		if ( $skip_existing ) {
			$where_clause .= " AND NOT EXISTS (
				SELECT 1 FROM {$wpdb->prefix}wc_order_stats
				WHERE {$wpdb->prefix}wc_order_stats.order_id = {$wpdb->posts}.ID
			)";
		}

		$count = $wpdb->get_var(
			"SELECT COUNT(*) FROM {$wpdb->posts}
			WHERE post_type IN ( 'shop_order', 'shop_order_refund' )
			AND post_status NOT IN ( 'wc-auto-draft', 'auto-draft', 'trash' )
			{$where_clause}"
		); // phpcs:ignore unprepared SQL ok.

		$order_ids = absint( $count ) > 0 ? $wpdb->get_col(
			$wpdb->prepare(
				"SELECT ID FROM {$wpdb->posts}
				WHERE post_type IN ( 'shop_order', 'shop_order_refund' )
				AND post_status NOT IN ( 'wc-auto-draft', 'auto-draft', 'trash' )
				{$where_clause}
				ORDER BY post_date_gmt ASC
				LIMIT %d
				OFFSET %d",
				$limit,
				$offset
			)
		) : array(); // phpcs:ignore unprepared SQL ok.

		return (object) array(
			'total' => absint( $count ),
			'ids'   => $order_ids,
		);
	}

	/**
	 * Helper method to ger order/refund IDS and total count that needs to be synced from HPOS.
	 *
	 * @internal
	 * @param int      $limit Number of records to retrieve.
	 * @param int      $page  Page number.
	 * @param int|bool $days Number of days prior to current date to limit search results.
	 * @param bool     $skip_existing Skip already imported orders.
	 *
	 * @return object Total counts.
	 */
	private static function get_items_from_orders_table( $limit, $page, $days, $skip_existing ) {
		global $wpdb;
		$where_clause = '';
		$offset       = $page > 1 ? ( $page - 1 ) * $limit : 0;
		$order_table  = OrdersTableDataStore::get_orders_table_name();

		if ( is_int( $days ) ) {
			$days_ago      = gmdate( 'Y-m-d 00:00:00', time() - ( DAY_IN_SECONDS * $days ) );
			$where_clause .= " AND orders.date_created_gmt >= '{$days_ago}'";
		}

		if ( $skip_existing ) {
			$where_clause .= "AND NOT EXiSTS (
					SELECT 1 FROM {$wpdb->prefix}wc_order_stats
					WHERE {$wpdb->prefix}wc_order_stats.order_id = orders.id
					)
				";
		}

		$count = $wpdb->get_var(
			"
SELECT COUNT(*) FROM {$order_table} AS orders
WHERE type in ( 'shop_order', 'shop_order_refund' )
AND status NOT IN ( 'wc-auto-draft', 'trash', 'auto-draft' )
{$where_clause}
"
		); // phpcs:ignore unprepared SQL ok.

		$order_ids = absint( $count ) > 0 ? $wpdb->get_col(
			$wpdb->prepare(
				"SELECT id FROM {$order_table} AS orders
				WHERE type IN ( 'shop_order', 'shop_order_refund' )
				AND status NOT IN ( 'wc-auto-draft', 'auto-draft', 'trash' )
				{$where_clause}
				ORDER BY date_created_gmt ASC
				LIMIT %d
				OFFSET %d",
				$limit,
				$offset
			)
		) : array(); // phpcs:ignore unprepared SQL ok.

		return (object) array(
			'total' => absint( $count ),
			'ids'   => $order_ids,
		);
	}

	/**
	 * Get total number of rows imported.
	 *
	 * @internal
	 */
	public static function get_total_imported() {
		global $wpdb;
		return $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}wc_order_stats" );
	}

	/**
	 * Schedule this import if the post is an order or refund.
	 *
	 * @param int $order_id Post ID.
	 *
	 * @internal
	 */
	public static function possibly_schedule_import( $order_id ) {
		if ( ! OrderUtil::is_order( $order_id, array( 'shop_order' ) ) && 'woocommerce_refund_created' !== current_filter() ) {
			return;
		}

		self::schedule_action( 'import', array( $order_id ) );
	}

	/**
	 * Imports a single order or refund to update lookup tables for.
	 * If an error is encountered in one of the updates, a retry action is scheduled.
	 *
	 * @internal
	 * @param int $order_id Order or refund ID.
	 * @return void
	 */
	public static function import( $order_id ) {
		$order = wc_get_order( $order_id );

		// If the order isn't found for some reason, skip the sync.
		if ( ! $order ) {
			return;
		}

		$type = $order->get_type();

		// If the order isn't the right type, skip sync.
		if ( 'shop_order' !== $type && 'shop_order_refund' !== $type ) {
			return;
		}

		// If the order has no id or date created, skip sync.
		if ( ! $order->get_id() || ! $order->get_date_created() ) {
			return;
		}

		$results = array(
			OrdersStatsDataStore::sync_order( $order_id ),
			ProductsDataStore::sync_order_products( $order_id ),
			CouponsDataStore::sync_order_coupons( $order_id ),
			TaxesDataStore::sync_order_taxes( $order_id ),
			CustomersDataStore::sync_order_customer( $order_id ),
		);

		if ( 'shop_order' === $type ) {
			$order_refunds = $order->get_refunds();

			foreach ( $order_refunds as $refund ) {
				OrdersStatsDataStore::sync_order( $refund->get_id() );
			}
		}

		ReportsCache::invalidate();
	}

	/**
	 * Delete a batch of orders.
	 *
	 * @internal
	 * @param int $batch_size Number of items to delete.
	 * @return void
	 */
	public static function delete( $batch_size ) {
		global $wpdb;

		$order_ids = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT order_id FROM {$wpdb->prefix}wc_order_stats ORDER BY order_id ASC LIMIT %d",
				$batch_size
			)
		);

		foreach ( $order_ids as $order_id ) {
			OrdersStatsDataStore::delete_order( $order_id );
		}
	}
}