1: <?php
2: 3: 4: 5: 6:
7: class Horde_Scheduler_Cron_Date
8: {
9: public $legalDays = array('MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN');
10:
11: public $sec;
12: public $min;
13: public $hour;
14: public $day;
15: public $month;
16:
17: public function __construct($raw)
18: {
19: $this->parse(Horde_String::upper($raw));
20: }
21:
22: public function nowMatches()
23: {
24: return $this->scheduledAt(time());
25: }
26:
27: public function scheduledAt($ts = null)
28: {
29: if ($ts === null) {
30: $ts = time();
31: }
32: return ($this->monthMatches($ts) &&
33: $this->monthMatches($ts) &&
34: $this->dayMatches($ts) &&
35: $this->hourMatches($ts) &&
36: $this->minMatches($ts) &&
37: $this->secMatches($ts));
38: }
39:
40: public function monthMatches($ts)
41: {
42: if ($this->month == '*') {
43: return true;
44: }
45:
46: $currentmonth = '-' . date('n', $ts) . '-';
47:
48: return (bool)strpos($this->month, $currentmonth);
49: }
50:
51: public function dayMatches($ts)
52: {
53: if (!empty($this->day['value']) && $this->day['value'] == '*') {
54: return true;
55: }
56:
57: $currentdaynum = '-' . date('j', $ts) . '-';
58: $currentdaytxt = Horde_String::upper(date('D'));
59:
60: foreach ($this->day as $day) {
61: if (@strpos($day['not'], $currentdaytxt) === false) {
62: $v1 = (@strpos($day['value'], $currentdaynum) !== false);
63: $v2 = (@strpos($day['and'], $currentdaytxt) !== false);
64:
65: if (!empty($day['and']) && ($v1 && $v2)) {
66: return true;
67: } elseif (empty($day['and']) && $v1) {
68: return true;
69: }
70: }
71: }
72:
73: return false;
74: }
75:
76: public function hourMatches($ts)
77: {
78: if ($this->hour == '*') {
79: return true;
80: }
81:
82: $currenthour = '-' . date('G', $ts) . '-';
83:
84: return (strpos($this->hour, $currenthour) !== false);
85: }
86:
87: public function minMatches($ts)
88: {
89: if ($this->min == '*') {
90: return true;
91: }
92:
93: $currentmin = '-' . intval(date('i', $ts)) . '-';
94:
95: return (strpos($this->min, $currentmin) !== false);
96: }
97:
98: public function secMatches($ts)
99: {
100: if ($this->sec == '*') {
101: return true;
102: }
103:
104: $currentsec = '-' . intval(date('s', $ts)) . '-';
105:
106: return (strpos($this->sec, $currentsec) !== false);
107: }
108:
109: public function parse($str)
110: {
111: $s = array();
112:
113: list($s['sec'], $s['min'], $s['hour'], $s['day'], $s['month']) = preg_split('|[\n\t ]+|', $str);
114:
115: foreach ($s as $k => $v) {
116: if (strpos($v, '*') !== false) {
117: $s[$k] = array('*');
118: } elseif (!$this->generallyDecentSyntax($v)) {
119: die("Illegal syntax in '$v'\n");
120: } else {
121: $s[$k] = explode(',', $s[$k]);
122: }
123: }
124:
125: if ($s['sec'][0] == '*') {
126: $this->sec = '*';
127: } else {
128: for ($i = 0; $i < sizeof($s['sec']); $i++) {
129: if ($this->isRange($s['sec'][$i])) {
130: $s['sec'][$i] = $this->expandRange($this->rangeVals($s['sec'][$i]));
131: }
132: }
133: $this->sec = '-' . join('-', $s['sec']) . '-';
134: }
135:
136: if ($s['min'][0] == '*') {
137: $this->min = '*';
138: } else {
139: for ($i = 0; $i < sizeof($s['min']); $i++) {
140: if ($this->isRange($s['min'][$i])) {
141: $s['min'][$i] = $this->expandRange($this->rangeVals($s['min'][$i]));
142: }
143: }
144: $this->min = '-' . join('-', $s['min']) . '-';
145: }
146:
147: if ($s['hour'][0] == '*') {
148: $this->hour = '*';
149: } else {
150: for ($i = 0; $i < sizeof($s['hour']); $i++) {
151: if ($this->isRange($s['hour'][$i])) {
152: $s['hour'][$i] = $this->expandRange($this->rangeVals($s['hour'][$i]));
153: }
154: }
155: $this->hour = '-' . join('-', $s['hour']) . '-';
156: }
157:
158: if ($s['day'][0] == '*') {
159: $this->day = '*';
160: } else {
161: for ($i = 0; $i < sizeof($s['day']); $i++) {
162: $tmp = array();
163: if (($char = $this->isCond($s['day'][$i])) !== false) {
164: if ($char == '&') {
165: list($tmp['value'], $tmp['and']) = explode($char, $s['day'][$i]);
166: if ($this->isRange($tmp['and'])) {
167: $tmp['and'] = $this->expandRange($this->rangeVals($tmp['and']));
168: }
169: } else {
170: list($tmp['value'], $tmp['not']) = explode($char, $s['day'][$i]);
171: if ($this->isRange($tmp['not'])) {
172: $tmp['not'] = $this->expandRange($this->rangeVals($tmp['not']));
173: }
174: }
175: } else {
176: $tmp = array('value' => $s['day'][$i]);
177: }
178:
179: $s['day'][$i] = $tmp;
180:
181: if ($this->isRange($s['day'][$i]['value'])) {
182: $s['day'][$i]['value'] = $this->expandRange($this->rangeVals($s['day'][$i]['value']));
183: }
184: }
185:
186: $this->day = $s['day'];
187: }
188:
189: if ($s['month'][0] == '*') {
190: $this->month = '*';
191: } else {
192: for ($i = 0; $i < sizeof($s['month']); $i++) {
193: if ($this->isRange($s['month'][$i])) {
194: $s['month'][$i] = $this->expandRange($this->rangeVals($s['month'][$i]));
195: }
196: }
197: $this->month = '-' . join('-', $s['month']) . '-';
198: }
199: }
200:
201: public function isCond($s)
202: {
203: if (strpos($s, '&') !== false) {
204: return '&';
205: } elseif (strpos($s, '!') !== false) {
206: return '!';
207: } else {
208: return false;
209: }
210: }
211:
212: public function isRange($s)
213: {
214: return preg_match('/^\w+\-\w+/', $s);
215: }
216:
217: public function isCondRange($s)
218: {
219: return ($this->isCond($s) && $this->isRange($s));
220: }
221:
222: public function isCondVal($s)
223: {
224: return ($this->isCond($s) && !$this->isRange($s));
225: }
226:
227: public function rangeVals($s)
228: {
229: return explode('-', $s);
230: }
231:
232: public function expandRange($l, $h = '')
233: {
234:
235: if (is_array($l)) {
236: $h = $l[1];
237: $l = $l[0];
238: }
239:
240: if ($this->isDigit($l)) {
241: if (!$this->isDigit($h)) {
242: die("Invalid value '$h' in range '$l-$h'");
243: }
244:
245:
246:
247: if ($l < 0) {
248: $l = 0;
249: } elseif ($l > 59) {
250: $l = 59;
251: }
252:
253: if ($h < 0) {
254: $h = 0;
255: } elseif ($h > 59) {
256: $h = 59;
257: }
258:
259: if ($l > $h) {
260: $tmp = $l;
261: $l = $h;
262: $h = $tmp;
263: unset($tmp);
264: }
265:
266:
267:
268: return '-' . join('-', range(intval($l), intval($h))) . '-';
269: } else {
270:
271: die("Invalid value '$l' in range '$l-$h'");
272: }
273: }
274:
275: public function dayValue($s)
276: {
277: for ($i = 0; $i < count($this->legalDays); $i++) {
278: if ($this->legalDays[$i] == $s) {
279: return $i;
280: }
281: }
282:
283: return -1;
284: }
285:
286: public function isDigit($s)
287: {
288: return preg_match('/^\d+$/', $s);
289: }
290:
291: public function isAlpha($s)
292: {
293: return $this->isLegalDay($s);
294: }
295:
296: public function isLegalDay($s)
297: {
298: return in_array($s, $this->legalDays);
299: }
300:
301: public function generallyDecentSyntax($s)
302: {
303: return ($s == '*' ||
304: preg_match('/^\d+(-\d+)?([!&][A-Z\*]+(-[A-Z\*]+)?)?(,\d+(-\d+)?([!&][A-Z\*]+(-[A-Z\*]+)?)?)*$/', $s));
305: }
306:
307: }
308: