Completed
Push — master-stable ( 656ca5...f5da56 )
by
unknown
201:36 queued 192:35
created

iCalendarReader   D

Complexity

Total Complexity 183

Size/Duplication

Total Lines 881
Duplicated Lines 14.3 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 126
loc 881
rs 4.4444
c 0
b 0
f 0
wmc 183
lcom 1
cbo 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
C get_events() 0 58 10
B apply_timezone_offset() 0 44 6
F filter_past_and_recurring_events() 96 339 75
D parse() 0 101 34
A key_value_from_string() 0 8 2
A timezone_from_string() 0 14 3
F add_component() 14 89 21
A escape() 0 22 2
C render() 6 62 12
C formatted_date() 10 55 13
A sort_by_recent() 0 17 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like iCalendarReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use iCalendarReader, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Gets and renders iCal feeds for the Upcoming Events widget and shortcode
5
 */
6
7
class iCalendarReader {
8
9
	public $todo_count = 0;
10
	public $event_count = 0;
11
	public $cal = array();
12
	public $_lastKeyWord = '';
13
	public $timezone = null;
14
15
	/**
16
	 * Class constructor
17
	 *
18
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
19
	 */
20
	public function __construct() {}
21
22
	/**
23
	 * Return an array of events
24
	 *
25
	 * @param string $url (default: '')
26
	 * @return array | false on failure
27
	 */
28
	public function get_events( $url = '', $count = 5 ) {
29
		$count = (int) $count;
30
		$transient_id = 'icalendar_vcal_' . md5( $url ) . '_' . $count;
31
32
		$vcal = get_transient( $transient_id );
33
34
		if ( ! empty( $vcal ) ) {
35
			if ( isset( $vcal['TIMEZONE'] ) )
36
				$this->timezone = $this->timezone_from_string( $vcal['TIMEZONE'] );
37
38
			if ( isset( $vcal['VEVENT'] ) ) {
39
				$vevent = $vcal['VEVENT'];
40
41
				if ( $count > 0 )
42
					$vevent = array_slice( $vevent, 0, $count );
43
44
				$this->cal['VEVENT'] = $vevent;
45
46
				return $this->cal['VEVENT'];
47
			}
48
		}
49
50
		if ( ! $this->parse( $url ) )
51
			return false;
52
53
		$vcal = array();
54
55
		if ( $this->timezone ) {
56
			$vcal['TIMEZONE'] = $this->timezone->getName();
57
		} else {
58
			$this->timezone = $this->timezone_from_string( '' );
59
		}
60
61
		if ( ! empty( $this->cal['VEVENT'] ) ) {
62
			$vevent = $this->cal['VEVENT'];
63
64
			// check for recurring events
65
			// $vevent = $this->add_recurring_events( $vevent );
66
67
			// remove before caching - no sense in hanging onto the past
68
			$vevent = $this->filter_past_and_recurring_events( $vevent );
69
70
			// order by soonest start date
71
			$vevent = $this->sort_by_recent( $vevent );
72
73
			$vcal['VEVENT'] = $vevent;
74
		}
75
76
		set_transient( $transient_id, $vcal, HOUR_IN_SECONDS );
77
78
		if ( !isset( $vcal['VEVENT'] ) )
79
			return false;
80
81
		if ( $count > 0 )
82
			return array_slice( $vcal['VEVENT'], 0, $count );
83
84
		return $vcal['VEVENT'];
85
	}
86
87
	function apply_timezone_offset( $events ) {
88
		if ( ! $events ) {
89
			return $events;
90
		}
91
92
		// get timezone offset from the timezone name.
93
		$timezone_name = get_option( 'timezone_string' );
94
		if ( $timezone_name ) {
95
			$timezone = new DateTimeZone( $timezone_name );
96
		} else {
97
			// If the timezone isn't set then the GMT offset must be set.
98
			// generate a DateInterval object from the timezone offset
99
			$gmt_offset = get_option( 'gmt_offset' ) * HOUR_IN_MINUTES;
100
			$timezone_offset_interval = date_interval_create_from_date_string( "{$gmt_offset} minutes" );
101
			$timezone = new DateTimeZone( 'UTC' );
102
		}
103
104
		$offsetted_events = array();
105
106
		foreach ( $events as $event ) {
107
			// Don't handle all-day events
108
			if ( 8 < strlen( $event['DTSTART'] ) ) {
109
				$start_time = preg_replace( '/Z$/', '', $event['DTSTART'] );
110
				$start_time = new DateTime( $start_time, $this->timezone );
111
				$start_time->setTimeZone( $timezone );
112
113
				$end_time = preg_replace( '/Z$/', '', $event['DTEND'] );
114
				$end_time = new DateTime( $end_time, $this->timezone );
115
				$end_time->setTimeZone( $timezone );
116
				
117
				if ( $timezone_offset_interval ) {
118
					$start_time->add( $timezone_offset_interval );
0 ignored issues
show
Bug introduced by
The variable $timezone_offset_interval does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
119
					$end_time->add( $timezone_offset_interval );
120
				}
121
122
				$event['DTSTART'] = $start_time->format( 'YmdHis\Z' );
123
				$event['DTEND'] = $end_time->format( 'YmdHis\Z' );
124
			}
125
126
			$offsetted_events[] = $event;
127
		}
128
129
		return $offsetted_events;
130
	}
131
132
	protected function filter_past_and_recurring_events( $events ) {
133
		$upcoming = array();
134
		$set_recurring_events = array();
135
		$recurrences = array();
0 ignored issues
show
Unused Code introduced by
$recurrences is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
136
		/**
137
		 * This filter allows any time to be passed in for testing or changing timezones, etc...
138
		 *
139
		 * @module widgets
140
		 * 
141
		 * @since 3.4.0
142
		 *
143
		 * @param object time() A time object.
144
		 */
145
		$current = apply_filters( 'ical_get_current_time', time() );
146
147
		foreach ( $events as $event ) {
148
149
			$date_from_ics = strtotime( $event['DTSTART'] );
150 View Code Duplication
			if ( isset( $event['DTEND'] ) ) {
151
				$duration = strtotime( $event['DTEND'] ) - strtotime( $event['DTSTART'] );
152
			} else {
153
				$duration = 0;
154
			}
155
156
			if ( isset( $event['RRULE'] ) && $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
157
				try {
158
					$adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone('UTC') );
159
					$adjusted_time->setTimeZone( new DateTimeZone( $this->timezone->getName() ) );
160
					$event['DTSTART'] = $adjusted_time->format('Ymd\THis');
161
					$date_from_ics = strtotime( $event['DTSTART'] );
162
163
					$event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
164
				} catch ( Exception $e ) {
165
					// Invalid argument to DateTime
166
				}
167
168
				if ( isset( $event['EXDATE'] ) ) {
169
					$exdates = array();
170
					foreach ( (array) $event['EXDATE'] as $exdate ) {
171
						try {
172
							$adjusted_time = new DateTime( $exdate, new DateTimeZone('UTC') );
173
							$adjusted_time->setTimeZone( new DateTimeZone( $this->timezone->getName() ) );
174
							if ( 8 == strlen( $event['DTSTART'] ) ) {
175
								$exdates[] = $adjusted_time->format( 'Ymd' );
176
							} else {
177
								$exdates[] = $adjusted_time->format( 'Ymd\THis' );
178
							}
179
						} catch ( Exception $e ) {
180
							// Invalid argument to DateTime
181
						}
182
					}
183
					$event['EXDATE'] = $exdates;
184
				} else {
185
					$event['EXDATE'] = array();
186
				}
187
			}
188
189
			if ( ! isset( $event['DTSTART'] ) ) {
190
				continue;
191
			}
192
193
			// Process events with RRULE before other events
194
			$rrule = isset( $event['RRULE'] ) ? $event['RRULE'] : false ;
195
			$uid = $event['UID'];
196
197
			if ( $rrule && ! in_array( $uid, $set_recurring_events ) ) {
198
199
				// Break down the RRULE into digestible chunks
200
				$rrule_array = array();
201
202
				foreach ( explode( ";", $event['RRULE'] ) as $rline ) {
203
					list( $rkey, $rvalue ) = explode( "=", $rline, 2 );
204
					$rrule_array[$rkey] = $rvalue;
205
				}
206
207
				$interval = ( isset( $rrule_array['INTERVAL'] ) ) ? $rrule_array['INTERVAL'] : 1;
208
				$rrule_count = ( isset( $rrule_array['COUNT'] ) ) ? $rrule_array['COUNT'] : 0;
209
				$until = ( isset( $rrule_array['UNTIL'] ) ) ? strtotime( $rrule_array['UNTIL'] ) : strtotime( '+1 year', $current );
210
211
				// Used to bound event checks
212
				$echo_limit = 10;
213
				$noop = false;
214
215
				// Set bydays for the event
216
				$weekdays = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
217
				$bydays = $weekdays;
218
219
				// Calculate a recent start date for incrementing depending on the frequency and interval
220
				switch ( $rrule_array['FREQ'] ) {
221
222
					case 'DAILY':
223
						$frequency = 'day';
224
						$echo_limit = 10;
225
226
						if ( $date_from_ics >= $current ) {
227
							$recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
228
						} else {
229
							// Interval and count
230
							$catchup = floor( ( $current - strtotime( $event['DTSTART'] ) ) / ( $interval * DAY_IN_SECONDS ) );
231
							if ( $rrule_count && $catchup > 0 ) {
232
								if ( $catchup < $rrule_count ) {
233
									$rrule_count = $rrule_count - $catchup;
234
									$recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' days', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
235
								} else {
236
									$noop = true;
237
								}
238 View Code Duplication
							} else {
239
								$recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' days', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
240
							}
241
						}
242
						break;
243
244
					case 'WEEKLY':
245
						$frequency = 'week';
246
						$echo_limit = 4;
247
248
						// BYDAY exception to current date
249
						$day = false;
250
						if ( ! isset( $rrule_array['BYDAY'] ) ) {
251
							$day = $rrule_array['BYDAY'] = strtoupper( substr( date( 'D', strtotime( $event['DTSTART'] ) ), 0, 2 ) );
252
						}
253
						$bydays = explode( ',', $rrule_array['BYDAY'] );
254
255
						if ( $date_from_ics >= $current ) {
256
							$recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
257
						} else {
258
							// Interval and count
259
							$catchup = floor( ( $current - strtotime( $event['DTSTART'] ) ) / ( $interval * WEEK_IN_SECONDS ) );
260
							if ( $rrule_count && $catchup > 0 ) {
261
								if ( ( $catchup * count( $bydays ) ) < $rrule_count ) {
262
									$rrule_count = $rrule_count - ( $catchup * count( $bydays ) ); // Estimate current event count
263
									$recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' weeks', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
264
								} else {
265
									$noop = true;
266
								}
267 View Code Duplication
							} else {
268
								$recurring_event_date_start = date( 'Ymd', strtotime( '+ ' . ( $interval * $catchup ) . ' weeks', strtotime( $event['DTSTART'] ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
269
							}
270
						}
271
272
						// Set to Sunday start
273
						if ( ! $noop && 'SU' !== strtoupper( substr( date( 'D', strtotime( $recurring_event_date_start ) ), 0, 2 ) ) ) {
274
							$recurring_event_date_start = date( 'Ymd', strtotime( "last Sunday", strtotime( $recurring_event_date_start ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
0 ignored issues
show
Bug introduced by
The variable $recurring_event_date_start does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
275
						}
276
						break;
277
278
					case 'MONTHLY':
279
						$frequency = 'month';
280
						$echo_limit = 1;
281
282
						if ( $date_from_ics >= $current ) {
283
							$recurring_event_date_start = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) );
284
						} else {
285
							// Describe the date in the month
286
							if ( isset( $rrule_array['BYDAY'] ) ) {
287
								$day_number = substr( $rrule_array['BYDAY'], 0, 1 );
288
								$week_day = substr( $rrule_array['BYDAY'], 1 );
289
								$day_cardinals = array( 1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth' );
290
								$weekdays = array( 'SU' => 'Sunday', 'MO' => 'Monday', 'TU' => 'Tuesday', 'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday', 'SA' => 'Saturday' );
291
								$event_date_desc = "{$day_cardinals[$day_number]} {$weekdays[$week_day]} of ";
292
							} else {
293
								$event_date_desc = date( 'd ', strtotime( $event['DTSTART'] ) );
294
							}
295
296
							// Interval only
297
							if ( $interval > 1 ) {
298
								$catchup = 0;
299
								$maybe = strtotime( $event['DTSTART'] );
300
								while ( $maybe < $current ) {
301
									$maybe = strtotime( '+ ' . ( $interval * $catchup ) . ' months', strtotime( $event['DTSTART'] ) );
302
									$catchup++;
303
								}
304
								$recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', strtotime( '+ ' . ( $interval * ( $catchup - 1 ) ) . ' months', strtotime( $event['DTSTART'] ) ) ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
305 View Code Duplication
							} else {
306
								$recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', $current ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
307
							}
308
309
							// Add one interval if necessary
310
							if ( strtotime( $recurring_event_date_start ) < $current ) {
311
								if ( $interval > 1 ) {
312
									$recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . date( 'F Y', strtotime( '+ ' . ( $interval * $catchup ) . ' months', strtotime( $event['DTSTART'] ) ) ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
0 ignored issues
show
Bug introduced by
The variable $catchup does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
313 View Code Duplication
								} else {
314
									try {
315
										$adjustment = new DateTime( date( 'Y-m-d', $current ) );
316
										$adjustment->modify( 'first day of next month' );
317
										$recurring_event_date_start = date( 'Ymd', strtotime( $event_date_desc . $adjustment->format( 'F Y' ) ) ) . date( '\THis', strtotime( $event['DTSTART'] ) );
318
									} catch ( Exception $e ) {
319
										// Invalid argument to DateTime
320
									}
321
								}
322
							}
323
						}
324
						break;
325
326
					case 'YEARLY':
327
						$frequency = 'year';
328
						$echo_limit = 1;
329
330
						if ( $date_from_ics >= $current ) {
331
							$recurring_event_date_start = date( "Ymd\THis", strtotime( $event['DTSTART'] ) );
332
						} else {
333
							$recurring_event_date_start = date( 'Y', $current ) . date( "md\THis", strtotime( $event['DTSTART'] ) );
334 View Code Duplication
							if ( strtotime( $recurring_event_date_start ) < $current ) {
335
								try {
336
									$next = new DateTime( date( 'Y-m-d', $current ) );
337
									$next->modify( 'first day of next year' );
338
									$recurring_event_date_start = $next->format( 'Y' ) . date ( 'md\THis', strtotime( $event['DTSTART'] ) );
339
								} catch ( Exception $e ) {
340
									// Invalid argument to DateTime
341
								}
342
							}
343
						}
344
						break;
345
346
					default:
347
						$frequency = false;
348
				}
349
350
				if ( $frequency !== false && ! $noop ) {
351
					$count_counter = 1;
352
353
					// If no COUNT limit, go to 10
354
					if ( empty( $rrule_count ) ) {
355
						$rrule_count = 10;
356
					}
357
358
					// Set up EXDATE handling for the event
359
					$exdates = ( isset( $event['EXDATE'] ) ) ? $event['EXDATE'] : array();
360
361
					for ( $i = 1; $i <= $echo_limit; $i++ ) {
362
363
						// Weeks need a daily loop and must check for inclusion in BYDAYS
364
						if ( 'week' == $frequency ) {
365
							$byday_event_date_start = strtotime( $recurring_event_date_start );
366
367
							foreach ( $weekdays as $day ) {
368
369
								$event_start_timestamp = $byday_event_date_start;
370
								$start_time = date( 'His', $event_start_timestamp );
371
								$event_end_timestamp = $event_start_timestamp + $duration;
372
								$end_time = date( 'His', $event_end_timestamp );
0 ignored issues
show
Unused Code introduced by
$end_time is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
373 View Code Duplication
								if ( 8 == strlen( $event['DTSTART'] ) ) {
374
									$exdate_compare = date( 'Ymd', $event_start_timestamp );
375
								} else {
376
									$exdate_compare = date( 'Ymd\THis', $event_start_timestamp );
377
								}
378
379 View Code Duplication
								if ( in_array( $day, $bydays ) && $event_end_timestamp > $current && $event_start_timestamp < $until && $count_counter <= $rrule_count && $event_start_timestamp >= $date_from_ics && ! in_array( $exdate_compare, $exdates ) ) {
380
									if ( 8 == strlen( $event['DTSTART'] ) ) {
381
										$event['DTSTART'] = date( 'Ymd', $event_start_timestamp );
382
										$event['DTEND'] = date( 'Ymd', $event_end_timestamp );
383
									} else {
384
										$event['DTSTART'] = date( 'Ymd\THis', $event_start_timestamp );
385
										$event['DTEND'] = date( 'Ymd\THis', $event_end_timestamp );
386
									}
387
									if ( $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
388
										try {
389
											$adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone( $this->timezone->getName() ) );
390
											$adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
391
											$event['DTSTART'] = $adjusted_time->format('Ymd\THis');
392
393
											$event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
394
										} catch ( Exception $e ) {
395
											// Invalid argument to DateTime
396
										}
397
									}
398
									$upcoming[] = $event;
399
									$count_counter++;
400
								}
401
402
								// Move forward one day
403
								$byday_event_date_start = strtotime( date( 'Ymd\T', strtotime( '+ 1 day', $event_start_timestamp ) ) . $start_time );
404
							}
405
406
							// Restore first event timestamp
407
							$event_start_timestamp = strtotime( $recurring_event_date_start );
408
409
						} else {
410
411
							$event_start_timestamp = strtotime( $recurring_event_date_start );
412
							$start_time = date( 'His', $event_start_timestamp );
413
							$event_end_timestamp = $event_start_timestamp + $duration;
414
							$end_time = date( 'His', $event_end_timestamp );
415 View Code Duplication
							if ( 8 == strlen( $event['DTSTART'] ) ) {
416
								$exdate_compare = date( 'Ymd', $event_start_timestamp );
417
							} else {
418
								$exdate_compare = date( 'Ymd\THis', $event_start_timestamp );
419
							}
420
421 View Code Duplication
							if ( $event_end_timestamp > $current && $event_start_timestamp < $until && $count_counter <= $rrule_count && $event_start_timestamp >= $date_from_ics && ! in_array( $exdate_compare, $exdates ) ) {
422
								if ( 8 == strlen( $event['DTSTART'] ) ) {
423
									$event['DTSTART'] = date( 'Ymd', $event_start_timestamp );
424
									$event['DTEND'] = date( 'Ymd', $event_end_timestamp );
425
								} else {
426
									$event['DTSTART'] = date( 'Ymd\T', $event_start_timestamp ) . $start_time;
427
									$event['DTEND'] = date( 'Ymd\T', $event_end_timestamp ) . $end_time;
428
								}
429
								if ( $this->timezone->getName() && 8 != strlen( $event['DTSTART'] ) ) {
430
									try {
431
										$adjusted_time = new DateTime( $event['DTSTART'], new DateTimeZone( $this->timezone->getName() ) );
432
										$adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
433
										$event['DTSTART'] = $adjusted_time->format('Ymd\THis');
434
435
										$event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
436
									} catch ( Exception $e ) {
437
										// Invalid argument to DateTime
438
									}
439
								}
440
								$upcoming[] = $event;
441
								$count_counter++;
442
							}
443
						}
444
445
						// Set up next interval and reset $event['DTSTART'] and $event['DTEND'], keeping timestamps intact
446
						$next_start_timestamp = strtotime( "+ {$interval} {$frequency}s", $event_start_timestamp );
447
						if ( 8 == strlen( $event['DTSTART'] ) ) {
448
							$event['DTSTART'] = date( 'Ymd', $next_start_timestamp );
449
							$event['DTEND'] = date( 'Ymd', strtotime( $event['DTSTART'] ) + $duration );
450 View Code Duplication
						} else {
451
							$event['DTSTART'] = date( 'Ymd\THis', $next_start_timestamp );
452
							$event['DTEND'] = date( 'Ymd\THis', strtotime( $event['DTSTART'] ) + $duration );
453
						}
454
455
						// Move recurring event date forward
456
						$recurring_event_date_start = $event['DTSTART'];
457
					}
458
					$set_recurring_events[] = $uid;
459
460
				}
461
462 View Code Duplication
			} else {
463
				// Process normal events
464
				if ( strtotime( isset( $event['DTEND'] ) ? $event['DTEND'] : $event['DTSTART'] ) >= $current ) {
465
					$upcoming[] = $event;
466
				}
467
			}
468
		}
469
		return $upcoming;
470
	}
471
472
	/**
473
	 * Parse events from an iCalendar feed
474
	 *
475
	 * @param string $url (default: '')
476
	 * @return array | false on failure
477
	 */
478
	public function parse( $url = '' ) {
479
		$cache_group = 'icalendar_reader_parse';
480
		$disable_get_key = 'disable:' . md5( $url );
481
482
		// Check to see if previous attempts have failed
483
		if ( false !== wp_cache_get( $disable_get_key, $cache_group ) )
484
			return false;
485
486
		// rewrite webcal: URI schem to HTTP
487
		$url = preg_replace('/^webcal/', 'http', $url );
488
		// try to fetch
489
		$r = wp_remote_get( $url, array( 'timeout' => 3, 'sslverify' => false ) );
490
		if ( 200 !== wp_remote_retrieve_response_code( $r ) ) {
491
			// We were unable to fetch any content, so don't try again for another 60 seconds
492
			wp_cache_set( $disable_get_key, 1, $cache_group, 60 );
493
			return false;
494
		}
495
496
		$body = wp_remote_retrieve_body( $r );
497
		if ( empty( $body ) )
498
			return false;
499
500
		$body = str_replace( "\r\n", "\n", $body );
501
		$lines = preg_split( "/\n(?=[A-Z])/", $body );
502
503
		if ( empty( $lines ) )
504
			return false;
505
506
		if ( false === stristr( $lines[0], 'BEGIN:VCALENDAR' ) )
507
			return false;
508
509
		foreach ( $lines as $line ) {
510
			$add  = $this->key_value_from_string( $line );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
511
			if ( ! $add ) {
512
				$this->add_component( $type, false, $line );
0 ignored issues
show
Bug introduced by
The variable $type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
513
				continue;
514
			}
515
			list( $keyword, $value ) = $add;
516
517
			switch ( $keyword ) {
518
				case 'BEGIN':
519
				case 'END':
520
					switch ( $line ) {
521
						case 'BEGIN:VTODO':
522
							$this->todo_count++;
523
							$type = 'VTODO';
524
							break;
525
						case 'BEGIN:VEVENT':
526
							$this->event_count++;
527
							$type = 'VEVENT';
528
							break;
529
						case 'BEGIN:VCALENDAR':
530
						case 'BEGIN:DAYLIGHT':
531
						case 'BEGIN:VTIMEZONE':
532
						case 'BEGIN:STANDARD':
533
							$type = $value;
534
							break;
535
						case 'END:VTODO':
536
						case 'END:VEVENT':
537
						case 'END:VCALENDAR':
538
						case 'END:DAYLIGHT':
539
						case 'END:VTIMEZONE':
540
						case 'END:STANDARD':
541
							$type = 'VCALENDAR';
542
							break;
543
					}
544
					break;
545
				case 'TZID':
546
					if ( 'VTIMEZONE' == $type && ! $this->timezone )
547
						$this->timezone = $this->timezone_from_string( $value );
548
					break;
549
				case 'X-WR-TIMEZONE':
550
					if ( ! $this->timezone )
551
						$this->timezone = $this->timezone_from_string( $value );
552
					break;
553
				default:
554
					$this->add_component( $type, $keyword, $value );
555
					break;
556
			}
557
		}
558
559
		// Filter for RECURRENCE-IDs
560
		$recurrences = array();
561
		if ( array_key_exists( 'VEVENT', $this->cal ) ) {
562
			foreach ( $this->cal['VEVENT'] as $event ) {
563
				if ( isset( $event['RECURRENCE-ID'] ) ) {
564
					$recurrences[] = $event;
565
				}
566
			}
567
			foreach ( $recurrences as $recurrence ) {
568
				for ( $i = 0; $i < count( $this->cal['VEVENT'] ); $i++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
569
					if ( $this->cal['VEVENT'][ $i ]['UID'] == $recurrence['UID'] && ! isset( $this->cal['VEVENT'][ $i ]['RECURRENCE-ID'] ) ) {
570
						$this->cal['VEVENT'][ $i ]['EXDATE'][] = $recurrence['RECURRENCE-ID'];
571
						break;
572
					}
573
				}
574
			}
575
		}
576
577
		return $this->cal;
578
	}
579
580
	/**
581
	 * Parse key:value from a string
582
	 *
583
	 * @param string $text (default: '')
584
	 * @return array
585
	 */
586
	public function key_value_from_string( $text = '' ) {
587
		preg_match( '/([^:]+)(;[^:]+)?[:]([\w\W]*)/', $text, $matches );
588
589
		if ( 0 == count( $matches ) )
590
			return false;
591
592
		return array( $matches[1], $matches[3] );
593
	}
594
595
	/**
596
	 * Convert a timezone name into a timezone object.
597
	 *
598
	 * @param string $text Timezone name. Example: America/Chicago
599
	 * @return object|null A DateTimeZone object if the conversion was successful.
600
	 */
601
	private function timezone_from_string( $text ) {
602
		try {
603
			$timezone = new DateTimeZone( $text );
604
		} catch ( Exception $e ) {
605
			$blog_timezone = get_option( 'timezone_string' );
606
			if ( ! $blog_timezone ) {
607
				$blog_timezone = 'Etc/UTC';
608
			}
609
610
			$timezone = new DateTimeZone( $blog_timezone );
611
		}
612
613
		return $timezone;
614
	}
615
616
	/**
617
	 * Add a component to the calendar array
618
	 *
619
	 * @param string $component (default: '')
620
	 * @param string $keyword (default: '')
621
	 * @param string $value (default: '')
622
	 * @return void
623
	 */
624
	public function add_component( $component = '', $keyword = '', $value = '' ) {
625
		if ( false == $keyword ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $keyword of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
626
			$keyword = $this->last_keyword;
0 ignored issues
show
Bug introduced by
The property last_keyword does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
627
			switch ( $component ) {
628
			case 'VEVENT':
629
				$value = $this->cal[ $component ][ $this->event_count - 1 ][ $keyword ] . $value;
630
				break;
631
			case 'VTODO' :
632
				$value = $this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ] . $value;
633
				break;
634
			}
635
		}
636
637
		/*
638
		 * Some events have a specific timezone set in their start/end date,
639
		 * and it may or may not be different than the calendar timzeone.
640
		 * Valid formats include:
641
		 * DTSTART;TZID=Pacific Standard Time:20141219T180000
642
		 * DTEND;TZID=Pacific Standard Time:20141219T200000
643
		 * EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z
644
		 * EXDATE;VALUE=DATE:2015050
645
		 * EXDATE;TZID=America/New_York:20150424T170000
646
		 * EXDATE;TZID=Pacific Standard Time:20120615T140000,20120629T140000,20120706T140000
647
		 */
648
649
		// Always store EXDATE as an array
650
		if ( stristr( $keyword, 'EXDATE' ) ) {
651
			$value = explode( ',', $value );
652
		}
653
654
		// Adjust DTSTART, DTEND, and EXDATE according to their TZID if set
655
		if ( strpos( $keyword, ';' ) && ( stristr( $keyword, 'DTSTART' ) || stristr( $keyword, 'DTEND' ) || stristr( $keyword, 'EXDATE' ) || stristr( $keyword, 'RECURRENCE-ID' ) ) ) {
656
			$keyword = explode( ';', $keyword );
657
658
			$tzid = false;
659
			if ( 2 == count( $keyword ) ) {
660
				$tparam = $keyword[1];
661
662
				if ( strpos( $tparam, "TZID" ) !== false ) {
663
					$tzid = $this->timezone_from_string( str_replace( 'TZID=', '', $tparam ) );
664
				}
665
			}
666
667
			// Normalize all times to default UTC
668
			if ( $tzid ) {
669
				$adjusted_times = array();
670
				foreach ( (array) $value as $v ) {
671
					try {
672
						$adjusted_time = new DateTime( $v, $tzid );
673
						$adjusted_time->setTimeZone( new DateTimeZone( 'UTC' ) );
674
						$adjusted_times[] = $adjusted_time->format('Ymd\THis');
675
					} catch ( Exception $e ) {
676
						// Invalid argument to DateTime
677
						return;
678
					}
679
				}
680
				$value = $adjusted_times;
681
			}
682
683
			// Format for adding to event
684
			$keyword = $keyword[0];
685
			if ( 'EXDATE' != $keyword ) {
686
				$value = implode( (array) $value );
687
			}
688
		}
689
690
		foreach ( (array) $value as $v ) {
691
			switch ($component) {
692 View Code Duplication
				case 'VTODO':
693
					if ( 'EXDATE' == $keyword ) {
694
						$this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ][] = $v;
695
					} else {
696
						$this->cal[ $component ][ $this->todo_count - 1 ][ $keyword ] = $v;
697
					}
698
					break;
699 View Code Duplication
				case 'VEVENT':
700
					if ( 'EXDATE' == $keyword ) {
701
						$this->cal[ $component ][ $this->event_count - 1 ][ $keyword ][] = $v;
702
					} else {
703
						$this->cal[ $component ][ $this->event_count - 1 ][ $keyword ] = $v;
704
					}
705
					break;
706
				default:
707
					$this->cal[ $component ][ $keyword ] = $v;
708
					break;
709
			}
710
		}
711
		$this->last_keyword = $keyword;
712
	}
713
714
	/**
715
	 * Escape strings with wp_kses, allow links
716
	 *
717
	 * @param string $string (default: '')
718
	 * @return string
719
	 */
720
	public function escape( $string = '' ) {
721
		// Unfold content lines per RFC 5545
722
		$string = str_replace( "\n\t", '', $string );
723
		$string = str_replace( "\n ", '', $string );
724
725
		$allowed_html = array(
726
			'a' => array(
727
				'href'  => array(),
728
				'title' => array()
729
			)
730
		);
731
732
		$allowed_tags = '';
733
		foreach ( array_keys( $allowed_html ) as $tag ) {
734
			$allowed_tags .= "<{$tag}>";
735
		}
736
737
		// Running strip_tags() first with allowed tags to get rid of remaining gallery markup, etc
738
		// because wp_kses() would only htmlentity'fy that. Then still running wp_kses(), for extra
739
		// safety and good measure.
740
		return wp_kses( strip_tags( $string, $allowed_tags ), $allowed_html );
741
	}
742
743
	/**
744
	 * Render the events
745
	 *
746
	 * @param string $url (default: '')
747
	 * @param string $context (default: 'widget') or 'shortcode'
0 ignored issues
show
Bug introduced by
There is no parameter named $context. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
748
	 * @return mixed bool|string false on failure, rendered HTML string on success.
749
	 */
750
	public function render( $url = '', $args = array() ) {
751
752
		$args = wp_parse_args( $args, array(
753
			'context' => 'widget',
754
			'number' => 5
755
		) );
756
757
		$events = $this->get_events( $url, $args['number'] );
758
		$events = $this->apply_timezone_offset( $events );
759
760
		if ( empty( $events ) )
761
			return false;
762
763
		ob_start();
764
765
		if ( 'widget' == $args['context'] ) : ?>
766
		<ul class="upcoming-events">
767
			<?php foreach ( $events as $event ) : ?>
768
			<li>
769
				<strong class="event-summary"><?php echo $this->escape( stripslashes( $event['SUMMARY'] ) ); ?></strong>
770
				<span class="event-when"><?php echo $this->formatted_date( $event ); ?></span>
771 View Code Duplication
				<?php if ( ! empty( $event['LOCATION'] ) ) : ?>
772
					<span class="event-location"><?php echo $this->escape( stripslashes( $event['LOCATION'] ) ); ?></span>
773
				<?php endif; ?>
774 View Code Duplication
				<?php if ( ! empty( $event['DESCRIPTION'] ) ) : ?>
775
					<span class="event-description"><?php echo wp_trim_words( $this->escape( stripcslashes( $event['DESCRIPTION'] ) ) ); ?></span>
776
				<?php endif; ?>
777
			</li>
778
			<?php endforeach; ?>
779
		</ul>
780
		<?php endif;
781
782
		if ( 'shortcode' == $args['context'] ) : ?>
783
		<table class="upcoming-events">
784
			<thead>
785
				<tr>
786
					<th><?php esc_html_e( 'Location', 'jetpack' ); ?></th>
787
					<th><?php esc_html_e( 'When', 'jetpack' ); ?></th>
788
					<th><?php esc_html_e( 'Summary', 'jetpack' ); ?></th>
789
					<th><?php esc_html_e( 'Description', 'jetpack' ); ?></th>
790
				</tr>
791
			</thead>
792
			<tbody>
793
			<?php foreach ( $events as $event ) : ?>
794
				<tr>
795
					<td><?php echo empty( $event['LOCATION'] ) ? '&nbsp;' : $this->escape( stripslashes( $event['LOCATION'] ) ); ?></td>
796
					<td><?php echo $this->formatted_date( $event ); ?></td>
797
					<td><?php echo empty( $event['SUMMARY'] ) ? '&nbsp;' : $this->escape( stripslashes( $event['SUMMARY'] ) ); ?></td>
798
					<td><?php echo empty( $event['DESCRIPTION'] ) ? '&nbsp;' : wp_trim_words( $this->escape( stripcslashes( $event['DESCRIPTION'] ) ) ); ?></td>
799
				</tr>
800
			<?php endforeach; ?>
801
			</tbody>
802
		</table>
803
		<?php endif;
804
805
		$rendered = ob_get_clean();
806
807
		if ( empty( $rendered ) )
808
			return false;
809
810
		return $rendered;
811
	}
812
813
	public function formatted_date( $event ) {
814
815
		$date_format = get_option( 'date_format' );
816
		$time_format = get_option( 'time_format' );
817
		$start = strtotime( $event['DTSTART'] );
818
		$end = isset( $event['DTEND'] ) ? strtotime( $event['DTEND'] ) : false;
819
820
		$all_day = ( 8 == strlen( $event['DTSTART'] ) );
821
822
		if ( !$all_day && $this->timezone ) {
823
			try {
824
				$start_time = new DateTime( $event['DTSTART'] );
825
				$timezone_offset = $this->timezone->getOffset( $start_time );
826
				$start += $timezone_offset;
827
828
				if ( $end ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $end of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
829
					$end += $timezone_offset;
830
				}
831
			} catch ( Exception $e ) {
832
				// Invalid argument to DateTime
833
			}
834
		}
835
		$single_day = $end ? ( $end - $start ) <= DAY_IN_SECONDS : true;
836
837
		/* Translators: Date and time */
838
		$date_with_time = __( '%1$s at %2$s' , 'jetpack' );
839
		/* Translators: Two dates with a separator */
840
		$two_dates = __( '%1$s &ndash; %2$s' , 'jetpack' );
841
842
		// we'll always have the start date. Maybe with time
843 View Code Duplication
		if ( $all_day )
844
			$date = date_i18n( $date_format, $start );
845
		else
846
			$date = sprintf( $date_with_time, date_i18n( $date_format, $start ), date_i18n( $time_format, $start ) );
847
848
		// single day, timed
849
		if ( $single_day && ! $all_day && false !== $end )
850
			$date = sprintf( $two_dates, $date, date_i18n( $time_format, $end ) );
851
852
		// multi-day
853
		if ( ! $single_day ) {
854
855 View Code Duplication
			if ( $all_day ) {
856
				// DTEND for multi-day events represents "until", not "including", so subtract one minute
857
				$end_date = date_i18n( $date_format, $end - 60 );
858
			} else {
859
				$end_date = sprintf( $date_with_time, date_i18n( $date_format, $end ), date_i18n( $time_format, $end ) );
860
			}
861
862
			$date = sprintf( $two_dates, $date, $end_date );
863
864
		}
865
866
		return $date;
867
	}
868
869
	protected function sort_by_recent( $list ) {
870
		$dates = $sorted_list = array();
871
872
		foreach ( $list as $key => $row ) {
873
			$date = $row['DTSTART'];
874
			// pad some time onto an all day date
875
			if ( 8 === strlen( $date ) )
876
				$date .= 'T000000Z';
877
			$dates[$key] = $date;
878
		}
879
		asort( $dates );
880
		foreach( $dates as $key => $value ) {
881
			$sorted_list[$key] = $list[$key];
882
		}
883
		unset($list);
884
		return $sorted_list;
885
	}
886
887
}
888
889
890
/**
891
 * Wrapper function for iCalendarReader->get_events()
892
 *
893
 * @param string $url (default: '')
894
 * @return array
895
 */
896
function icalendar_get_events( $url = '', $count = 5 ) {
897
	// Find your calendar's address http://support.google.com/calendar/bin/answer.py?hl=en&answer=37103
898
	$ical = new iCalendarReader();
899
	return $ical->get_events( $url, $count );
900
}
901
902
/**
903
 * Wrapper function for iCalendarReader->render()
904
 *
905
 * @param string $url (default: '')
906
 * @param string $context (default: 'widget') or 'shortcode'
0 ignored issues
show
Bug introduced by
There is no parameter named $context. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
907
 * @return mixed bool|string false on failure, rendered HTML string on success.
908
 */
909
function icalendar_render_events( $url = '', $args = array() ) {
910
	$ical = new iCalendarReader();
911
	return $ical->render( $url, $args );
912
}
913