PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://gitlab.com/axeltizon/magentoV1.9-demopoweraccess
PHP | 360 lines | 216 code | 40 blank | 104 comment | 38 complexity | 5a7fde064a2f20285cddbbeb9efe1884 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) 2014 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. * Generate 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. $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
  56. $defaultJobsRoot = Mage::getConfig()->getNode('default/crontab/jobs');
  57. /** @var $schedule Mage_Cron_Model_Schedule */
  58. foreach ($schedules->getIterator() as $schedule) {
  59. $jobConfig = $jobsRoot->{$schedule->getJobCode()};
  60. if (!$jobConfig || !$jobConfig->run) {
  61. $jobConfig = $defaultJobsRoot->{$schedule->getJobCode()};
  62. if (!$jobConfig || !$jobConfig->run) {
  63. continue;
  64. }
  65. }
  66. $this->_processJob($schedule, $jobConfig);
  67. }
  68. $this->generate();
  69. $this->cleanup();
  70. }
  71. /**
  72. * Process cron queue for tasks marked as always
  73. *
  74. * @param Varien_Event_Observer $observer
  75. */
  76. public function dispatchAlways($observer)
  77. {
  78. $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
  79. if ($jobsRoot instanceof Varien_Simplexml_Element) {
  80. foreach ($jobsRoot->children() as $jobCode => $jobConfig) {
  81. $this->_processAlwaysTask($jobCode, $jobConfig);
  82. }
  83. }
  84. $defaultJobsRoot = Mage::getConfig()->getNode('default/crontab/jobs');
  85. if ($defaultJobsRoot instanceof Varien_Simplexml_Element) {
  86. foreach ($defaultJobsRoot->children() as $jobCode => $jobConfig) {
  87. $this->_processAlwaysTask($jobCode, $jobConfig);
  88. }
  89. }
  90. }
  91. public function getPendingSchedules()
  92. {
  93. if (!$this->_pendingSchedules) {
  94. $this->_pendingSchedules = Mage::getModel('cron/schedule')->getCollection()
  95. ->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_PENDING)
  96. ->load();
  97. }
  98. return $this->_pendingSchedules;
  99. }
  100. /**
  101. * Generate cron schedule
  102. *
  103. * @return Mage_Cron_Model_Observer
  104. */
  105. public function generate()
  106. {
  107. /**
  108. * check if schedule generation is needed
  109. */
  110. $lastRun = Mage::app()->loadCache(self::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT);
  111. if ($lastRun > time() - Mage::getStoreConfig(self::XML_PATH_SCHEDULE_GENERATE_EVERY)*60) {
  112. return $this;
  113. }
  114. $schedules = $this->getPendingSchedules();
  115. $exists = array();
  116. foreach ($schedules->getIterator() as $schedule) {
  117. $exists[$schedule->getJobCode().'/'.$schedule->getScheduledAt()] = 1;
  118. }
  119. /**
  120. * generate global crontab jobs
  121. */
  122. $config = Mage::getConfig()->getNode('crontab/jobs');
  123. if ($config instanceof Mage_Core_Model_Config_Element) {
  124. $this->_generateJobs($config->children(), $exists);
  125. }
  126. /**
  127. * generate configurable crontab jobs
  128. */
  129. $config = Mage::getConfig()->getNode('default/crontab/jobs');
  130. if ($config instanceof Mage_Core_Model_Config_Element) {
  131. $this->_generateJobs($config->children(), $exists);
  132. }
  133. /**
  134. * save time schedules generation was ran with no expiration
  135. */
  136. Mage::app()->saveCache(time(), self::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
  137. return $this;
  138. }
  139. /**
  140. * Generate jobs for config information
  141. *
  142. * @param $jobs
  143. * @param array $exists
  144. * @return Mage_Cron_Model_Observer
  145. */
  146. protected function _generateJobs($jobs, $exists)
  147. {
  148. $scheduleAheadFor = Mage::getStoreConfig(self::XML_PATH_SCHEDULE_AHEAD_FOR)*60;
  149. $schedule = Mage::getModel('cron/schedule');
  150. foreach ($jobs as $jobCode => $jobConfig) {
  151. $cronExpr = null;
  152. if ($jobConfig->schedule->config_path) {
  153. $cronExpr = Mage::getStoreConfig((string)$jobConfig->schedule->config_path);
  154. }
  155. if (empty($cronExpr) && $jobConfig->schedule->cron_expr) {
  156. $cronExpr = (string)$jobConfig->schedule->cron_expr;
  157. }
  158. if (!$cronExpr || $cronExpr == 'always') {
  159. continue;
  160. }
  161. $now = time();
  162. $timeAhead = $now + $scheduleAheadFor;
  163. $schedule->setJobCode($jobCode)
  164. ->setCronExpr($cronExpr)
  165. ->setStatus(Mage_Cron_Model_Schedule::STATUS_PENDING);
  166. for ($time = $now; $time < $timeAhead; $time += 60) {
  167. $ts = strftime('%Y-%m-%d %H:%M:00', $time);
  168. if (!empty($exists[$jobCode.'/'.$ts])) {
  169. // already scheduled
  170. continue;
  171. }
  172. if (!$schedule->trySchedule($time)) {
  173. // time does not match cron expression
  174. continue;
  175. }
  176. $schedule->unsScheduleId()->save();
  177. }
  178. }
  179. return $this;
  180. }
  181. /**
  182. * Clean up the history of tasks
  183. *
  184. * @return Mage_Cron_Model_Observer
  185. */
  186. public function cleanup()
  187. {
  188. // check if history cleanup is needed
  189. $lastCleanup = Mage::app()->loadCache(self::CACHE_KEY_LAST_HISTORY_CLEANUP_AT);
  190. if ($lastCleanup > time() - Mage::getStoreConfig(self::XML_PATH_HISTORY_CLEANUP_EVERY)*60) {
  191. return $this;
  192. }
  193. $history = Mage::getModel('cron/schedule')->getCollection()
  194. ->addFieldToFilter('status', array('in'=>array(
  195. Mage_Cron_Model_Schedule::STATUS_SUCCESS,
  196. Mage_Cron_Model_Schedule::STATUS_MISSED,
  197. Mage_Cron_Model_Schedule::STATUS_ERROR,
  198. )))
  199. ->load();
  200. $historyLifetimes = array(
  201. Mage_Cron_Model_Schedule::STATUS_SUCCESS => Mage::getStoreConfig(self::XML_PATH_HISTORY_SUCCESS)*60,
  202. Mage_Cron_Model_Schedule::STATUS_MISSED => Mage::getStoreConfig(self::XML_PATH_HISTORY_FAILURE)*60,
  203. Mage_Cron_Model_Schedule::STATUS_ERROR => Mage::getStoreConfig(self::XML_PATH_HISTORY_FAILURE)*60,
  204. );
  205. $now = time();
  206. foreach ($history->getIterator() as $record) {
  207. if (strtotime($record->getExecutedAt()) < $now-$historyLifetimes[$record->getStatus()]) {
  208. $record->delete();
  209. }
  210. }
  211. // save time history cleanup was ran with no expiration
  212. Mage::app()->saveCache(time(), self::CACHE_KEY_LAST_HISTORY_CLEANUP_AT, array('crontab'), null);
  213. return $this;
  214. }
  215. /**
  216. * Processing cron task which is marked as always
  217. *
  218. * @param $jobCode
  219. * @param $jobConfig
  220. * @return Mage_Cron_Model_Observer
  221. */
  222. protected function _processAlwaysTask($jobCode, $jobConfig)
  223. {
  224. if (!$jobConfig || !$jobConfig->run) {
  225. return;
  226. }
  227. $cronExpr = isset($jobConfig->schedule->cron_expr)? (string) $jobConfig->schedule->cron_expr : '';
  228. if ($cronExpr != 'always') {
  229. return;
  230. }
  231. $schedule = $this->_getAlwaysJobSchedule($jobCode);
  232. if ($schedule !== false) {
  233. $this->_processJob($schedule, $jobConfig, true);
  234. }
  235. return $this;
  236. }
  237. /**
  238. * Process cron task
  239. *
  240. * @param Mage_Cron_Model_Schedule $schedule
  241. * @param $jobConfig
  242. * @param bool $isAlways
  243. * @return Mage_Cron_Model_Observer
  244. */
  245. protected function _processJob($schedule, $jobConfig, $isAlways = false)
  246. {
  247. $runConfig = $jobConfig->run;
  248. if (!$isAlways) {
  249. $scheduleLifetime = Mage::getStoreConfig(self::XML_PATH_SCHEDULE_LIFETIME) * 60;
  250. $now = time();
  251. $time = strtotime($schedule->getScheduledAt());
  252. if ($time > $now) {
  253. return;
  254. }
  255. }
  256. $errorStatus = Mage_Cron_Model_Schedule::STATUS_ERROR;
  257. try {
  258. if (!$isAlways) {
  259. if ($time < $now - $scheduleLifetime) {
  260. $errorStatus = Mage_Cron_Model_Schedule::STATUS_MISSED;
  261. Mage::throwException(Mage::helper('cron')->__('Too late for the schedule.'));
  262. }
  263. }
  264. if ($runConfig->model) {
  265. if (!preg_match(self::REGEX_RUN_MODEL, (string)$runConfig->model, $run)) {
  266. Mage::throwException(Mage::helper('cron')->__('Invalid model/method definition, expecting "model/class::method".'));
  267. }
  268. if (!($model = Mage::getModel($run[1])) || !method_exists($model, $run[2])) {
  269. Mage::throwException(Mage::helper('cron')->__('Invalid callback: %s::%s does not exist', $run[1], $run[2]));
  270. }
  271. $callback = array($model, $run[2]);
  272. $arguments = array($schedule);
  273. }
  274. if (empty($callback)) {
  275. Mage::throwException(Mage::helper('cron')->__('No callbacks found'));
  276. }
  277. if (!$isAlways) {
  278. if (!$schedule->tryLockJob()) {
  279. // another cron started this job intermittently, so skip it
  280. return;
  281. }
  282. /**
  283. though running status is set in tryLockJob we must set it here because the object
  284. was loaded with a pending status and will set it back to pending if we don't set it here
  285. */
  286. }
  287. $schedule
  288. ->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
  289. ->save();
  290. call_user_func_array($callback, $arguments);
  291. $schedule
  292. ->setStatus(Mage_Cron_Model_Schedule::STATUS_SUCCESS)
  293. ->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
  294. } catch (Exception $e) {
  295. $schedule->setStatus($errorStatus)
  296. ->setMessages($e->__toString());
  297. }
  298. $schedule->save();
  299. return $this;
  300. }
  301. /**
  302. * Get job for task marked as always
  303. *
  304. * @param $jobCode
  305. * @return bool|Mage_Cron_Model_Schedule
  306. */
  307. protected function _getAlwaysJobSchedule($jobCode)
  308. {
  309. /** @var $schedule Mage_Cron_Model_Schedule */
  310. $schedule = Mage::getModel('cron/schedule')->load($jobCode, 'job_code');
  311. if ($schedule->getId() === null) {
  312. $ts = strftime('%Y-%m-%d %H:%M:00', time());
  313. $schedule->setJobCode($jobCode)
  314. ->setCreatedAt($ts)
  315. ->setScheduledAt($ts);
  316. }
  317. $schedule->setStatus(Mage_Cron_Model_Schedule::STATUS_RUNNING)->save();
  318. return $schedule;
  319. }
  320. }