Completed
Pull Request — develop (#1350)
by Naveen
03:08
created

Background_Process::is_started()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
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
87
			return true;
88
		}
89
90
		return false;
91
	}
92
93
94
	/**
95
	 * Cancels the current process.
96
	 */
97
	public function cancel() {
98
99
		$action = $this->action;
100
		$this->log->debug( "Cancelling ${action}..." );
101
102
		// Cleanup the process data.
103
		$this->cancel_process();
104
105
		// Set the state to cancelled.
106
		$state = $this->get_state();
107
		$state->set_state( 'cancelled' );
108
		update_option( $this->get_state_storage_key(), $state, false );
109
110
		// Finally delete the transient.
111
		delete_transient( "{$this->action}__cancel" );
112
113
	}
114
115
116
	/**
117
	 * Get the sync state.
118
	 *
119
	 * @return Sync_State The {@link Sync_State}.
120
	 */
121
	public function get_state() {
122
123
		try {
124
			return get_option( $this->get_state_storage_key(), Sync_State::unknown() );
125
		} catch ( \Exception $e ) {
126
			return Sync_State::unknown();
127
		}
128
129
	}
130
131
132
	/**
133
	 * This function is called:
134
	 *  - To start a new Synchronization, by passing a {@link Sync_Start_Message} instance.
135
	 *  - To process a item, by passing a numeric ID.
136
	 *
137
	 * This function returns the parameter for the next call or NULL if there are no more items to process.
138
	 *
139
	 * @param int[] $items An array of item IDs.
140
	 *
141
	 * @return int[]|false The next IDs or false if there are no more.
142
	 */
143
	protected function task( $items ) {
144
145
		// Check if we must cancel.
146
		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...
147
			$this->log->debug( "Cancelling background process " . $this->action . " due to no items inside task() method" );
148
			$this->cancel();
149
150
			return false;
151
		}
152
153
		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...
154
			$this->log->debug( sprintf( "Synchronizing items %s...", implode( ', ', $items ) ) );
155
		}
156
		// Sync the item.
157
		if ( $this->process_items( $items ) ) {
158
			// Update the process state, set the index in state in order
159
			// to reflect the new items.
160
			$this->update_batch_index();
161
162
			// Get the next batch for processing.
163
			return $this->get_next_batch();
164
		} else {
165
			// Return the failed term ids again.
166
			return $items;
167
		}
168
	}
169
170
	/**
171
	 * Process all the items in the current batch.
172
	 *
173
	 * @param $items
174
	 *
175
	 * @return bool If all items are successfully processed.
176
	 */
177
	abstract protected function process_items( $items );
178
179
	/**
180
	 * Return next batch of items after processing.
181
	 * @return int[] or false
182
	 */
183
	protected function get_next_batch() {
184
		$next = $this->data_source->next();
185
		$this->log->debug( "Returned the following items to be processed for next batch " . var_export( $next, true ) );
186
187
		return $next;
188
	}
189
190
191
	private function update_batch_index() {
192
		$next = $this->data_source->next();
193
194
		$next_state = isset( $next ) ? 'started' : 'ended';
195
196
		/**
197
		 * Update the synchronization meta data, by increasing the current index.
198
		 *
199
		 * @var Sync_State $sync The {@link Sync_State}.
200
		 */
201
		$state = self::get_state()
202
		             ->increment_index( $this->data_source->get_batch_size() )
203
		             ->set_state( $next_state );
204
		$this->log->debug( 'Items index for ' . $this->get_action_key() . " updated to " . var_export( $state->get_array(), true ) );
205
206
		update_option( $this->get_state_storage_key(), $state, false );
207
208
	}
209
210
211
}