Calendar_Event_Core   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 0
loc 273
rs 9.6
wmc 32
lcom 1
cbo 1

6 Methods

Rating   Name   Duplication   Size   Complexity  
A condition() 0 20 4
A add_class() 0 6 1
A remove_class() 0 6 1
A output() 0 6 1
F notify() 0 123 22
A day_occurrence() 0 21 3
1
<?php defined('SYSPATH') or die('No direct access allowed.');
2
/**
3
 * Calendar event observer class.
4
 *
5
 * $Id: Calendar_Event.php 4129 2009-03-27 17:47:03Z zombor $
6
 *
7
 * @package    Calendar
8
 * @author     Kohana Team
9
 * @copyright  (c) 2007-2008 Kohana Team
10
 * @license    http://kohanaphp.com/license.html
11
 */
12
class Calendar_Event_Core extends Event_Observer
13
{
14
15
    // Boolean conditions
16
    protected $booleans = array(
17
        'current',
18
        'weekend',
19
        'first_day',
20
        'last_day',
21
        'last_occurrence',
22
        'easter',
23
    );
24
25
    // Rendering conditions
26
    protected $conditions = array();
27
28
    // Cell classes
29
    protected $classes = array();
30
31
    // Cell output
32
    protected $output = '';
33
34
    /**
35
     * Adds a condition to the event. The condition can be one of the following:
36
     *
37
     * timestamp       - UNIX timestamp
38
     * day             - day number (1-31)
39
     * week            - week number (1-5)
40
     * month           - month number (1-12)
41
     * year            - year number (4 digits)
42
     * day_of_week     - day of week (1-7)
43
     * current         - active month (boolean) (only show data for the month being rendered)
44
     * weekend         - weekend day (boolean)
45
     * first_day       - first day of month (boolean)
46
     * last_day        - last day of month (boolean)
47
     * occurrence      - occurrence of the week day (1-5) (use with "day_of_week")
48
     * last_occurrence - last occurrence of week day (boolean) (use with "day_of_week")
49
     * easter          - Easter day (boolean)
50
     * callback        - callback test (boolean)
51
     *
52
     * To unset a condition, call condition with a value of NULL.
53
     *
54
     * @chainable
55
     * @param   string  condition key
56
     * @param   mixed   condition value
57
     * @return  Calendar_Event_Core
58
     */
59
    public function condition($key, $value)
60
    {
61
        if ($value === null) {
62
            unset($this->conditions[$key]);
63
        } else {
64
            if ($key === 'callback') {
65
                // Do nothing
66
            } elseif (in_array($key, $this->booleans)) {
67
                // Make the value boolean
68
                $value = (bool) $value;
69
            } else {
70
                // Make the value an int
71
                $value = (int) $value;
72
            }
73
74
            $this->conditions[$key] = $value;
75
        }
76
77
        return $this;
78
    }
79
80
    /**
81
     * Add a CSS class for this event. This can be called multiple times.
82
     *
83
     * @chainable
84
     * @param   string  CSS class name
85
     * @return  Calendar_Event_Core
86
     */
87
    public function add_class($class)
88
    {
89
        $this->classes[$class] = $class;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Remove a CSS class for this event. This can be called multiple times.
96
     *
97
     * @chainable
98
     * @param   string  CSS class name
99
     * @return  Calendar_Event_Core
100
     */
101
    public function remove_class($class)
102
    {
103
        unset($this->classes[$class]);
104
105
        return $this;
106
    }
107
108
    /**
109
     * Set HTML output for this event.
110
     *
111
     * @chainable
112
     * @param   string  HTML output
113
     * @return  Calendar_Event_Core
114
     */
115
    public function output($str)
116
    {
117
        $this->output = $str;
118
119
        return $this;
120
    }
121
122
    /**
123
     * Add a CSS class for this event. This can be called multiple times.
124
     *
125
     * @chainable
126
     * @param   string  CSS class name
127
     * @return  false|null
128
     */
129
    public function notify($data)
130
    {
131
        // Split the date and current status
132
        list($month, $day, $year, $week, $current) = $data;
133
134
        // Get a timestamp for the day
135
        $timestamp = mktime(0, 0, 0, $month, $day, $year);
136
137
        // Date conditionals
138
        $condition = array(
139
            'timestamp'   => (int) $timestamp,
140
            'day'         => (int) date('j', $timestamp),
141
            'week'        => (int) $week,
142
            'month'       => (int) date('n', $timestamp),
143
            'year'        => (int) date('Y', $timestamp),
144
            'day_of_week' => (int) date('w', $timestamp),
145
            'current'     => (bool) $current,
146
        );
147
148
        // Tested conditions
149
        $tested = array();
150
151
        foreach ($condition as $key => $value) {
152
            // Timestamps need to be handled carefully
153
            if ($key === 'timestamp' and isset($this->conditions['timestamp'])) {
154
                // This adds 23 hours, 59 minutes and 59 seconds to today's timestamp, as 24 hours
155
                // is classed as a new day
156
                $next_day = $timestamp + 86399;
157
                
158
                if ($this->conditions['timestamp'] < $timestamp or $this->conditions['timestamp'] > $next_day) {
159
                    return false;
160
                }
161
            }
162
            // Test basic conditions first
163
            elseif (isset($this->conditions[$key]) and $this->conditions[$key] !== $value) {
164
                return false;
165
            }
166
167
            // Condition has been tested
168
            $tested[$key] = true;
169
        }
170
171
        if (isset($this->conditions['weekend'])) {
172
            // Weekday vs Weekend
173
            $condition['weekend'] = ($condition['day_of_week'] === 0 or $condition['day_of_week'] === 6);
174
        }
175
176
        if (isset($this->conditions['first_day'])) {
177
            // First day of month
178
            $condition['first_day'] = ($condition['day'] === 1);
179
        }
180
181
        if (isset($this->conditions['last_day'])) {
182
            // Last day of month
183
            $condition['last_day'] = ($condition['day'] === (int) date('t', $timestamp));
184
        }
185
186
        if (isset($this->conditions['occurrence'])) {
187
            // Get the occurance of the current day
188
            $condition['occurrence'] = $this->day_occurrence($timestamp);
189
        }
190
191
        if (isset($this->conditions['last_occurrence'])) {
192
            // Test if the next occurance of this date is next month
193
            $condition['last_occurrence'] = ((int) date('n', $timestamp + 604800) !== $condition['month']);
194
        }
195
196
        if (isset($this->conditions['easter'])) {
197
            if ($condition['month'] === 3 or $condition['month'] === 4) {
198
                // This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
199
                // Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
200
                // 1876. This algorithm has also been published in the 1922 book General Astronomy by
201
                // Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
202
                // 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus.
203
204
                $a = $condition['year'] % 19;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
205
                $b = (int) ($condition['year'] / 100);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
206
                $c = $condition['year'] % 100;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $c. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
207
                $d = (int) ($b / 4);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $d. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
208
                $e = $b % 4;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $e. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
209
                $f = (int) (($b + 8) / 25);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $f. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
210
                $g = (int) (($b - $f + 1) / 3);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $g. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
211
                $h = (19 * $a + $b - $d - $g + 15) % 30;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $h. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
212
                $i = (int) ($c / 4);
213
                $k = $c % 4;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $k. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
214
                $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $l. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
215
                $m = (int) (($a + 11 * $h + 22 * $l) / 451);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
216
                $p = ($h + $l - 7 * $m + 114) % 31;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $p. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
217
218
                $month = (int) (($h + $l - 7 * $m + 114) / 31);
219
                $day = $p + 1;
220
221
                $condition['easter'] = ($condition['month'] === $month and $condition['day'] === $day);
222
            } else {
223
                // Easter can only happen in March or April
224
                $condition['easter'] = false;
225
            }
226
        }
227
228
        if (isset($this->conditions['callback'])) {
229
            // Use a callback to determine validity
230
            $condition['callback'] = call_user_func($this->conditions['callback'], $condition, $this);
231
        }
232
233
        $conditions = array_diff_key($this->conditions, $tested);
234
235
        foreach ($conditions as $key => $value) {
236
            if ($key === 'callback') {
237
                // Callbacks are tested on a TRUE/FALSE basis
238
                $value = true;
239
            }
240
241
            // Test advanced conditions
242
            if ($condition[$key] !== $value) {
243
                return false;
244
            }
245
        }
246
247
        $this->caller->add_data(array(
248
            'classes' => $this->classes,
249
            'output'  => $this->output,
250
        ));
251
    }
252
253
    /**
254
     * Find the week day occurrence for a specific timestamp. The occurrence is
255
     * relative to the current month. For example, the second Saturday of any
256
     * given month will return "2" as the occurrence. This is used in combination
257
     * with the "occurrence" condition.
258
     *
259
     * @param   integer  UNIX timestamp
260
     * @param integer $timestamp
261
     * @return  integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
262
     */
263
    protected function day_occurrence($timestamp)
264
    {
265
        // Get the current month for the timestamp
266
        $month = date('m', $timestamp);
267
268
        // Default occurrence is one
269
        $occurrence = 1;
270
271
        // Reduce the timestamp by one week for each loop. This has the added
272
        // benefit of preventing an infinite loop.
273
        while ($timestamp -= 604800) {
274
            if (date('m', $timestamp) !== $month) {
275
                // Once the timestamp has gone into the previous month, the
276
                // proper occurrence has been found.
277
                return $occurrence;
278
            }
279
280
            // Increment the occurrence
281
            $occurrence++;
282
        }
283
    }
284
} // End Calendar Event
285