'Lkr5w⪡r0 Qr5qAr / BrOtACrs`ur( Urf4i&Oir@qr br !@ao-0r q Urx@0 $uq跔rxTr; &>y}rJ!Idaur trxuq$r qrwZ,rH59 TCr<rQu?02;r{ r,'<Ylrxo6nr"Tq\rKr8לqwRr 9`crw rl q6`r<GBGrlq0Lr| '[ArU ;OMrq1vf7rܺ 1|q!4rTޥ rFh0 LT wv"D"r@Ua?NrBq+ENrrYQbr!\urR3u66(ϚD@ep`@4\blPP!qx'ӘӋTH @~m&!~/vkx([ O&(MV%xbe h@^1$se>nֱgВU pCOSU`%bDxN @L b.D+rbΫ(oD rbhS SJvE Pv1p(D;  78=*Lr1c`уD) TxHSA}xH0h==z2!h C '`Cj@h)xC@h#݀kmXT0h4hN/t2B@Ե0DCQ>,EhDe 0ֈ!8`x++Y]Gg,T1}xqPmtPr|2=d$!=t0P]yNz`+n{0v)H)8^px =tqt >ShttL%txҀq4EqtOҁnt82ּJH@8/.Bl.>/|&T=] M8Cc,@Hb䐭D#iH0X!W-;;d͂*B@ UH$@rAqlrcaźʿԹ^$0p;T0;dJVb0:l1bxWA%{ l+|xrfA@8WgI&dޫd ;aW@h0z$琘x0hpdg+[ $0l8 Fgt{LSnKZ 0h|nnl= D0hD$@җ`0. 08U|z0.l0hĈ9wN9_'|w 0n1tb`F  t s8L0hcF|{B0h5 GCs pSP#p!VlC;@G MG;hSҴr1x$%σn Wv,dǿ!6衃R1)xC̔w@8hX š|BOb¹cgy@q9$b 1q.qu x~~6T;p!y{lC_etA[Vc6h_Btc@tOt(@'8tZ%nCX/h( Qq<_s;HVЕ i2cHK%eh1 zy,#y10ts˸sZ<{1dP[_[8XAD σeġfxXA(˃^Gв81ʖ@I冔414WxsN\a Ac/-q@`K2L1 8ʓc\?cg&,[12$<F yH!o ̓s@3s%v?~11f`XKtA,{{,7ZAe2qCfi*xAسgH C@&OkSxT!p̓::/m 1LuXj۱j(J 1^&{{=Nf1b$~&Scȗ!r|FMГx1|!"!i!@YX}}jDTx1HK ~<U1.􀺨Ew3@sW!11H˓& #v+p@G5 |s|"*1 o}+1&{(>NtH! ̓Gvkl@oD0-p5} if ( ! \rocket_direct_filesystem()->exists( WP_ROCKET_CACHE_ROOT_PATH . '.' . $this->identifier . '_process_cancelled' ) ) { return false; } return true; } /** * Lock process * * Lock the process so that multiple instances can't run simultaneously. * Override if applicable, but the duration should be greater than that * defined in the time_exceeded() method. */ protected function lock_process() { $this->start_time = time(); // Set start time of current process. $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration ); set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration ); } /** * Unlock process * * Unlock the process so that other instances can spawn. * * @return $this */ protected function unlock_process() { delete_site_transient( $this->identifier . '_process_lock' ); return $this; } /** * Get batch * * @return stdClass Return the first batch from the queue */ protected function get_batch() { global $wpdb; $table = $wpdb->options; $column = 'option_name'; $key_column = 'option_id'; $value_column = 'option_value'; if ( is_multisite() ) { $table = $wpdb->sitemeta; $column = 'meta_key'; $key_column = 'meta_id'; $value_column = 'meta_value'; } $key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%'; $query = $wpdb->get_row( $wpdb->prepare( " SELECT * FROM {$table} WHERE {$column} LIKE %s ORDER BY {$key_column} ASC LIMIT 1 ", $key ) ); $batch = new stdClass(); $batch->key = $query->$column; $batch->data = maybe_unserialize( $query->$value_column ); return $batch; } /** * Handle * * Pass each queue item to the task handler, while remaining * within server memory and time limit constraints. */ protected function handle() { $this->lock_process(); do { $batch = $this->get_batch(); foreach ( $batch->data as $key => $value ) { $task = $this->task( $value ); if ( false !== $task ) { $batch->data[ $key ] = $task; } else { unset( $batch->data[ $key ] ); } if ( $this->time_exceeded() || $this->memory_exceeded() || $this->is_process_cancelled() ) { // Batch limits reached. break; } } // Update or delete current batch. if ( ! empty( $batch->data ) && ! $this->is_process_cancelled() ) { $this->update( $batch->key, $batch->data ); } else { $this->complete_batch(); $this->delete( $batch->key ); } } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() && ! $this->is_process_cancelled() ); $this->unlock_process(); // Start next batch or complete process. if ( ! $this->is_queue_empty() ) { $this->dispatch(); } else { $this->complete(); } wp_die(); } /** * Memory exceeded * * Ensures the batch process never exceeds 90% * of the maximum WordPress memory. * * @return bool */ protected function memory_exceeded() { $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory $current_memory = memory_get_usage( true ); $return = false; if ( $current_memory >= $memory_limit ) { $return = true; } return apply_filters( $this->identifier . '_memory_exceeded', $return ); } /** * Get memory limit * * @return int */ protected function get_memory_limit() { if ( function_exists( 'ini_get' ) ) { $memory_limit = ini_get( 'memory_limit' ); } else { // Sensible default. $memory_limit = '128M'; } if ( ! $memory_limit || -1 === intval( $memory_limit ) ) { // Unlimited, set to 32GB. $memory_limit = '32000M'; } return wp_convert_hr_to_bytes( $memory_limit ); } /** * Time exceeded. * * Ensures the batch never exceeds a sensible time limit. * A timeout limit of 30s is common on shared hosting. * * @return bool */ protected function time_exceeded() { $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds $return = false; if ( time() >= $finish ) { $return = true; } return apply_filters( $this->identifier . '_time_exceeded', $return ); } /** * Current batch is completed. */ protected function complete_batch(){ // Override this code on the instantiated process class WP_Rocket_just if needed. } /** * Complete. * * Override if applicable, but ensure that the below actions are * performed, or, call parent::complete(). */ protected function complete() { // Unschedule the cron healthcheck. $this->clear_scheduled_event(); \rocket_direct_filesystem()->delete( WP_ROCKET_CACHE_ROOT_PATH . '.' . $this->identifier . '_process_cancelled' ); } /** * Schedule cron healthcheck * * @param mixed $schedules Schedules. * * @return mixed */ public function schedule_cron_healthcheck( $schedules ) { $interval = apply_filters( $this->identifier . '_cron_interval', 5 ); if ( property_exists( $this, 'cron_interval' ) ) { $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval ); } // Adds every 5 minutes to the existing schedules. $schedules[ $this->identifier . '_cron_interval' ] = array( 'interval' => MINUTE_IN_SECONDS * $interval, 'display' => sprintf( __( 'Every %d Minutes' ), $interval ), ); return $schedules; } /** * Handle cron healthcheck * * Restart the background process if not already running * and data exists in the queue. */ public function handle_cron_healthcheck() { if ( $this->is_process_running() ) { // Background process already running. exit; } if ( $this->is_queue_empty() ) { // No data to process. $this->clear_scheduled_event(); exit; } $this->handle(); exit; } /** * Schedule event */ protected function schedule_event() { if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) { wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier ); } } /** * Clear scheduled event */ protected function clear_scheduled_event() { $timestamp = wp_next_scheduled( $this->cron_hook_identifier ); if ( $timestamp ) { wp_unschedule_event( $timestamp, $this->cron_hook_identifier ); } } /** * Cancel Process * * Stop processing queue items, clear cronjob and delete batch. * */ public function cancel_process() { if ( ! $this->is_queue_empty() ) { $batch = $this->get_batch(); $this->delete( $batch->key ); $this->unlock_process(); wp_clear_scheduled_hook( $this->cron_hook_identifier ); \rocket_direct_filesystem()->touch( WP_ROCKET_CACHE_ROOT_PATH . '.' . $this->identifier . '_process_cancelled' ); } } /** * Task * * Override this method to perform any actions required on each * queue item. Return the modified item WP_Rocket_for further processing * in the next pass through. Or, return false to remove the * item from the queue. * * @param mixed $item Queue item to iterate over. * * @return mixed */ abstract protected function task( $item ); }