Completed
Pull Request — develop (#1350)
by Naveen
04:17 queued 01:06
created

Background_Process::get_next_batch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Wordlift\Common\Background_Process;
4
5
abstract class Background_Process extends \Wordlift_Plugin_WP_Background_Process {
6
	/**
7
	 * @var \Wordlift_Log_Service
8
	 */
9
	private $log;
10
	/**
11
	 * @var Data_Source
12
	 */
13
	protected $data_source;
14
15
	/**
16
	 * Background_Process constructor.
17
	 *
18
	 * @param $data_source Data_Source
19
	 */
20
	public function __construct( $data_source ) {
21
		parent::__construct();
22
		$this->data_source = $data_source;
23
		$this->log = \Wordlift_Log_Service::get_logger( get_class() );
24
		// Set value for action key.
25
		$this->action = $this->get_action_key();
26
	}
27
28
	/**
29
	 * The key which is used to store the Sync_State class for the current process
30
	 * @return string
31
	 */
32
	protected abstract function get_state_storage_key();
33
34
	/**
35
	 * The key which is used as prefix to store the options.
36
	 * @return string
37
	 */
38
	protected abstract function get_action_key();
39
40
41
	/**
42
	 * Check whether the process must cancel or not.
43
	 *
44
	 * @return bool Whether to cancel or not the process.
45
	 */
46
	private function must_cancel() {
47
48
		return get_transient( "{$this->action}__cancel" );
49
	}
50
51
	/**
52
	 * Check whether the provided state is `started` or not.
53
	 *
54
	 * @param Sync_State $state The {@link Sync_State}.
55
	 *
56
	 * @return bool True if the state is started.
57
	 */
58
	private function is_started( $state ) {
59
		return $state instanceof Sync_State && 'started' === $state->state && 30 > ( time() - $state->last_update );
60
	}
61
62
63
	/**
64
	 * Start the background processing.
65
	 *
66
	 * @return bool True if the process has been started, otherwise false.
67
	 */
68
	public function start() {
69
		$action  = $this->get_action_key();
70
		$this->log->debug( "Trying to start  ${action}..." );
71
		// Create a new Sync_Model state of `started`.
72 View Code Duplication
		if ( ! $this->is_started( self::get_state() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73
			$this->log->debug( "Starting..." );
74
75
			$sync_state = new Sync_State( time(), 0, $this->data_source->count(), time(), 'started' );
76
			update_option( $this->get_state_storage_key(), $sync_state, false );
77
78
			$next = $this->data_source->next();
79
80
			$this->push_to_queue( $next );
81
			$this->save()->dispatch();
82
83
			if ( $next && is_array($next) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $next of type integer[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
84
				$this->log->debug( sprintf( 'Started with term IDs %s.', implode( ', ', $next ) ) );
85
			}
86
			return true;
87
		}
88
89
		return false;
90
	}
91
92
93
	/**
94
	 * Cancels the current process.
95
	 */
96
	public function cancel() {
97
98
		$action = $this->action;
99
		$this->log->debug( "Cancelling ${action}..." );
100
101
		// Cleanup the process data.
102
		$this->cancel_process();
103
104
		// Set the state to cancelled.
105
		$state = $this->get_state();
106
		$state->set_state( 'cancelled' );
107
		update_option( $this->get_state_storage_key(), $state, false );
108
109
		// Finally delete the transient.
110
		delete_transient( "{$this->action}__cancel" );
111
112
	}
113
114
115
	/**
116
	 * Get the sync state.
117
	 *
118
	 * @return Sync_State The {@link Sync_State}.
119
	 */
120
	public function get_state() {
121
122
		try {
123
			return get_option( $this->get_state_storage_key(), Sync_State::unknown() );
124
		} catch ( \Exception $e ) {
125
			return Sync_State::unknown();
126
		}
127
128
	}
129
130
131
	/**
132
	 * This function is called:
133
	 *  - To start a new Synchronization, by passing a {@link Sync_Start_Message} instance.
134
	 *  - To process a item, by passing a numeric ID.
135
	 *
136
	 * This function returns the parameter for the next call or NULL if there are no more items to process.
137
	 *
138
	 * @param int[] $items An array of item IDs.
139
	 *
140
	 * @return int[]|false The next IDs or false if there are no more.
141
	 */
142
	protected function task( $items ) {
143
144
		// Check if we must cancel.
145
		if ( $this->must_cancel() || ! $items ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $items of type integer[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
146
			$this->log->debug("Cancelling background process " . $this->action . " due to no items inside task() method");
147
			$this->cancel();
148
			return false;
149
		}
150
151
		if ( $items && is_array( $items ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $items of type integer[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
152
			$this->log->debug( sprintf( "Synchronizing items %s...", implode( ', ', $items ) ) );
153
		}
154
		// Sync the item.
155
		if ( $this->process_items( $items ) ) {
156
			// Update the process state, set the index in state in order
157
			// to reflect the new items.
158
			$this->update_batch_index();
159
			// Get the next batch for processing.
160
			return $this->get_next_batch();
161
		}
162
		else {
163
			// Return the failed term ids again.
164
			return $items;
165
		}
166
	}
167
168
	/**
169
	 * Process all the items in the current batch.
170
	 * @param $items
171
	 *
172
	 * @return bool If all items are successfully processed.
173
	 */
174
	abstract protected function process_items( $items );
175
176
	/**
177
	 * Return next batch of items after processing.
178
	 * @return int[] or false
179
	 */
180
	 protected function get_next_batch() {
181
	 	return $this->data_source->next();
182
	 }
183
184
185
186
	private function update_batch_index() {
187
		$next       = $this->data_source->next();
188
		$next_state = isset( $next ) ? 'started' : 'ended';
189
190
		/**
191
		 * Update the synchronization meta data, by increasing the current index.
192
		 *
193
		 * @var Sync_State $sync The {@link Sync_State}.
194
		 */
195
		$state = self::get_state()
196
		             ->increment_index( $this->data_source->get_batch_size() )
197
		             ->set_state( $next_state );
198
199
		update_option( $this->get_state_storage_key(), $state, false );
200
201
	}
202
203
204
}