1<?php 2/** 3 * @package Habari 4 * 5 */ 6 7/** 8 * Static class to build and read cron entries 9 * 10 */ 11class CronTab extends ActionHandler 12{ 13 /** 14 * Executes all cron jobs in the DB if there are any to run. 15 * 16 * @param boolean $async If true, allows execution to continue by making an asynchronous request to a cron URL 17 */ 18 static function run_cron( $async = false ) 19 { 20 // check if it's time to run crons, and if crons are already running. 21 $next_cron = HabariDateTime::date_create( Options::get( 'next_cron' ) ); 22 $time = HabariDateTime::date_create(); 23 if ( ( $next_cron->int > $time->int ) 24 || ( Options::get( 'cron_running' ) && Options::get( 'cron_running' ) > microtime( true ) ) 25 ) { 26 return; 27 } 28 29 // cron_running will timeout in 10 minutes 30 // round cron_running to 4 decimals 31 $run_time = microtime( true ) + 600; 32 $run_time = sprintf( "%.4f", $run_time ); 33 Options::set( 'cron_running', $run_time ); 34 35 if ( $async ) { 36 // Timeout is really low so that it doesn't wait for the request to finish 37 $cronurl = URL::get( 'cron', 38 array( 39 'time' => $run_time, 40 'asyncronous' => Utils::crypt( Options::get( 'GUID' ) ) ) 41 ); 42 $request = new RemoteRequest( $cronurl, 'GET', 1 ); 43 44 try { 45 $request->execute(); 46 } 47 catch ( RemoteRequest_Timeout $e ) { 48 // the request timed out - we knew that would happen 49 } 50 catch ( Exception $e ) { 51 // some other error occurred. we still don't care 52 } 53 } 54 else { 55 // @todo why do we usleep() and why don't we just call act_poll_cron()? 56 usleep( 5000 ); 57 if ( Options::get( 'cron_running' ) != $run_time ) { 58 return; 59 } 60 61 $time = HabariDateTime::date_create(); 62 $crons = DB::get_results( 63 'SELECT * FROM {crontab} WHERE start_time <= ? AND next_run <= ?', 64 array( $time->sql, $time->sql ), 65 'CronJob' 66 ); 67 if ( $crons ) { 68 foreach ( $crons as $cron ) { 69 $cron->execute(); 70 } 71 } 72 73 EventLog::log( _t( 'CronTab run completed.' ), 'debug', 'crontab', 'habari', $crons ); 74 75 // set the next run time to the lowest next_run OR a max of one day. 76 $next_cron = DB::get_value( 'SELECT next_run FROM {crontab} ORDER BY next_run ASC LIMIT 1', array() ); 77 Options::set( 'next_cron', min( intval( $next_cron ), $time->modify( '+1 day' )->int ) ); 78 Options::set( 'cron_running', false ); 79 } 80 } 81 82 /** 83 * Handles asyncronous cron calls. 84 * 85 * @todo next_cron should be the actual next run time and update it when new 86 * crons are added instead of just maxing out at one day.. 87 */ 88 function act_poll_cron() 89 { 90 Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) ); 91 92 $time = doubleval( $this->handler_vars['time'] ); 93 if ( $time != Options::get( 'cron_running' ) ) { 94 return; 95 } 96 97 // allow script to run for 10 minutes. This only works on host with safe mode DISABLED 98 if ( !ini_get( 'safe_mode' ) ) { 99 set_time_limit( 600 ); 100 } 101 $time = HabariDateTime::date_create(); 102 $crons = DB::get_results( 103 'SELECT * FROM {crontab} WHERE start_time <= ? AND next_run <= ?', 104 array( $time->sql, $time->sql ), 105 'CronJob' 106 ); 107 108 if ( $crons ) { 109 foreach ( $crons as $cron ) { 110 $cron->execute(); 111 } 112 } 113 114 // set the next run time to the lowest next_run OR a max of one day. 115 $next_cron = DB::get_value( 'SELECT next_run FROM {crontab} ORDER BY next_run ASC LIMIT 1', array() ); 116 Options::set( 'next_cron', min( intval( $next_cron ), $time->modify( '+1 day' )->int ) ); 117 Options::set( 'cron_running', false ); 118 } 119 120 /** 121 * Get a Cron Job by name or id from the Database. 122 * 123 * @param mixed $name The name or id of the cron job to retreive. 124 * @return CronJob The cron job retreived from the DB 125 */ 126 static function get_cronjob( $name ) 127 { 128 if ( is_int( $name ) ) { 129 $cron = DB::get_row( 'SELECT * FROM {crontab} WHERE cron_id = ?', array( $name ), 'CronJob' ); 130 } 131 else { 132 $cron = DB::get_row( 'SELECT * FROM {crontab} WHERE name = ?', array( $name ), 'CronJob' ); 133 } 134 return $cron; 135 } 136 137 /** 138 * Delete a Cron Job by name or id from the Database. 139 * 140 * @param mixed $name The name or id of the cron job to delete. 141 * @return bool Wheather or not the delete was successfull 142 */ 143 static function delete_cronjob( $name ) 144 { 145 $cron = self::get_cronjob( $name ); 146 if ( $cron ) { 147 return $cron->delete(); 148 } 149 return false; 150 } 151 152 /** 153 * Add a new cron job to the DB. 154 * 155 * @see CronJob 156 * @param array $paramarray A paramarray of cron job feilds. 157 */ 158 static function add_cron( $paramarray ) 159 { 160 $cron = new CronJob( $paramarray ); 161 $result = $cron->insert(); 162 163 //If the new cron should run earlier than the others, rest next_cron to its strat time. 164 $next_cron = DB::get_value( 'SELECT next_run FROM {crontab} ORDER BY next_run ASC LIMIT 1', array() ); 165 if ( intval( Options::get( 'next_cron' ) ) > intval( $next_cron ) ) { 166 Options::set( 'next_cron', $next_cron ); 167 } 168 return $result; 169 } 170 171 /** 172 * Add a new cron job to the DB, that runs only once. 173 * 174 * @param string $name The name of the cron job. 175 * @param mixed $callback The callback function or plugin action for the cron job to execute. 176 * @param HabariDateTime $run_time The time to execute the cron. 177 * @param string $description The description of the cron job. 178 */ 179 static function add_single_cron( $name, $callback, $run_time, $description = '' ) 180 { 181 $paramarray = array( 182 'name' => $name, 183 'callback' => $callback, 184 'start_time' => $run_time, 185 'end_time' => $run_time, // only run once 186 'description' => $description 187 ); 188 return self::add_cron( $paramarray ); 189 } 190 191 /** 192 * Add a new cron job to the DB, that runs hourly. 193 * 194 * @param string $name The name of the cron job. 195 * @param mixed $callback The callback function or plugin action for the cron job to execute. 196 * @param string $description The description of the cron job. 197 */ 198 static function add_hourly_cron( $name, $callback, $description = '' ) 199 { 200 $paramarray = array( 201 'name' => $name, 202 'callback' => $callback, 203 'increment' => 3600, // one hour 204 'description' => $description 205 ); 206 return self::add_cron( $paramarray ); 207 } 208 209 /** 210 * Add a new cron job to the DB, that runs daily. 211 * 212 * @param string $name The name of the cron job. 213 * @param mixed $callback The callback function or plugin action for the cron job to execute. 214 * @param string $description The description of the cron job. 215 */ 216 static function add_daily_cron( $name, $callback, $description = '' ) 217 { 218 $paramarray = array( 219 'name' => $name, 220 'callback' => $callback, 221 'increment' => 86400, // one day 222 'description' => $description 223 ); 224 return self::add_cron( $paramarray ); 225 } 226 227 /** 228 * Add a new cron job to the DB, that runs weekly. 229 * 230 * @param string $name The name of the cron job. 231 * @param mixed $callback The callback function or plugin action for the cron job to execute. 232 * @param string $description The description of the cron job. 233 */ 234 static function add_weekly_cron( $name, $callback, $description = '' ) 235 { 236 $paramarray = array( 237 'name' => $name, 238 'callback' => $callback, 239 'increment' => 604800, // one week (7 days) 240 'description' => $description 241 ); 242 return self::add_cron( $paramarray ); 243 } 244 245 /** 246 * Add a new cron job to the DB, that runs monthly. 247 * 248 * @param string $name The name of the cron job. 249 * @param mixed $callback The callback function or plugin action for the cron job to execute. 250 * @param string $description The description of the cron job. 251 */ 252 static function add_monthly_cron( $name, $callback, $description = '' ) 253 { 254 $paramarray = array( 255 'name' => $name, 256 'callback' => $callback, 257 'increment' => 2592000, // one month (30 days) 258 'description' => $description 259 ); 260 return self::add_cron( $paramarray ); 261 } 262} 263 264?> 265