Completed
Push — develop ( f11ef2...d41b65 )
by David
06:01 queued 11s
created

Sync_Background_Process::sync_items()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 1
dl 0
loc 37
rs 9.0168
c 0
b 0
f 0
1
<?php
2
3
namespace Wordlift\Dataset;
4
5
class Sync_Background_Process extends \Wordlift_Plugin_WP_Background_Process {
6
7
	protected $action = 'wl_dataset__sync';
8
9
	/**
10
	 * @var Sync_Service
11
	 */
12
	private $sync_service;
13
14
	/**
15
	 * @var \Wordlift_Log_Service
16
	 */
17
	private $log;
18
19
	/**
20
	 * Sync_Background_Process constructor.
21
	 *
22
	 * @param $sync_service Sync_Service A {@link Sync_Service} instance providing the supporting functions to this background process.
23
	 */
24
	public function __construct( $sync_service ) {
25
		parent::__construct();
26
27
		$this->log = \Wordlift_Log_Service::get_logger( get_class() );
28
29
		$this->sync_service = $sync_service;
30
31
	}
32
33
	/**
34
	 * This function is called:
35
	 *  - To start a new Synchronization, by passing a {@link Sync_Start_Message} instance.
36
	 *  - To synchronize a post, by passing a numeric ID.
37
	 *
38
	 * This function returns the parameter for the next call or NULL if there are no more posts to process.
39
	 *
40
	 * @param int[] $post_ids An array of post IDs.
41
	 *
42
	 * @return int[]|false The next post IDs or false if there are no more.
43
	 */
44
	protected function task( $post_ids ) {
45
46
		// Check if we must cancel.
47
		if ( $this->must_cancel() ) {
48
			$this->cancel();
49
50
			return false;
51
		}
52
53
		$this->log->debug( sprintf( "Synchronizing posts %s...", implode( ', ', $post_ids ) ) );
54
55
		// Sync the item.
56
		return $this->sync_items( $post_ids );
57
	}
58
59
	/**
60
	 * Start the background processing.
61
	 *
62
	 * @return bool True if the process has been started, otherwise false.
63
	 */
64
	public function start() {
65
66
		// Create a new Sync_Model state of `started`.
67
		if ( ! $this->is_started( self::get_state() ) ) {
68
			$this->log->debug( "Starting..." );
69
70
			$sync_state = new Sync_State( time(), 0, $this->sync_service->count(), time(), 'started' );
71
			update_option( '_wl_dataset_sync', $sync_state, false );
72
73
			$next = $this->sync_service->next();
74
			$this->push_to_queue( $next );
75
			$this->save()->dispatch();
76
77
			$this->log->debug( sprintf( 'Started with post IDs %s.', implode( ', ', $next ) ) );
78
79
			return true;
80
		}
81
82
		return false;
83
	}
84
85
	/**
86
	 * Set the transient to cancel the process. The next time the process runs, it'll check whether this transient is
87
	 * set and will stop processing.
88
	 */
89
	public function request_cancel() {
90
91
		set_transient( "{$this->action}__cancel", true );
92
93
	}
94
95
	/**
96
	 * Get the sync state.
97
	 *
98
	 * @return Sync_State The {@link Sync_State}.
99
	 */
100
	public static function get_state() {
101
102
		try {
103
			return get_option( '_wl_dataset_sync', Sync_State::unknown() );
104
		} catch ( \Exception $e ) {
105
			return Sync_State::unknown();
106
		}
107
108
	}
109
110
	/**
111
	 * Check whether the provided state is `started` or not.
112
	 *
113
	 * @param Sync_State $state The {@link Sync_State}.
114
	 *
115
	 * @return bool True if the state is started.
116
	 */
117
	private function is_started( $state ) {
118
		return $state instanceof Sync_State && 'started' === $state->state && 30 > ( time() - $state->last_update );
119
	}
120
121
	/**
122
	 * Check whether the process must cancel or not.
123
	 *
124
	 * @return bool Whether to cancel or not the process.
125
	 */
126
	private function must_cancel() {
127
128
		return get_transient( "{$this->action}__cancel" );
129
	}
130
131
	/**
132
	 * Cancels the current process.
133
	 */
134
	private function cancel() {
135
136
		$this->log->debug( "Cancelling synchronization..." );
137
138
		// Cleanup the process data.
139
		$this->cancel_process();
140
141
		// Set the state to cancelled.
142
		$state = self::get_state();
143
		$state->set_state( 'cancelled' );
144
		update_option( '_wl_dataset_sync', $state, false );
145
146
		// Finally delete the transient.
147
		delete_transient( "{$this->action}__cancel" );
148
149
	}
150
151
	/**
152
	 * Push the post with the provided ID to the remote platform.
153
	 *
154
	 * @param int[] $post_ids The post IDs.
155
	 *
156
	 * @return int[]|false The next post ID to process or false if processing is complete.
157
	 */
158
	private function sync_items( $post_ids ) {
159
160
		if ( ! is_array( $post_ids ) ) {
161
			$this->log->error( '$post_ids must be an array, received: ' . var_export( $post_ids, true ) );
162
			return false;
163
		}
164
165
		// Sync this item.
166
		if ( $this->sync_service->sync_items( $post_ids ) ) {
167
168
			$next       = $this->sync_service->next();
169
			$next_state = isset( $next ) ? 'started' : 'ended';
170
171
			/**
172
			 * Update the synchronization meta data, by increasing the current index.
173
			 *
174
			 * @var Sync_State $sync The {@link Sync_State}.
175
			 */
176
			$state = self::get_state()
177
			             ->increment_index( $this->sync_service->get_batch_size() )
178
			             ->set_state( $next_state );
179
			update_option( '_wl_dataset_sync', $state, false );
180
181
			$this->log->debug( "State updated to " . var_export( $state, true ) );
182
183
			// Return the next IDs or false if there aren't.
184
			return isset( $next ) ? $next : false;
185
		} else {
186
			// Retry.
187
			// @@todo: put a limit to the number of retries.
188
189
			$this->log->error( sprintf( "Sync failed for posts %s.", implode( ', ', $post_ids ) ) );
190
191
			return $post_ids;
192
		}
193
194
	}
195
196
}
197