PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/app/code/core/Mage/Cron/Model/Observer.php

https://github.com/weburnit/magento-lite
PHP | 255 lines | 157 code | 31 blank | 67 comment | 23 complexity | 4823edb32c336ae4fc5f5b562ec0a514 MD5 | raw file
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Cron
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Crontab observer
  28. *
  29. * @category Mage
  30. * @package Mage_Cron
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Cron_Model_Observer
  34. {
  35. const CACHE_KEY_LAST_SCHEDULE_GENERATE_AT = 'cron_last_schedule_generate_at';
  36. const CACHE_KEY_LAST_HISTORY_CLEANUP_AT = 'cron_last_history_cleanup_at';
  37. const XML_PATH_SCHEDULE_GENERATE_EVERY = 'system/cron/schedule_generate_every';
  38. const XML_PATH_SCHEDULE_AHEAD_FOR = 'system/cron/schedule_ahead_for';
  39. const XML_PATH_SCHEDULE_LIFETIME = 'system/cron/schedule_lifetime';
  40. const XML_PATH_HISTORY_CLEANUP_EVERY = 'system/cron/history_cleanup_every';
  41. const XML_PATH_HISTORY_SUCCESS = 'system/cron/history_success_lifetime';
  42. const XML_PATH_HISTORY_FAILURE = 'system/cron/history_failure_lifetime';
  43. const REGEX_RUN_MODEL = '#^([a-z0-9_]+/[a-z0-9_]+)::([a-z0-9_]+)$#i';
  44. protected $_pendingSchedules;
  45. /**
  46. * Process cron queue
  47. * Geterate tasks schedule
  48. * Cleanup tasks schedule
  49. *
  50. * @param Varien_Event_Observer $observer
  51. */
  52. public function dispatch($observer)
  53. {
  54. $schedules = $this->getPendingSchedules();
  55. $scheduleLifetime = Mage::getStoreConfig(self::XML_PATH_SCHEDULE_LIFETIME) * 60;
  56. $now = time();
  57. $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
  58. foreach ($schedules->getIterator() as $schedule) {
  59. $jobConfig = $jobsRoot->{$schedule->getJobCode()};
  60. if (!$jobConfig || !$jobConfig->run) {
  61. continue;
  62. }
  63. $runConfig = $jobConfig->run;
  64. $time = strtotime($schedule->getScheduledAt());
  65. if ($time > $now) {
  66. continue;
  67. }
  68. try {
  69. $errorStatus = Mage_Cron_Model_Schedule::STATUS_ERROR;
  70. $errorMessage = Mage::helper('cron')->__('Unknown error.');
  71. if ($time < $now - $scheduleLifetime) {
  72. $errorStatus = Mage_Cron_Model_Schedule::STATUS_MISSED;
  73. Mage::throwException(Mage::helper('cron')->__('Too late for the schedule.'));
  74. }
  75. if ($runConfig->model) {
  76. if (!preg_match(self::REGEX_RUN_MODEL, (string)$runConfig->model, $run)) {
  77. Mage::throwException(Mage::helper('cron')->__('Invalid model/method definition, expecting "model/class::method".'));
  78. }
  79. if (!($model = Mage::getModel($run[1])) || !method_exists($model, $run[2])) {
  80. Mage::throwException(Mage::helper('cron')->__('Invalid callback: %s::%s does not exist', $run[1], $run[2]));
  81. }
  82. $callback = array($model, $run[2]);
  83. $arguments = array($schedule);
  84. }
  85. if (empty($callback)) {
  86. Mage::throwException(Mage::helper('cron')->__('No callbacks found'));
  87. }
  88. if (!$schedule->tryLockJob()) {
  89. // another cron started this job intermittently, so skip it
  90. continue;
  91. }
  92. $schedule->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
  93. ->save();
  94. call_user_func_array($callback, $arguments);
  95. $schedule->setStatus(Mage_Cron_Model_Schedule::STATUS_SUCCESS)
  96. ->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
  97. } catch (Exception $e) {
  98. $schedule->setStatus($errorStatus)
  99. ->setMessages($e->__toString());
  100. }
  101. $schedule->save();
  102. }
  103. $this->generate();
  104. $this->cleanup();
  105. }
  106. public function getPendingSchedules()
  107. {
  108. if (!$this->_pendingSchedules) {
  109. $this->_pendingSchedules = Mage::getModel('cron/schedule')->getCollection()
  110. ->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_PENDING)
  111. ->load();
  112. }
  113. return $this->_pendingSchedules;
  114. }
  115. /**
  116. * Generate cron schedule
  117. *
  118. * @return Mage_Cron_Model_Observer
  119. */
  120. public function generate()
  121. {
  122. /**
  123. * check if schedule generation is needed
  124. */
  125. $lastRun = Mage::app()->loadCache(self::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT);
  126. if ($lastRun > time() - Mage::getStoreConfig(self::XML_PATH_SCHEDULE_GENERATE_EVERY)*60) {
  127. return $this;
  128. }
  129. $schedules = $this->getPendingSchedules();
  130. $exists = array();
  131. foreach ($schedules->getIterator() as $schedule) {
  132. $exists[$schedule->getJobCode().'/'.$schedule->getScheduledAt()] = 1;
  133. }
  134. /**
  135. * generate global crontab jobs
  136. */
  137. $config = Mage::getConfig()->getNode('crontab/jobs');
  138. if ($config instanceof Mage_Core_Model_Config_Element) {
  139. $this->_generateJobs($config->children(), $exists);
  140. }
  141. /**
  142. * generate configurable crontab jobs
  143. */
  144. $config = Mage::getConfig()->getNode('default/crontab/jobs');
  145. if ($config instanceof Mage_Core_Model_Config_Element) {
  146. $this->_generateJobs($config->children(), $exists);
  147. }
  148. /**
  149. * save time schedules generation was ran with no expiration
  150. */
  151. Mage::app()->saveCache(time(), self::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
  152. return $this;
  153. }
  154. /**
  155. * Generate jobs for config information
  156. *
  157. * @param $jobs
  158. * @param array $exists
  159. * @return Mage_Cron_Model_Observer
  160. */
  161. protected function _generateJobs($jobs, $exists)
  162. {
  163. $scheduleAheadFor = Mage::getStoreConfig(self::XML_PATH_SCHEDULE_AHEAD_FOR)*60;
  164. $schedule = Mage::getModel('cron/schedule');
  165. foreach ($jobs as $jobCode => $jobConfig) {
  166. $cronExpr = null;
  167. if ($jobConfig->schedule->config_path) {
  168. $cronExpr = Mage::getStoreConfig((string)$jobConfig->schedule->config_path);
  169. }
  170. if (empty($cronExpr) && $jobConfig->schedule->cron_expr) {
  171. $cronExpr = (string)$jobConfig->schedule->cron_expr;
  172. }
  173. if (!$cronExpr) {
  174. continue;
  175. }
  176. $now = time();
  177. $timeAhead = $now + $scheduleAheadFor;
  178. $schedule->setJobCode($jobCode)
  179. ->setCronExpr($cronExpr)
  180. ->setStatus(Mage_Cron_Model_Schedule::STATUS_PENDING);
  181. for ($time = $now; $time < $timeAhead; $time += 60) {
  182. $ts = strftime('%Y-%m-%d %H:%M:00', $time);
  183. if (!empty($exists[$jobCode.'/'.$ts])) {
  184. // already scheduled
  185. continue;
  186. }
  187. if (!$schedule->trySchedule($time)) {
  188. // time does not match cron expression
  189. continue;
  190. }
  191. $schedule->unsScheduleId()->save();
  192. }
  193. }
  194. return $this;
  195. }
  196. public function cleanup()
  197. {
  198. // check if history cleanup is needed
  199. $lastCleanup = Mage::app()->loadCache(self::CACHE_KEY_LAST_HISTORY_CLEANUP_AT);
  200. if ($lastCleanup > time() - Mage::getStoreConfig(self::XML_PATH_HISTORY_CLEANUP_EVERY)*60) {
  201. return $this;
  202. }
  203. $history = Mage::getModel('cron/schedule')->getCollection()
  204. ->addFieldToFilter('status', array('in'=>array(
  205. Mage_Cron_Model_Schedule::STATUS_SUCCESS,
  206. Mage_Cron_Model_Schedule::STATUS_MISSED,
  207. Mage_Cron_Model_Schedule::STATUS_ERROR,
  208. )))->load();
  209. $historyLifetimes = array(
  210. Mage_Cron_Model_Schedule::STATUS_SUCCESS => Mage::getStoreConfig(self::XML_PATH_HISTORY_SUCCESS)*60,
  211. Mage_Cron_Model_Schedule::STATUS_MISSED => Mage::getStoreConfig(self::XML_PATH_HISTORY_FAILURE)*60,
  212. Mage_Cron_Model_Schedule::STATUS_ERROR => Mage::getStoreConfig(self::XML_PATH_HISTORY_FAILURE)*60,
  213. );
  214. $now = time();
  215. foreach ($history->getIterator() as $record) {
  216. if (strtotime($record->getExecutedAt()) < $now-$historyLifetimes[$record->getStatus()]) {
  217. $record->delete();
  218. }
  219. }
  220. // save time history cleanup was ran with no expiration
  221. Mage::app()->saveCache(time(), self::CACHE_KEY_LAST_HISTORY_CLEANUP_AT, array('crontab'), null);
  222. return $this;
  223. }
  224. }