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) 2012 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: /**
28: * Crontab schedule model
29: *
30: * @method Mage_Cron_Model_Resource_Schedule _getResource()
31: * @method Mage_Cron_Model_Resource_Schedule getResource()
32: * @method string getJobCode()
33: * @method Mage_Cron_Model_Schedule setJobCode(string $value)
34: * @method string getStatus()
35: * @method Mage_Cron_Model_Schedule setStatus(string $value)
36: * @method string getMessages()
37: * @method Mage_Cron_Model_Schedule setMessages(string $value)
38: * @method string getCreatedAt()
39: * @method Mage_Cron_Model_Schedule setCreatedAt(string $value)
40: * @method string getScheduledAt()
41: * @method Mage_Cron_Model_Schedule setScheduledAt(string $value)
42: * @method string getExecutedAt()
43: * @method Mage_Cron_Model_Schedule setExecutedAt(string $value)
44: * @method string getFinishedAt()
45: * @method Mage_Cron_Model_Schedule setFinishedAt(string $value)
46: *
47: * @category Mage
48: * @package Mage_Cron
49: * @author Magento Core Team <core@magentocommerce.com>
50: */
51: class Mage_Cron_Model_Schedule extends Mage_Core_Model_Abstract
52: {
53: const STATUS_PENDING = 'pending';
54: const STATUS_RUNNING = 'running';
55: const STATUS_SUCCESS = 'success';
56: const STATUS_MISSED = 'missed';
57: const STATUS_ERROR = 'error';
58:
59: public function _construct()
60: {
61: $this->_init('cron/schedule');
62: }
63:
64: public function setCronExpr($expr)
65: {
66: $e = preg_split('#\s+#', $expr, null, PREG_SPLIT_NO_EMPTY);
67: if (sizeof($e)<5 || sizeof($e)>6) {
68: throw Mage::exception('Mage_Cron', 'Invalid cron expression: '.$expr);
69: }
70:
71: $this->setCronExprArr($e);
72: return $this;
73: }
74:
75: /**
76: * Checks the observer's cron expression against time
77: *
78: * Supports $this->setCronExpr('* 0-5,10-59/5 2-10,15-25 january-june/2 mon-fri')
79: *
80: * @param Varien_Event $event
81: * @return boolean
82: */
83: public function trySchedule($time)
84: {
85: $e = $this->getCronExprArr();
86: if (!$e || !$time) {
87: return false;
88: }
89: if (!is_numeric($time)) {
90: $time = strtotime($time);
91: }
92:
93: $d = getdate(Mage::getSingleton('core/date')->timestamp($time));
94:
95: $match = $this->matchCronExpression($e[0], $d['minutes'])
96: && $this->matchCronExpression($e[1], $d['hours'])
97: && $this->matchCronExpression($e[2], $d['mday'])
98: && $this->matchCronExpression($e[3], $d['mon'])
99: && $this->matchCronExpression($e[4], $d['wday']);
100:
101: if ($match) {
102: $this->setCreatedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
103: $this->setScheduledAt(strftime('%Y-%m-%d %H:%M', $time));
104: }
105: return $match;
106: }
107:
108: public function matchCronExpression($expr, $num)
109: {
110: // handle ALL match
111: if ($expr==='*') {
112: return true;
113: }
114:
115: // handle multiple options
116: if (strpos($expr,',')!==false) {
117: foreach (explode(',',$expr) as $e) {
118: if ($this->matchCronExpression($e, $num)) {
119: return true;
120: }
121: }
122: return false;
123: }
124:
125: // handle modulus
126: if (strpos($expr,'/')!==false) {
127: $e = explode('/', $expr);
128: if (sizeof($e)!==2) {
129: throw Mage::exception('Mage_Cron', "Invalid cron expression, expecting 'match/modulus': ".$expr);
130: }
131: if (!is_numeric($e[1])) {
132: throw Mage::exception('Mage_Cron', "Invalid cron expression, expecting numeric modulus: ".$expr);
133: }
134: $expr = $e[0];
135: $mod = $e[1];
136: } else {
137: $mod = 1;
138: }
139:
140: // handle all match by modulus
141: if ($expr==='*') {
142: $from = 0;
143: $to = 60;
144: }
145: // handle range
146: elseif (strpos($expr,'-')!==false) {
147: $e = explode('-', $expr);
148: if (sizeof($e)!==2) {
149: throw Mage::exception('Mage_Cron', "Invalid cron expression, expecting 'from-to' structure: ".$expr);
150: }
151:
152: $from = $this->getNumeric($e[0]);
153: $to = $this->getNumeric($e[1]);
154: }
155: // handle regular token
156: else {
157: $from = $this->getNumeric($expr);
158: $to = $from;
159: }
160:
161: if ($from===false || $to===false) {
162: throw Mage::exception('Mage_Cron', "Invalid cron expression: ".$expr);
163: }
164:
165: return ($num>=$from) && ($num<=$to) && ($num%$mod===0);
166: }
167:
168: public function getNumeric($value)
169: {
170: static $data = array(
171: 'jan'=>1,
172: 'feb'=>2,
173: 'mar'=>3,
174: 'apr'=>4,
175: 'may'=>5,
176: 'jun'=>6,
177: 'jul'=>7,
178: 'aug'=>8,
179: 'sep'=>9,
180: 'oct'=>10,
181: 'nov'=>11,
182: 'dec'=>12,
183:
184: 'sun'=>0,
185: 'mon'=>1,
186: 'tue'=>2,
187: 'wed'=>3,
188: 'thu'=>4,
189: 'fri'=>5,
190: 'sat'=>6,
191: );
192:
193: if (is_numeric($value)) {
194: return $value;
195: }
196:
197: if (is_string($value)) {
198: $value = strtolower(substr($value,0,3));
199: if (isset($data[$value])) {
200: return $data[$value];
201: }
202: }
203:
204: return false;
205: }
206:
207: /**
208: * Sets a job to STATUS_RUNNING only if it is currently in STATUS_PENDING.
209: * Returns true if status was changed and false otherwise.
210: *
211: * This is used to implement locking for cron jobs.
212: *
213: * @return boolean
214: */
215: public function tryLockJob()
216: {
217: return $this->_getResource()->trySetJobStatusAtomic($this->getId(), self::STATUS_RUNNING,self::STATUS_PENDING);
218: }
219: }
220: