PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/application/libraries/CronParser.php

http://github.com/ushahidi/Ushahidi_Web
PHP | 604 lines | 474 code | 60 blank | 70 comment | 67 complexity | b6c99abef76cb6a306ab66767f282428 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php /* $Id: CronParser.php,v 1.7 2005/09/12 01:04:05 ns Exp $ */
  2. /**####################################################################################################**\
  3. Version: V1.01
  4. Release Date: 12 Sep 2005
  5. Licence: GPL
  6. By: Nikol S
  7. Please send bug reports to ns@eyo.com.au
  8. \**####################################################################################################**/
  9. /* This class is based on the concept in the CronParser class written by Mick Sear http://www.ecreate.co.uk
  10. * The following functions are direct copies from or based on the original class:
  11. * getLastRan(), getDebug(), debug(), expand_ranges()
  12. *
  13. * Who can use this class?
  14. * This class is idea for people who can not use the traditional Unix cron through shell.
  15. * One way of using is embedding the calling script in a web page which is often visited.
  16. * The script will work out the last due time, by comparing with run log timestamp. The scrip
  17. * will envoke any scripts needed to run, be it deleting older table records, or updating prices.
  18. * It can parse the same cron string used by Unix.
  19. */
  20. /* Usage example:
  21. $cron_str0 = "0,12,30-51 3,21-23,10 1-25 9-12,1 0,3-7";
  22. require_once("CronParser.php");
  23. $cron = new CronParser();
  24. $cron->calcLastRan($cron_str0);
  25. // $cron->getLastRanUnix() returns an Unix timestamp
  26. echo "Cron '$cron_str0' last due at: " . date('r', $cron->getLastRanUnix()) . "<p>";
  27. // $cron->getLastRan() returns last due time in an array
  28. print_r($cron->getLastRan());
  29. echo "Debug:<br>" . nl2br($cron->getDebug());
  30. $cron_str1 = "3 12 * * *";
  31. if ($cron->calcLastRan($cron_str1))
  32. {
  33. echo "<p>Cron '$cron_str1' last due at: " . date('r', $cron->getLastRanUnix()) . "<p>";
  34. print_r($cron->getLastRan());
  35. }
  36. else
  37. {
  38. echo "Error parsing";
  39. }
  40. echo "Debug:<br>" . nl2br($cron->getDebug());
  41. *#######################################################################################################
  42. */
  43. class CronParser
  44. {
  45. var $bits = Array(); //exploded String like 0 1 * * *
  46. var $now = Array(); //Array of cron-style entries for time()
  47. var $lastRan; //Timestamp of last ran time.
  48. var $taken;
  49. var $debug;
  50. var $year;
  51. var $month;
  52. var $day;
  53. var $hour;
  54. var $minute;
  55. var $minutes_arr = array(); //minutes array based on cron string
  56. var $hours_arr = array(); //hours array based on cron string
  57. var $months_arr = array(); //months array based on cron string
  58. function getLastRan()
  59. {
  60. return explode(",", strftime("%M,%H,%d,%m,%w,%Y", $this->lastRan)); //Get the values for now in a format we can use
  61. }
  62. function getLastRanUnix()
  63. {
  64. return $this->lastRan;
  65. }
  66. function getDebug()
  67. {
  68. return $this->debug;
  69. }
  70. function debug($str)
  71. {
  72. if (is_array($str))
  73. {
  74. $this->debug .= "\nArray: ";
  75. foreach($str as $k=>$v)
  76. {
  77. $this->debug .= "$k=>$v, ";
  78. }
  79. }
  80. else
  81. {
  82. $this->debug .= "\n$str";
  83. }
  84. //echo nl2br($this->debug);
  85. }
  86. /**
  87. * Assumes that value is not *, and creates an array of valid numbers that
  88. * the string represents. Returns an array.
  89. */
  90. function expand_ranges($str)
  91. {
  92. if (strstr($str, ","))
  93. {
  94. $arParts = explode(',', $str);
  95. foreach ($arParts AS $part)
  96. {
  97. if (strstr($part, '-'))
  98. {
  99. $arRange = explode('-', $part);
  100. for ($i = $arRange[0]; $i <= $arRange[1]; $i++)
  101. {
  102. $ret[] = $i;
  103. }
  104. }
  105. else
  106. {
  107. $ret[] = $part;
  108. }
  109. }
  110. }
  111. elseif (strstr($str, '-'))
  112. {
  113. $arRange = explode('-', $str);
  114. for ($i = $arRange[0]; $i <= $arRange[1]; $i++)
  115. {
  116. $ret[] = $i;
  117. }
  118. }
  119. else
  120. {
  121. $ret[] = $str;
  122. }
  123. $ret = array_unique($ret);
  124. sort($ret);
  125. return $ret;
  126. }
  127. function daysinmonth($month, $year)
  128. {
  129. return date('t', mktime(0, 0, 0, $month, 1, $year));
  130. }
  131. /**
  132. * Calculate the last due time before this moment
  133. */
  134. function calcLastRan($string)
  135. {
  136. $tstart = microtime();
  137. $this->debug = "";
  138. $this->lastRan = 0;
  139. $this->year = NULL;
  140. $this->month = NULL;
  141. $this->day = NULL;
  142. $this->hour = NULL;
  143. $this->minute = NULL;
  144. $this->hours_arr = array();
  145. $this->minutes_arr = array();
  146. $this->months_arr = array();
  147. $string = preg_replace('/[\s]{2,}/', ' ', $string);
  148. if (preg_match('/[^-,* \\d]/', $string) !== 0)
  149. {
  150. $this->debug("Cron String contains invalid character");
  151. return false;
  152. }
  153. $this->debug("<b>Working on cron schedule: $string</b>");
  154. $this->bits = @explode(" ", $string);
  155. if (count($this->bits) != 5)
  156. {
  157. $this->debug("Cron string is invalid. Too many or too little sections after explode");
  158. return false;
  159. }
  160. //put the current time into an array
  161. $t = strftime("%M,%H,%d,%m,%w,%Y", time());
  162. $this->now = explode(",", $t);
  163. $this->year = $this->now[5];
  164. $arMonths = $this->_getMonthsArray();
  165. do
  166. {
  167. $this->month = array_pop($arMonths);
  168. }
  169. while ($this->month > $this->now[3]);
  170. if ($this->month === NULL)
  171. {
  172. $this->year = $this->year - 1;
  173. $this->debug("Not due within this year. So checking the previous year " . $this->year);
  174. $arMonths = $this->_getMonthsArray();
  175. $this->_prevMonth($arMonths);
  176. }
  177. elseif ($this->month == $this->now[3]) //now Sep, month = array(7,9,12)
  178. {
  179. $this->debug("Cron is due this month, getting days array.");
  180. $arDays = $this->_getDaysArray($this->month, $this->year);
  181. do
  182. {
  183. $this->day = array_pop($arDays);
  184. }
  185. while ($this->day > $this->now[2]);
  186. if ($this->day === NULL)
  187. {
  188. $this->debug("Smallest day is even greater than today");
  189. $this->_prevMonth($arMonths);
  190. }
  191. elseif ($this->day == $this->now[2])
  192. {
  193. $this->debug("Due to run today");
  194. $arHours = $this->_getHoursArray();
  195. do
  196. {
  197. $this->hour = array_pop($arHours);
  198. }
  199. while ($this->hour > $this->now[1]);
  200. if ($this->hour === NULL) // now =2, arHours = array(3,5,7)
  201. {
  202. $this->debug("Not due this hour and some earlier hours, so go for previous day");
  203. $this->_prevDay($arDays, $arMonths);
  204. }
  205. elseif ($this->hour < $this->now[1]) //now =2, arHours = array(1,3,5)
  206. {
  207. $this->minute = $this->_getLastMinute();
  208. }
  209. else // now =2, arHours = array(1,2,5)
  210. {
  211. $this->debug("Due this hour");
  212. $arMinutes = $this->_getMinutesArray();
  213. do
  214. {
  215. $this->minute = array_pop($arMinutes);
  216. }
  217. while ($this->minute > $this->now[0]);
  218. if ($this->minute === NULL)
  219. {
  220. $this->debug("Not due this minute, so go for previous hour.");
  221. $this->_prevHour($arHours, $arDays, $arMonths);
  222. }
  223. else
  224. {
  225. $this->debug("Due this very minute or some earlier minutes before this moment within this hour.");
  226. }
  227. }
  228. }
  229. else
  230. {
  231. $this->debug("Cron was due on " . $this->day . " of this month");
  232. $this->hour = $this->_getLastHour();
  233. $this->minute = $this->_getLastMinute();
  234. }
  235. }
  236. else //now Sep, arrMonths=array(7, 10)
  237. {
  238. $this->debug("Cron was due before this month. Previous month is: " . $this->year . '-' . $this->month);
  239. $this->day = $this->_getLastDay($this->month, $this->year);
  240. if ($this->day === NULL)
  241. {
  242. //No scheduled date within this month. So we will try the previous month in the month array
  243. $this->_prevMonth($arMonths);
  244. }
  245. else
  246. {
  247. $this->hour = $this->_getLastHour();
  248. $this->minute = $this->_getLastMinute();
  249. }
  250. }
  251. $tend = microtime();
  252. $this->taken = $tend - $tstart;
  253. $this->debug("Parsing $string taken " . $this->taken . " seconds");
  254. //if the last due is beyond 1970
  255. if ($this->minute === NULL)
  256. {
  257. $this->debug("Error calculating last due time");
  258. return false;
  259. }
  260. else
  261. {
  262. $this->debug("LAST DUE: " . $this->hour . ":" . $this->minute . " on " . $this->day . "/" . $this->month . "/" . $this->year);
  263. $this->lastRan = mktime($this->hour, $this->minute, 0, $this->month, $this->day, $this->year);
  264. return true;
  265. }
  266. }
  267. //get the due time before current month
  268. function _prevMonth($arMonths)
  269. {
  270. $this->month = array_pop($arMonths);
  271. if ($this->month === NULL)
  272. {
  273. $this->year = $this->year -1;
  274. if ($this->year <= 1970)
  275. {
  276. $this->debug("Can not calculate last due time. At least not before 1970..");
  277. }
  278. else
  279. {
  280. $this->debug("Have to go for previous year " . $this->year);
  281. $arMonths = $this->_getMonthsArray();
  282. $this->_prevMonth($arMonths);
  283. }
  284. }
  285. else
  286. {
  287. $this->debug("Getting the last day for previous month: " . $this->year . '-' . $this->month);
  288. $this->day = $this->_getLastDay($this->month, $this->year);
  289. if ($this->day === NULL)
  290. {
  291. //no available date schedule in this month
  292. $this->_prevMonth($arMonths);
  293. }
  294. else
  295. {
  296. $this->hour = $this->_getLastHour();
  297. $this->minute = $this->_getLastMinute();
  298. }
  299. }
  300. }
  301. //get the due time before current day
  302. function _prevDay($arDays, $arMonths)
  303. {
  304. $this->debug("Go for the previous day");
  305. $this->day = array_pop($arDays);
  306. if ($this->day === NULL)
  307. {
  308. $this->debug("Have to go for previous month");
  309. $this->_prevMonth($arMonths);
  310. }
  311. else
  312. {
  313. $this->hour = $this->_getLastHour();
  314. $this->minute = $this->_getLastMinute();
  315. }
  316. }
  317. //get the due time before current hour
  318. function _prevHour($arHours, $arDays, $arMonths)
  319. {
  320. $this->debug("Going for previous hour");
  321. $this->hour = array_pop($arHours);
  322. if ($this->hour === NULL)
  323. {
  324. $this->debug("Have to go for previous day");
  325. $this->_prevDay($arDays, $arMonths);
  326. }
  327. else
  328. {
  329. $this->minute = $this->_getLastMinute();
  330. }
  331. }
  332. //not used at the moment
  333. function _getLastMonth()
  334. {
  335. $months = $this->_getMonthsArray();
  336. $month = array_pop($months);
  337. return $month;
  338. }
  339. function _getLastDay($month, $year)
  340. {
  341. //put the available days for that month into an array
  342. $days = $this->_getDaysArray($month, $year);
  343. $day = array_pop($days);
  344. return $day;
  345. }
  346. function _getLastHour()
  347. {
  348. $hours = $this->_getHoursArray();
  349. $hour = array_pop($hours);
  350. return $hour;
  351. }
  352. function _getLastMinute()
  353. {
  354. $minutes = $this->_getMinutesArray();
  355. $minute = array_pop($minutes);
  356. return $minute;
  357. }
  358. //remove the out of range array elements. $arr should be sorted already and does not contain duplicates
  359. function _sanitize ($arr, $low, $high)
  360. {
  361. $count = count($arr);
  362. for ($i = 0; $i <= ($count - 1); $i++)
  363. {
  364. if ($arr[$i] < $low)
  365. {
  366. $this->debug("Remove out of range element. {$arr[$i]} is outside $low - $high");
  367. unset($arr[$i]);
  368. }
  369. else
  370. {
  371. break;
  372. }
  373. }
  374. for ($i = ($count - 1); $i >= 0; $i--)
  375. {
  376. if ($arr[$i] > $high)
  377. {
  378. $this->debug("Remove out of range element. {$arr[$i]} is outside $low - $high");
  379. unset ($arr[$i]);
  380. }
  381. else
  382. {
  383. break;
  384. }
  385. }
  386. //re-assign keys
  387. sort($arr);
  388. return $arr;
  389. }
  390. //given a month/year, list all the days within that month fell into the week days list.
  391. function _getDaysArray($month, $year = 0)
  392. {
  393. if ($year == 0)
  394. {
  395. $year = $this->year;
  396. }
  397. $days = array();
  398. //return everyday of the month if both bit[2] and bit[4] are '*'
  399. if ($this->bits[2] == '*' AND $this->bits[4] == '*')
  400. {
  401. $days = $this->getDays($month, $year);
  402. }
  403. else
  404. {
  405. //create an array for the weekdays
  406. if ($this->bits[4] == '*')
  407. {
  408. for ($i = 0; $i <= 6; $i++)
  409. {
  410. $arWeekdays[] = $i;
  411. }
  412. }
  413. else
  414. {
  415. $arWeekdays = $this->expand_ranges($this->bits[4]);
  416. $arWeekdays = $this->_sanitize($arWeekdays, 0, 7);
  417. //map 7 to 0, both represents Sunday. Array is sorted already!
  418. if (in_array(7, $arWeekdays))
  419. {
  420. if (in_array(0, $arWeekdays))
  421. {
  422. array_pop($arWeekdays);
  423. }
  424. else
  425. {
  426. $tmp[] = 0;
  427. array_pop($arWeekdays);
  428. $arWeekdays = array_merge($tmp, $arWeekdays);
  429. }
  430. }
  431. }
  432. $this->debug("Array for the weekdays");
  433. $this->debug($arWeekdays);
  434. if ($this->bits[2] == '*')
  435. {
  436. $daysmonth = $this->getDays($month, $year);
  437. }
  438. else
  439. {
  440. $daysmonth = $this->expand_ranges($this->bits[2]);
  441. // so that we do not end up with 31 of Feb
  442. $daysinmonth = $this->daysinmonth($month, $year);
  443. $daysmonth = $this->_sanitize($daysmonth, 1, $daysinmonth);
  444. }
  445. //Now match these days with weekdays
  446. foreach ($daysmonth AS $day)
  447. {
  448. $wkday = date('w', mktime(0, 0, 0, $month, $day, $year));
  449. if (in_array($wkday, $arWeekdays))
  450. {
  451. $days[] = $day;
  452. }
  453. }
  454. }
  455. $this->debug("Days array matching weekdays for $year-$month");
  456. $this->debug($days);
  457. return $days;
  458. }
  459. //given a month/year, return an array containing all the days in that month
  460. function getDays($month, $year)
  461. {
  462. $daysinmonth = $this->daysinmonth($month, $year);
  463. $this->debug("Number of days in $year-$month : $daysinmonth");
  464. $days = array();
  465. for ($i = 1; $i <= $daysinmonth; $i++)
  466. {
  467. $days[] = $i;
  468. }
  469. return $days;
  470. }
  471. function _getHoursArray()
  472. {
  473. if (empty($this->hours_arr))
  474. {
  475. $hours = array();
  476. if ($this->bits[1] == '*')
  477. {
  478. for ($i = 0; $i <= 23; $i++)
  479. {
  480. $hours[] = $i;
  481. }
  482. }
  483. else
  484. {
  485. $hours = $this->expand_ranges($this->bits[1]);
  486. $hours = $this->_sanitize($hours, 0, 23);
  487. }
  488. $this->debug("Hour array");
  489. $this->debug($hours);
  490. $this->hours_arr = $hours;
  491. }
  492. return $this->hours_arr;
  493. }
  494. function _getMinutesArray()
  495. {
  496. if (empty($this->minutes_arr))
  497. {
  498. $minutes = array();
  499. if ($this->bits[0] == '*')
  500. {
  501. for ($i = 0; $i <= 60; $i++)
  502. {
  503. $minutes[] = $i;
  504. }
  505. }
  506. else
  507. {
  508. $minutes = $this->expand_ranges($this->bits[0]);
  509. $minutes = $this->_sanitize($minutes, 0, 59);
  510. }
  511. $this->debug("Minutes array");
  512. $this->debug($minutes);
  513. $this->minutes_arr = $minutes;
  514. }
  515. return $this->minutes_arr;
  516. }
  517. function _getMonthsArray()
  518. {
  519. if (empty($this->months_arr))
  520. {
  521. $months = array();
  522. if ($this->bits[3] == '*')
  523. {
  524. for ($i = 1; $i <= 12; $i++)
  525. {
  526. $months[] = $i;
  527. }
  528. }
  529. else
  530. {
  531. $months = $this->expand_ranges($this->bits[3]);
  532. $months = $this->_sanitize($months, 1, 12);
  533. }
  534. $this->debug("Months array");
  535. $this->debug($months);
  536. $this->months_arr = $months;
  537. }
  538. return $this->months_arr;
  539. }
  540. }
  541. ?>