Events   C
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 584
Duplicated Lines 6.16 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 36
loc 584
rs 5.6756
c 0
b 0
f 0
wmc 68
lcom 1
cbo 2

36 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A get_events() 0 7 3
A set_events() 0 3 1
A set_timezone() 0 7 2
A shift() 0 8 2
A private_only() 0 4 1
A public_only() 0 4 1
A recurring() 0 4 1
A not_recurring() 0 4 1
A whole_day() 0 4 1
A not_whole_day() 0 4 1
A multi_day() 0 4 1
A single_day() 0 4 1
A with_location() 0 4 1
A without_location() 0 4 1
B filter_property() 0 19 8
A after() 0 4 2
A before() 0 4 2
B parse() 0 12 6
A get_first() 0 3 1
A get_last() 0 3 1
A get_upcoming() 0 3 1
A get_latest() 0 3 1
A get_closest() 0 8 3
A get_year() 0 8 2
A get_month() 0 13 2
A get_week() 0 14 3
A get_day() 0 8 1
A get_today() 0 5 1
A get_tomorrow() 0 5 1
A get_yesterday() 0 5 1
A filter_events() 0 11 1
A future() 11 11 4
A past() 11 11 4
A from() 7 7 2
A to() 7 7 2

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 Events 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 Events, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Events Collection
4
 *
5
 * @package SimpleCalendar/Events
6
 */
7
namespace SimpleCalendar\Events;
8
9
use Carbon\Carbon;
10
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * The Events.
17
 *
18
 * A collection of Event objects.
19
 *
20
 * @since  3.0.0
21
 */
22
class Events {
23
24
	/**
25
	 * Events.
26
	 *
27
	 * @access public
28
	 * @var array
29
	 */
30
	protected $events = array();
31
32
	/**
33
	 * Timezone.
34
	 *
35
	 * @access public
36
	 * @var string
37
	 */
38
	protected $timezone = 'UTC';
39
40
	/**
41
	 * Constructor.
42
	 *
43
	 * @since 3.0.0
44
	 *
45
	 * @param array $e Events.
46
	 * @param string|\DateTimeZone $tz Timezone.
47
	 */
48
	public function __construct( $e = array(), $tz = 'UTC' ) {
49
		$this->set_events( $e );
50
		$this->set_timezone( $tz );
51
	}
52
53
	/**
54
	 * Get events.
55
	 *
56
	 * @since  3.0.0
57
	 *
58
	 * @param  string|int $n Amount of events (optional).
59
	 *
60
	 * @return array
61
	 */
62
	public function get_events( $n = '' ) {
63
		if ( ! empty( $n ) && ! empty( $this->events ) ) {
64
			$length = absint( $n );
65
			return array_slice( $this->events, 0, $length, true );
66
		}
67
		return $this->events;
68
	}
69
70
	/**
71
	 * Set events.
72
	 *
73
	 * @since 3.0.0
74
	 *
75
	 * @param array $ev Events.
76
	 */
77
	public function set_events( array $ev ) {
78
		$this->events = $ev;
79
	}
80
81
	/**
82
	 * Set timezone.
83
	 *
84
	 * @since  3.0.0
85
	 *
86
	 * @param  string|\DateTimeZone $tz Timezone.
87
	 *
88
	 * @return Events
89
	 */
90
	public function set_timezone( $tz ) {
91
		if ( $tz instanceof \DateTimeZone ) {
92
			$tz = $tz->getName();
93
		}
94
		$this->timezone = simcal_esc_timezone( $tz, $this->timezone );
95
		return $this;
96
	}
97
98
	/**
99
	 * Shift events.
100
	 *
101
	 * @since  3.0.0
102
	 *
103
	 * @param  int $n
104
	 *
105
	 * @return Events
106
	 */
107
	public function shift( $n ) {
108
		if ( ! empty( $this->events ) ) {
109
			$offset = intval( $n );
110
			$length = count( $this->events );
111
			$this->set_events( array_slice( $this->events, $offset, $length, true ) );
112
		}
113
		return $this;
114
	}
115
116
	/**
117
	 * Filter private events.
118
	 *
119
	 * @since  3.0.0
120
	 *
121
	 * @return Events
122
	 */
123
	public function private_only() {
124
		$this->set_events( $this->filter_property( 'public', 'hide' ) );
125
		return $this;
126
	}
127
128
	/**
129
	 * Filter public events.
130
	 *
131
	 * @since  3.0.0
132
	 *
133
	 * @return Events
134
	 */
135
	public function public_only() {
136
		$this->set_events( $this->filter_property( 'public', 'show' ) );
137
		return $this;
138
	}
139
140
	/**
141
	 * Filter recurring events in the current block.
142
	 *
143
	 * @since  3.0.0
144
	 *
145
	 * @return Events
146
	 */
147
	public function recurring() {
148
		$this->set_events( $this->filter_property( 'recurrence', 'show' ) );
149
		return $this;
150
	}
151
152
	/**
153
	 * Filter non recurring events in the current block.
154
	 *
155
	 * @since  3.0.0
156
	 *
157
	 * @return Events
158
	 */
159
	public function not_recurring() {
160
		$this->set_events( $this->filter_property( 'recurrence', 'hide' ) );
161
		return $this;
162
	}
163
164
	/**
165
	 * Filter whole day events in the current block.
166
	 *
167
	 * @since  3.0.0
168
	 *
169
	 * @return Events
170
	 */
171
	public function whole_day() {
172
		$this->set_events( $this->filter_property( 'whole_day', 'show' ) );
173
		return $this;
174
	}
175
176
	/**
177
	 * Filter non whole day in the current block.
178
	 *
179
	 * @since  3.0.0
180
	 *
181
	 * @return Events
182
	 */
183
	public function not_whole_day() {
184
		$this->set_events( $this->filter_property( 'whole_day', 'hide' ) );
185
		return $this;
186
	}
187
188
	/**
189
	 * Filter events spanning multiple days in the current block.
190
	 *
191
	 * @since  3.0.0
192
	 *
193
	 * @return Events
194
	 */
195
	public function multi_day() {
196
		$this->set_events( $this->filter_property( 'multiple_days', 'show' ) );
197
		return $this;
198
	}
199
200
	/**
201
	 * Filter events that do not span multiple days in the current block.
202
	 *
203
	 * @since  3.0.0
204
	 *
205
	 * @return Events
206
	 */
207
	public function single_day() {
208
		$this->set_events( $this->filter_property( 'multiple_days', 'hide' ) );
209
		return $this;
210
	}
211
212
	/**
213
	 * Filter events in the current block that have a location.
214
	 *
215
	 * @since  3.0.0
216
	 *
217
	 * @return Events
218
	 */
219
	public function with_location() {
220
		$this->set_events( $this->filter_property( 'venue', 'show' ) );
221
		return $this;
222
	}
223
224
	/**
225
	 * Filter events in the current block that do not have a location.
226
	 *
227
	 * @since  3.0.0
228
	 *
229
	 * @return Events
230
	 */
231
	public function without_location() {
232
		$this->set_events( $this->filter_property( 'venue', 'hide' ) );
233
		return $this;
234
	}
235
236
	/**
237
	 * Filter whole day events.
238
	 *
239
	 * @since  3.0.0
240
	 * @access private
241
	 *
242
	 * @param  string $property
243
	 * @param  string $toggle
244
	 *
245
	 * @return array
246
	 */
247
	private function filter_property( $property, $toggle ) {
248
		$filtered = array();
249
		if ( ! empty( $this->events ) ) {
250
			foreach ( $this->events as $ts => $events ) {
251
				foreach ( $events as $event ) {
252
					if ( 'hide' == $toggle ) {
253
						if ( ! $event->$property ) {
254
							$filtered[ $ts ][] = $event;
255
						}
256
					} elseif ( 'show' == $toggle ) {
257
						if ( $event->$property ) {
258
							$filtered[ $ts ][] = $event;
259
						}
260
					}
261
				}
262
			}
263
		}
264
		return $filtered;
265
	}
266
267
	/**
268
	 * Filter events in the past.
269
	 *
270
	 * @since  3.0.0
271
	 *
272
	 * @param  int|string $present
273
	 *
274
	 * @return Events
275
	 */
276 View Code Duplication
	public function future( $present = '' ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
		$last = $this->get_last();
278
		$to = $last instanceof Event ? $last->start_utc : false;
279
		if ( $to ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $to 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...
280
			if ( empty( $present ) ) {
281
				$present = Carbon::now( $this->timezone )->getTimestamp();
282
			}
283
			$this->set_events( $this->filter_events( intval( $present ), $to ) );
284
		}
285
		return $this;
286
	}
287
288
	/**
289
	 * Filter events in the future.
290
	 *
291
	 * @since  3.0.0
292
	 *
293
	 * @param  int|string $present
294
	 *
295
	 * @return Events
296
	 */
297 View Code Duplication
	public function past( $present = '' ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
298
		$first = $this->get_last();
299
		$from  = $first instanceof Event ? $first->start_utc : false;
300
		if ( $from ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $from 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...
301
			if ( empty( $present ) ) {
302
				$present = Carbon::now( $this->timezone )->getTimestamp();
303
			}
304
			$this->set_events( $this->filter_events( $from, intval( $present ) ) );
305
		}
306
		return $this;
307
	}
308
309
	/**
310
	 * Filter events after time.
311
	 *
312
	 * @since  3.0.0
313
	 *
314
	 * @param  int|string|\DateTime|Carbon $time
315
	 *
316
	 * @return Events
317
	 */
318
	public function after( $time ) {
319
		$dt = $this->parse( $time );
320
		return ! is_null( $dt ) ? $this->future( $dt->getTimestamp() ) : $this;
321
	}
322
323
	/**
324
	 * Filter events before time.
325
	 *
326
	 * @since  3.0.0
327
	 *
328
	 * @param  int|string|\DateTime|Carbon $time
329
	 *
330
	 * @return Events
331
	 */
332
	public function before( $time ) {
333
		$dt = $this->parse( $time );
334
		return ! is_null( $dt ) ? $this->past( $dt->getTimestamp() ) : $this;
335
	}
336
337
	/**
338
	 * Filter events from a certain time onwards.
339
	 *
340
	 * @since  3.0.0
341
	 *
342
	 * @param  int|string|\DateTime|Carbon $time
343
	 *
344
	 * @return Events
345
	 */
346 View Code Duplication
	public function from( $time ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
347
		$last = $this->parse( $time );
348
		if ( ! is_null( $last ) ) {
349
			$this->set_events( $this->filter_events( $time, $last->getTimestamp() ) );
350
		}
351
		return $this;
352
	}
353
354
	/**
355
	 * Filter events up to to a certain time.
356
	 *
357
	 * @since  3.0.0
358
	 *
359
	 * @param  int|string|\DateTime|Carbon $time
360
	 *
361
	 * @return Events
362
	 */
363 View Code Duplication
	public function to( $time ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
364
		$first = $this->parse( $time );
365
		if ( ! is_null( $first ) ) {
366
			$this->set_events( $this->filter_events( $first->getTimestamp(), $time ) );
367
		}
368
		return $this;
369
	}
370
371
	/**
372
	 * Parse time.
373
	 *
374
	 * @since  3.0.0
375
	 *
376
	 * @param  int|string|\DateTime|Carbon $time
377
	 *
378
	 * @return null|Carbon
379
	 */
380
	private function parse( $time ) {
381
		if ( is_int( $time ) ) {
382
			return Carbon::createFromTimestamp( $time, $this->timezone );
383
		} elseif ( is_string( $time ) && ! empty( $time ) ) {
384
			return Carbon::parse( $time, $this->timezone );
385
		} elseif ( $time instanceof Carbon ) {
386
			return $time->setTimezone( $this->timezone );
387
		} elseif ( $time instanceof \DateTime ) {
388
			return Carbon::instance( $time )->setTimezone( $this->timezone );
389
		}
390
		return null;
391
	}
392
393
	/**
394
	 * Get first event of the current block.
395
	 *
396
	 * @since  3.0.0
397
	 *
398
	 * @return null|Event
399
	 */
400
	public function get_first() {
401
		return array_shift( $this->events );
402
	}
403
404
	/**
405
	 * Get last event of the current block.
406
	 *
407
	 * @since  3.0.0
408
	 *
409
	 * @return null|Event
410
	 */
411
	public function get_last() {
412
		return array_pop( $this->events );
413
	}
414
415
	/**
416
	 * Get the closest event in the future.
417
	 *
418
	 * @since  3.0.0
419
	 *
420
	 * @return null|Event
421
	 */
422
	public function get_upcoming() {
423
		return $this->get_closest( 'future' );
424
	}
425
426
	/**
427
	 * Get the closest event in the past.
428
	 *
429
	 * @since  3.0.0
430
	 *
431
	 * @return null|Event
432
	 */
433
	public function get_latest() {
434
		return $this->get_closest( 'past' );
435
	}
436
437
	/**
438
	 * Get the closest event compared to now.
439
	 *
440
	 * @since  3.0.0
441
	 * @access private
442
	 *
443
	 * @param  string $dir Direction: 'future' or 'past'.
444
	 *
445
	 * @return null|Event
446
	 */
447
	private function get_closest( $dir ) {
448
		if ( 'future' == $dir ) {
449
			return array_shift( $this->future()->get_events() );
0 ignored issues
show
Bug introduced by
$this->future()->get_events() cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
450
		} elseif ( 'past' == $dir ) {
451
			return array_shift( $this->past()->get_events() );
0 ignored issues
show
Bug introduced by
$this->past()->get_events() cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
452
		}
453
		return null;
454
	}
455
456
	/**
457
	 * Get events for the given year.
458
	 *
459
	 * @since  3.0.0
460
	 *
461
	 * @param  int $year Year.
462
	 *
463
	 * @return array Multidimensional array with month number, week number and Event objects for each weekday.
464
	 */
465
	public function get_year( $year ) {
466
		$y = intval( $year );
467
		$months = array();
468
		for ( $m = 1; $m <= 12; $m++ ) {
469
			$months[ strval( $m ) ] = $this->get_month( $y, $m );
470
		}
471
		return $months;
472
	}
473
474
	/**
475
	 * Get events for the given month in the given year.
476
	 *
477
	 * @since  3.0.0
478
	 *
479
	 * @param  int $year  Year.
480
	 * @param  int $month Month number.
481
	 *
482
	 * @return array Multidimensional array with week number, day of the week and array of Event objects for each week day.
483
	 */
484
	public function get_month( $year, $month ) {
485
		$y = intval( $year );
486
		$m = min( max( 1, absint( $month ) ), 12 );
487
		$days  = Carbon::createFromDate( $y, $m, 2, $this->timezone )->startOfMonth()->daysInMonth;
488
		$weeks = array();
489
		for ( $d = 1; $d < $days; $d++ ) {
490
			$current = Carbon::createFromDate( $y, $m, $d );
491
			$week = $current->weekOfYear;
492
			$day  = $current->dayOfWeek;
493
			$weeks[ strval( $week ) ][ strval( $day ) ] = $this->get_day( $y, $m, $d );
494
		}
495
		return $weeks;
496
	}
497
498
	/**
499
	 * Get events for the given week in the given year.
500
	 *
501
	 * @since  3.0.0
502
	 *
503
	 * @param  int $year Year.
504
	 * @param  int $week Week number.
505
	 *
506
	 * @return array Associative array with day of the week for key and array of Event objects for value.
507
	 */
508
	public function get_week( $year, $week ) {
509
		$y = intval( $year );
510
		$w = absint( $week );
511
		$m = date( 'n', strtotime( strval( $y ) . '-W' . strval( $w ) ) );
512
		$month_dt = Carbon::createFromDate( $y, $m, 2, $this->timezone );
513
		$days = array();
514
		for ( $d = 1; $d < $month_dt->daysInMonth; $d++ ) {
515
			$current = Carbon::createFromDate( $y, $m, $d );
516
			if ( $w == $current->weekOfYear ) {
517
				$days[ strval( $current->dayOfWeek ) ] = $this->get_day( $y, $m, $d );
518
			}
519
		}
520
		return $days;
521
	}
522
523
	/**
524
	 * Get events for the given day of the given month in the given year.
525
	 *
526
	 * @since  3.0.0
527
	 *
528
	 * @param  int $year  Year.
529
	 * @param  int $month Month number.
530
	 * @param  int $day   Day of the month number.
531
	 *
532
	 * @return array Event objects for the day.
533
	 */
534
	public function get_day( $year, $month, $day ) {
535
		$y = intval( $year );
536
		$m = min( max( 1, absint( $month ) ), 12 );
537
		$d = min( absint( $day ), 31 );
538
		$from = Carbon::createFromDate( $y, $m, $d, $this->timezone )->startOfDay()->getTimestamp();
539
		$to   = Carbon::createFromDate( $y, $m, $d, $this->timezone )->endOfDay()->getTimestamp();
540
		return $this->filter_events( $from, $to );
541
	}
542
543
	/**
544
	 * Get events for today.
545
	 *
546
	 * @since  3.0.0
547
	 *
548
	 * @return array Event objects for today.
549
	 */
550
	public function get_today() {
551
		$start = Carbon::today( $this->timezone )->startOfDay()->getTimestamp();
552
		$end   = Carbon::today( $this->timezone )->endOfDay()->getTimestamp();
553
		return $this->filter_events( $start, $end );
554
	}
555
556
	/**
557
	 * Get events for tomorrow.
558
	 *
559
	 * @since  3.0.0
560
	 *
561
	 * @return array Event objects for tomorrow.
562
	 */
563
	public function get_tomorrow() {
564
		$start = Carbon::tomorrow( $this->timezone )->startOfDay()->getTimestamp();
565
		$end   = Carbon::tomorrow( $this->timezone )->endOfDay()->getTimestamp();
566
		return $this->filter_events( $start, $end );
567
	}
568
569
	/**
570
	 * Get events for yesterday.
571
	 *
572
	 * @since  3.0.0
573
	 *
574
	 * @return array Event objects for yesterday.
575
	 */
576
	public function get_yesterday() {
577
		$start = Carbon::yesterday( $this->timezone )->startOfDay()->getTimestamp();
578
		$end   = Carbon::yesterday( $this->timezone )->endOfDay()->getTimestamp();
579
		return $this->filter_events( $start, $end );
580
	}
581
582
	/**
583
	 * Filter events by timestamps.
584
	 *
585
	 * @since  3.0.0
586
	 * @access private
587
	 *
588
	 * @param  int $from Lower bound timestamp.
589
	 * @param  int $to   Upper bound timestamp.
590
	 *
591
	 * @return array Filtered array of Event objects.
592
	 */
593
	private function filter_events( $from, $to ) {
594
		$timestamps   = array_keys( $this->events );
595
		$lower_bound  = array_filter( $timestamps,  function( $ts ) use( $from ) {
596
			return intval( $ts ) > intval( $from );
597
		} );
598
		$higher_bound = array_filter( $lower_bound, function( $ts ) use( $to ) {
599
			return intval( $ts ) > intval( $to );
600
		} );
601
		$filtered = array_combine( $higher_bound, $higher_bound );
602
		return array_intersect_key( $this->events, $filtered );
603
	}
604
605
}
606