PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 5.4.2
WooCommerce Square v5.4.2
5.4.2 5.4.1 5.4.0 trunk 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.2.0 3.3.0 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.7.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 4.0.0 4.1.0 4.2.0 4.2.1 4.2.2 4.2.3 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.4.2 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.9.0 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 4.9.9 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.2.0 5.3.0 5.3.1 5.3.2 5.3.3
woocommerce-square / includes / Handlers / Background_Job.php
woocommerce-square / includes / Handlers Last commit date
Product 1 month ago Async_Request.php 2 years ago Background_Job.php 2 months ago Category.php 1 month ago Connection.php 4 months ago Email.php 1 year ago Order.php 1 day ago Order_Sync.php 10 months ago Product.php 1 month ago Products.php 1 day ago Sync.php 3 years ago
Background_Job.php
336 lines
1 <?php
2 /**
3 * WooCommerce Square
4 *
5 * This source file is subject to the GNU General Public License v3.0
6 * that is bundled with this package in the file license.txt.
7 * It is also available through the world-wide-web at this URL:
8 * http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
9 * If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to license@woocommerce.com so we can send you a copy immediately.
12 *
13 * DISCLAIMER
14 *
15 * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16 * versions in the future. If you wish to customize WooCommerce Square for your
17 * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18 *
19 */
20
21 namespace WooCommerce\Square\Handlers;
22
23 use WooCommerce\Square\Framework\Utilities\Background_Job_Handler;
24 use WooCommerce\Square\Sync\Job;
25 use WooCommerce\Square\Sync\Records;
26 use WooCommerce\Square\Sync\Interval_Polling;
27 use WooCommerce\Square\Sync\Manual_Synchronization;
28 use WooCommerce\Square\Sync\Product_Import;
29
30 defined( 'ABSPATH' ) || exit;
31
32 /**
33 * Product and Inventory Synchronization handler class.
34 *
35 * This class handles manual and interval synchronization jobs.
36 * It is a wrapper for the framework background handler and as such it only handles loopback business to keep the queue processing.
37 * See the individual job implementations:
38 *
39 * @see Manual_Synchronization manual jobs re-process ALL synced products
40 * @see Interval_Polling interval (polling) jobs perform API requests for ONLY the latest changes and update the associated products
41 *
42 * @since 2.0.0
43 */
44 class Background_Job extends Background_Job_Handler {
45
46
47 /**
48 * Initializes the background sync handler.
49 *
50 * @since 2.0.0
51 */
52 public function __construct() {
53
54 $this->prefix = 'wc_square';
55 $this->action = 'background_sync';
56 $this->data_key = 'product_ids';
57
58 parent::__construct();
59
60 add_action( "{$this->identifier}_job_complete", array( $this, 'job_complete' ) );
61 add_action( "{$this->identifier}_job_failed", array( $this, 'job_failed' ) );
62 add_filter( 'woocommerce_debug_tools', array( $this, 'add_debug_tool' ) );
63 add_action( 'wc_square_job_runner', array( $this, 'handle' ) );
64
65 // Sync healthcheck
66 add_action( $this->cron_hook_identifier, array( $this, 'handle_sync_healthcheck' ) );
67 }
68
69
70 /**
71 * Creates a new job.
72 *
73 * @since 2.0.0
74 *
75 * @param array $attrs array of job attributes
76 * @return \stdClass|null
77 */
78 public function create_job( $attrs ) {
79
80 $sor = wc_square()->get_settings_handler()->get_system_of_record();
81
82 return parent::create_job(
83 wp_parse_args(
84 $attrs,
85 array(
86 'action' => '', // job action
87 'catalog_processed' => false, // whether the Square catalog has been processed
88 'cursor' => '', // job advancement position
89 'manual' => false, // whether it's a sync job triggered manually
90 'percentage' => 0, // percentage completed
91 'product_ids' => array(), // products to process
92 'processed_product_ids' => array(), // newly imported products processed
93 'updated_product_ids' => array(), // updated products processed
94 'skipped_products' => array(), // remote product IDs that were skipped
95 'system_of_record' => $sor, // Sync setting used
96 )
97 )
98 );
99 }
100
101
102 /**
103 * Handles job execution.
104 *
105 * Overridden to support our multi-step job structure. There are steps that can take a long time to process, so this
106 * ensures only one step is performed for each background request.
107 *
108 * @since 2.0.0
109 */
110 public function handle() {
111
112 // Schedule sync healthcheck event if not already scheduled.
113 $this->schedule_event();
114
115 $this->lock_process();
116
117 // Get next job in the queue
118 $job = $this->get_job();
119
120 // handle PHP errors from here on out
121 register_shutdown_function( array( $this, 'handle_shutdown' ), $job );
122
123 // Start processing
124 $this->process_job( $job );
125
126 $this->unlock_process();
127
128 // Start next job or complete process
129 if ( ! $this->is_queue_empty() ) {
130 // If the job has a retry count set, we'll retry the job after a delay.
131 if ( isset( $job->retry ) && is_numeric( $job->retry ) && $job->retry > 0 ) {
132 $base_delay = 30; // Base delay in seconds for rate limit errors. 30 seconds.
133 $delay = $base_delay * ( pow( 2, $job->retry ) );
134 wc_square()->log( "Retrying in {$delay} seconds." );
135 as_schedule_single_action( time() + $delay, 'wc_square_job_runner' );
136 } else {
137 as_enqueue_async_action( 'wc_square_job_runner' );
138 }
139 } else {
140 $this->complete();
141 }
142 }
143
144
145 /**
146 * Processes a background job.
147 *
148 * @since 2.0.0
149 *
150 * @param object|\stdClass $job
151 * @param null $items_per_batch
152 * @return false|object|\stdClass
153 */
154 public function process_job( $job, $items_per_batch = null ) {
155
156 if ( ! $job ) {
157 return;
158 }
159
160 // indicate that the job has started processing
161 if ( 'processing' !== $job->status ) {
162
163 $job->status = 'processing';
164 $job->started_processing_at = current_time( 'mysql' );
165 $job = $this->update_job( $job );
166 }
167
168 if ( 'poll' === $job->action ) {
169
170 $job = new Interval_Polling( $job );
171
172 } elseif ( 'product_import' === $job->action ) {
173
174 $job = new Product_Import( $job );
175
176 } elseif ( ! empty( $job->manual ) ) {
177
178 $job = new Manual_Synchronization( $job );
179 }
180
181 if ( $job instanceof Job ) {
182 $current_user_id = get_current_user_id();
183 $job = $job->run();
184 wp_set_current_user( $current_user_id ); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Discouraged -- required for background job processing
185 }
186
187 return $job;
188 }
189
190
191 /**
192 * Handles actions after a sync job is complete.
193 *
194 * @since 2.0.0
195 *
196 * @param $job
197 */
198 public function job_complete( $job ) {
199
200 wc_square()->get_sync_handler()->set_last_synced_at();
201
202 wc_square()->get_sync_handler()->record_sync( $job->processed_product_ids, $job );
203
204 wc_square()->get_email_handler()->get_sync_completed_email()->trigger( $job );
205 }
206
207
208 /**
209 * Handles actions after a sync job has failed.
210 *
211 * @since 2.0.0
212 *
213 * @param $job
214 */
215 public function job_failed( $job ) {
216
217 Records::set_record(
218 array(
219 'type' => 'failed',
220 'message' => __( 'Sync failed. Please try again', 'woocommerce-square' ),
221 )
222 );
223
224 wc_square()->get_email_handler()->get_sync_completed_email()->trigger( $job );
225 }
226
227
228 /**
229 * No-op: implements framework parent abstract method.
230 *
231 * @since 2.0.0
232 *
233 * @param null $item
234 * @param \stdClass $job
235 */
236 protected function process_item( $item, $job ) {}
237
238 /**
239 * Adds some helpful debug tools.
240 *
241 * @since 2.0.0
242 *
243 * @param array $tools existing debug tools
244 * @return array
245 */
246 public function add_debug_tool( $tools ) {
247
248 // this key is not unique to the plugin to avoid duplicate tools
249 $tools['wc_square_clear_background_jobs'] = array(
250 'name' => __( 'Clear Square Sync', 'woocommerce-square' ),
251 'button' => __( 'Clear', 'woocommerce-square' ),
252 'desc' => __( 'This tool will clear any ongoing Square product syncs.', 'woocommerce-square' ),
253 'callback' => array( $this, 'run_clear_background_jobs' ),
254 );
255
256 return $tools;
257 }
258
259
260 /**
261 * Clear all background jobs of any status.
262 *
263 * @since 2.0.0
264 */
265 public function clear_all_jobs() {
266
267 $jobs = $this->get_jobs();
268
269 if ( is_array( $jobs ) ) {
270 $this->delete_jobs( $jobs );
271 }
272
273 delete_transient( 'wc_square_background_sync_process_lock' );
274 }
275
276
277 /**
278 * Deletes a set of background jobs.
279 *
280 * @since 2.0.0
281 *
282 * @param object[] $jobs jobs to delete
283 */
284 public function delete_jobs( $jobs ) {
285
286 foreach ( $jobs as $job ) {
287 $this->delete_job( $job );
288 }
289 }
290
291 /**
292 * Runs the "Clear Square Sync" tool.
293 *
294 * Provides a way for merchants to clear any ongoing or stuck product syncs.
295 *
296 * @since 2.0.0
297 */
298 public function run_clear_background_jobs() {
299
300 $this->clear_all_jobs();
301
302 $this->debug_message = esc_html__( 'Success! You can now sync your products.', 'woocommerce-square' );
303
304 return true;
305 }
306
307 /**
308 * Handle Sync healthcheck
309 *
310 * Restart the background sync process if not already running
311 * and data exists in the queue.
312 *
313 * @since 3.8.2
314 */
315 public function handle_sync_healthcheck() {
316
317 if ( $this->is_process_running() ) {
318 // background process already running
319 return;
320 }
321
322 if ( $this->is_queue_empty() ) {
323 // no data to process
324 return;
325 }
326
327 if ( as_has_scheduled_action( 'wc_square_job_runner' ) ) {
328 // scheduled action for trigger sync is already exists
329 return;
330 }
331
332 // Start the sync process
333 as_enqueue_async_action( 'wc_square_job_runner' );
334 }
335 }
336