Completed
Push — master ( c18f14...017364 )
by mw
292:38 queued 257:37
created

includes/dataitems/SMW_DI_Time.php (9 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use SMW\Exception\DataItemException;
4
use SMW\DataValues\Time\JulianDay;
5
use SMW\DataValues\Time\CalendarModel;
6
7
/**
8
 * This class implements time data items.
9
 * Such data items represent a unique point in time, given in either Julian or
10
 * Gregorian notation (possibly proleptic), and a precision setting that states
11
 * which of the components year, month, day, time were specified expicitly.
12
 * Even when not specified, the data item always assumes default values for the
13
 * missing parts, so the item really captures one point in time, no intervals.
14
 * Times are always assumed to be in UTC.
15
 *
16
 * "Y0K issue": Neither the Gregorian nor the Julian calendar assume a year 0,
17
 * i.e. the year 1 BC(E) was followed by 1 AD/CE. See
18
 * http://en.wikipedia.org/wiki/Year_zero
19
 * This implementation adheres to this convention and disallows year 0. The
20
 * stored year numbers use positive numbers for CE and negative numbers for
21
 * BCE. This is not just relevant for the question of how many years have
22
 * (exactly) passed since a given date, but also for the location of leap
23
 * years.
24
 *
25
 * @since 1.6
26
 *
27
 * @author Markus Krötzsch
28
 * @ingroup SMWDataItems
29
 */
30
class SMWDITime extends SMWDataItem implements CalendarModel {
31
32
	const PREC_Y    = SMW_PREC_Y;
33
	const PREC_YM   = SMW_PREC_YM;
34
	const PREC_YMD  = SMW_PREC_YMD;
35
	const PREC_YMDT = SMW_PREC_YMDT;
36
37
	/**
38
	 * The year before which we do not accept anything but year numbers and
39
	 * largely discourage calendar models.
40
	 */
41
	const PREHISTORY = -10000;
42
43
	/**
44
	 * Maximal number of days in a given month.
45
	 * @var array
46
	 */
47
	protected static $m_daysofmonths = array( 1 => 31, 2 => 29, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 );
48
49
	/**
50
	 * Precision SMWDITime::PREC_Y, SMWDITime::PREC_YM,
51
	 * SMWDITime::PREC_YMD, or SMWDITime::PREC_YMDT.
52
	 * @var integer
53
	 */
54
	protected $m_precision;
55
	/**
56
	 * Calendar model: SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN.
57
	 * @var integer
58
	 */
59
	protected $m_model;
60
	/**
61
	 * Number of year, possibly negative.
62
	 * @var integer
63
	 */
64
	protected $m_year;
65
	/**
66
	 * Number of month.
67
	 * @var integer
68
	 */
69
	protected $m_month;
70
	/**
71
	 * Number of day.
72
	 * @var integer
73
	 */
74
	protected $m_day;
75
	/**
76
	 * Hours of the day.
77
	 * @var integer
78
	 */
79
	protected $m_hours;
80
	/**
81
	 * Minutes of the hour.
82
	 * @var integer
83
	 */
84
	protected $m_minutes;
85
	/**
86
	 * Seconds of the minute.
87
	 * @var integer
88
	 */
89
	protected $m_seconds;
90
91
	/**
92
	 * @var integer
93
	 */
94
	protected $timezone;
95
96
	/**
97
	 * @var integer|null
98
	 */
99
	protected $era = null;
100
101
	/**
102
	 * @var integer
103
	 */
104
	protected $julianDay = null;
105
106
	/**
107
	 * Create a time data item. All time components other than the year can
108
	 * be false to indicate that they are not specified. This will affect
109
	 * the internal precision setting. The missing values are initialised
110
	 * to minimal values (0 or 1) for internal calculations.
111
	 *
112
	 * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
113
	 * @param $year integer number of the year (possibly negative)
114
	 * @param $month mixed integer number or false
115
	 * @param $day mixed integer number or false
116
	 * @param $hour mixed integer number or false
117
	 * @param $minute mixed integer number or false
118
	 * @param $second mixed integer number or false
119
	 * @param integer|false $timezone
120
	 *
121
	 * @todo Implement more validation here.
122
	 */
123 232
	public function __construct( $calendarmodel, $year, $month = false, $day = false,
124
	                             $hour = false, $minute = false, $second = false, $timezone = false ) {
125
126 232
		if ( ( $calendarmodel != self::CM_GREGORIAN ) && ( $calendarmodel != self::CM_JULIAN ) ) {
127
			throw new DataItemException( "Unsupported calendar model constant \"$calendarmodel\"." );
128
		}
129
130 232
		if ( $year == 0 ) {
131 1
			throw new DataItemException( "There is no year 0 in Gregorian and Julian calendars." );
132
		}
133
134 232
		$this->m_model   = $calendarmodel;
135 232
		$this->m_year    = intval( $year );
136 232
		$this->m_month   = $month != false ? intval( $month ) : 1;
137 232
		$this->m_day     = $day != false ? intval( $day ) : 1;
138 232
		$this->m_hours   = $hour !== false ? intval( $hour ) : 0;
139 232
		$this->m_minutes = $minute !== false ? intval( $minute ) : 0;
140 232
		$this->m_seconds = $second !== false ? floatval( $second ) : 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like $second !== false ? floatval($second) : 0 can also be of type double. However, the property $m_seconds is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
141
142 232
		$this->timezone = $timezone !== false ? $timezone : 0;
143 232
		$year = strval( $year );
144 232
		$this->era      = $year{0} === '+' ? 1 : ( $year{0} === '-' ? -1 : 0 );
145
146 232
		if ( $this->isOutOfBoundsBySome() ) {
147
			throw new DataItemException( "Part of the date is out of bounds." );
148
		}
149
150 232
		if ( $this->isOutOfBoundsByDayNumberOfMonth() ) {
151
			throw new DataItemException( "Month {$this->m_month} in year {$this->m_year} did not have {$this->m_day} days in this calendar model." );
152
		}
153
154 232
		$this->setPrecisionLevelBy( $month, $day, $hour );
155 232
	}
156
157
	/**
158
	 * @since 1.6
159
	 *
160
	 * @return integer
161
	 */
162 224
	public function getDIType() {
163 224
		return SMWDataItem::TYPE_TIME;
164
	}
165
166
	/**
167
	 * @since 1.6
168
	 *
169
	 * @return integer
170
	 */
171 223
	public function getCalendarModel() {
172 223
		return $this->m_model;
173
	}
174
175
	/**
176
	 * @since 2.5
177
	 *
178
	 * @return integer
179
	 */
180 1
	public function getTimezone() {
181 1
		return $this->timezone;
182
	}
183
184
	/**
185
	 * @since 1.6
186
	 *
187
	 * @return integer
188
	 */
189 84
	public function getPrecision() {
190 84
		return $this->m_precision;
191
	}
192
193
	/**
194
	 * Indicates whether a user explicitly used an era marker even for a positive
195
	 * year.
196
	 *
197
	 * - [-1] indicates BC(E)
198
	 * - [0]/null indicates no era marker
199
	 * - [1] indicates AD/CE was used
200
	 *
201
	 * @since 2.4
202
	 *
203
	 * @return integer
204
	 */
205 26
	public function getEra() {
206 26
		return $this->era;
207
	}
208
209
	/**
210
	 * @since 1.6
211
	 *
212
	 * @return integer
213
	 */
214 223
	public function getYear() {
215 223
		return $this->m_year;
216
	}
217
218
	/**
219
	 * @since 1.6
220
	 *
221
	 * @return integer
222
	 */
223 223
	public function getMonth() {
224 223
		return $this->m_month;
225
	}
226
227
	/**
228
	 * @since 1.6
229
	 *
230
	 * @return integer
231
	 */
232 223
	public function getDay() {
233 223
		return $this->m_day;
234
	}
235
236
	/**
237
	 * @since 1.6
238
	 *
239
	 * @return integer
240
	 */
241 223
	public function getHour() {
242 223
		return $this->m_hours;
243
	}
244
245
	/**
246
	 * @since 1.6
247
	 *
248
	 * @return integer
249
	 */
250 223
	public function getMinute() {
251 223
		return $this->m_minutes;
252
	}
253
254
	/**
255
	 * @since 1.6
256
	 *
257
	 * @return integer
258
	 */
259 224
	public function getSecond() {
260 224
		return $this->m_seconds;
261
	}
262
263
	/**
264
	 * @since 2.4
265
	 *
266
	 * @return string
267
	 */
268 5
	public function getCalendarModelLiteral() {
269
270
		$literal = array(
271 5
			self::CM_GREGORIAN => '',
272 5
			self::CM_JULIAN    => 'JL'
273
		);
274
275 5
		return $literal[$this->m_model];
276
	}
277
278
	/**
279
	 * @since 2.4
280
	 *
281
	 * @param DateTime $dateTime
282
	 *
283
	 * @return SMWDITime|false
284
	 */
285 2
	public static function newFromDateTime( DateTime $dateTime ) {
286
287 2
		$calendarModel = self::CM_JULIAN;
288
289 2
		$year = $dateTime->format( 'Y' );
290 2
		$month = $dateTime->format( 'm' );
291 2
		$day = $dateTime->format( 'd' );
292
293 2
		if ( ( $year > 1582 ) ||
294 1
			( ( $year == 1582 ) && ( $month > 10 ) ) ||
295 2
			( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
296 2
			$calendarModel = self::CM_GREGORIAN;
297
		}
298
299 2
		return self::doUnserialize( $calendarModel . '/' . $dateTime->format( 'Y/m/d/H/i/s.u' ) );
300
	}
301
302
	/**
303
	 * @since 2.4
304
	 *
305
	 * @return DateTime
306
	 */
307 16
	public function asDateTime() {
308
309 16
		$year = str_pad( $this->m_year, 4, '0', STR_PAD_LEFT );
310
311
		// Avoid "Failed to parse time string (-900-02-02 00:00:00) at
312
		// position 7 (-): Double timezone specification"
313 16
		if ( $this->m_year < 0 ) {
314 2
			$year = '-' . str_pad( $this->m_year * -1, 4, '0', STR_PAD_LEFT );
315
		}
316
317
		// Avoid "Failed to parse time string (1300-11-02 12:03:25.888499949) at
318
		// at position 11 (1): The timezone could not ..."
319 16
		$seconds = number_format( str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), 7, '.', '' );
320
321 16
		$time = $year . '-' .
322 16
			str_pad( $this->m_month, 2, '0', STR_PAD_LEFT )     . '-' .
323 16
			str_pad( $this->m_day, 2, '0', STR_PAD_LEFT )       . ' ' .
324 16
			str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT )     . ':' .
325 16
			str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT )   . ':' .
326 16
			$seconds;
327
328 16
		return new DateTime( $time );
329
	}
330
331
	/**
332
	 * Creates and returns a new instance of SMWDITime from a MW timestamp.
333
	 *
334
	 * @since 1.8
335
	 *
336
	 * @param string $timestamp must be in format
337
	 *
338
	 * @return SMWDITime|false
339
	 */
340 215
	public static function newFromTimestamp( $timestamp ) {
341 215
		$timestamp = wfTimestamp( TS_MW, (string)$timestamp );
342
343 215
		if ( $timestamp === false ) {
344
			return false;
345
		}
346
347 215
		return new self(
348 215
			self::CM_GREGORIAN,
349 215
			substr( $timestamp, 0, 4 ),
350 215
			substr( $timestamp, 4, 2 ),
351 215
			substr( $timestamp, 6, 2 ),
352 215
			substr( $timestamp, 8, 2 ),
353 215
			substr( $timestamp, 10, 2 ),
354 215
			substr( $timestamp, 12, 2 )
355
		);
356
	}
357
358
	/**
359
	 * Returns a MW timestamp representation of the value.
360
	 *
361
	 * @since 1.6.2
362
	 *
363
	 * @param $outputtype
364
	 *
365
	 * @return mixed
366
	 */
367 213
	public function getMwTimestamp( $outputtype = TS_UNIX ) {
368 213
		return wfTimestamp(
369
			$outputtype,
370 213
			implode( '', array(
371 213
				str_pad( $this->m_year, 4, '0', STR_PAD_LEFT ),
372 213
				str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ),
373 213
				str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ),
374 213
				str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ),
375 213
				str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ),
376 213
				str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ),
377
			) )
378
		);
379
	}
380
381
	/**
382
	 * Get the data in the specified calendar model. This might require
383
	 * conversion.
384
	 * @note Conversion can be unreliable for very large absolute year
385
	 * numbers when the internal calculations hit floating point accuracy.
386
	 * Callers might want to avoid this (calendar models make little sense
387
	 * in such cases anyway).
388
	 * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
389
	 * @return SMWDITime
390
	 */
391 81
	public function getForCalendarModel( $calendarmodel ) {
392 81
		if ( $calendarmodel == $this->m_model ) {
393 81
			return $this;
394
		} else {
395 4
			return self::newFromJD( $this->getJD(), $calendarmodel, $this->m_precision );
396
		}
397
	}
398
399
	/**
400
	 * Return a number that helps comparing time data items. For
401
	 * dates in the Julian Day era (roughly from 4713 BCE onwards), we use
402
	 * the Julian Day number. For earlier dates, the (negative) year number
403
	 * with a fraction for the date is used (times are ignored). This
404
	 * avoids calculation errors that would occur for very ancient dates
405
	 * if the JD number was used there.
406
	 * @return double sortkey
407
	 */
408 220
	public function getSortKey() {
409 220
		$jd = ( $this->m_year >= -4713 ) ? $jd = $this->getJD() : -1;
410 220
		if ( $jd > 0 ) {
411 220
			return $jd;
412
		} else {
413 3
			return $this->m_year - 1 + ( $this->m_month - 1 ) / 12 + ( $this->m_day - 1 ) / 12 / 31;
414
		}
415
	}
416
417
	/**
418
	 * @since 1.6
419
	 *
420
	 * @return double
421
	 */
422 222
	public function getJD() {
423
424 222
		if ( $this->julianDay !== null ) {
425 41
			return $this->julianDay;
426
		}
427
428 222
		$this->julianDay = JulianDay::getJD(
0 ignored issues
show
Documentation Bug introduced by
The property $julianDay was declared of type integer, but \SMW\DataValues\Time\Jul...(), $this->getSecond()) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
429 222
			$this->getCalendarModel(),
430 222
			$this->getYear(),
431 222
			$this->getMonth(),
432 222
			$this->getDay(),
433 222
			$this->getHour(),
434 222
			$this->getMinute(),
435 222
			$this->getSecond()
436
		);
437
438 222
		return $this->julianDay;
439
	}
440
441
	/**
442
	 * @since 1.6
443
	 *
444
	 * @return string
445
	 */
446 223
	public function getSerialization() {
447 223
		$result = strval( $this->m_model ) . '/' . ( $this->era > 0 ? '+' : '' ) . strval( $this->m_year );
448
449 223
		if ( $this->m_precision >= self::PREC_YM ) {
450 218
			$result .= '/' . strval( $this->m_month );
451
		}
452
453 223
		if ( $this->m_precision >= self::PREC_YMD ) {
454 218
			$result .= '/' . strval( $this->m_day );
455
		}
456
457 223
		if ( $this->m_precision >= self::PREC_YMDT ) {
458 217
			$result .= '/' . strval( $this->m_hours ) . '/' . strval( $this->m_minutes ) . '/' . strval( $this->m_seconds ) . '/' . strval( $this->timezone );
459
		}
460
461 223
		return $result;
462
	}
463
464
	/**
465
	 * Create a data item from the provided serialization string.
466
	 *
467
	 * @return SMWDITime
468
	 */
469 70
	public static function doUnserialize( $serialization ) {
470 70
		$parts = explode( '/', $serialization, 8 );
471 70
		$values = array();
472
473 70
		if ( count( $parts ) <= 1 ) {
474 1
			throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
475
		}
476
477 69
		for ( $i = 0; $i < 8; $i += 1 ) {
478
479 69
			$values[$i] = false;
480
481
			// Can contain something like '1/1970/1/12/11/43/0/Asia/Tokyo'
482 69
			if ( $i == 7 && isset( $parts[$i] ) ) {
483 60
				$values[$i] = strval( $parts[$i] );
484 60
				continue;
485
			}
486
487 69
			if ( $i < count( $parts ) ) {
488
489 69
				if ( $parts[$i] !== '' && !is_numeric( $parts[$i] ) ) {
490 1
					throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid datetime specification." );
491
				}
492
493
				// 6 == seconds, we want to keep microseconds
494 69
				$values[$i] = $i == 6 ? floatval( $parts[$i] ) : intval( $parts[$i] );
495
496
				// Find out whether the input contained an explicit AD/CE era marker
497 69
				if ( $i == 1 ) {
498 69
					$values[$i] = ( $parts[1]{0} === '+' ? '+' : '' ) . $values[$i];
499
				}
500
			}
501
		}
502
503 68
		return new self( $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6], $values[7] );
504
	}
505
506
	/**
507
	 * Create a new time dataItem from a specified Julian Day number,
508
	 * calendar model, presicion.
509
	 *
510
	 * @param double $jdValue
511
	 * @param integer|null $calendarmodel
512
	 * @param integer|null $precision
513
	 *
514
	 * @return DITime object
515
	 */
516 11
	public static function newFromJD( $jdValue, $calendarModel = null, $precision = null, $timezone = false ) {
517
518 11
		$hour = $minute = $second = false;
519 11
		$year = $month = $day = false;
520
521 11
		if ( $precision === null ) {
522 3
			$precision = strpos( strval( $jdValue ), '.5' ) !== false ? self::PREC_YMD : self::PREC_YMDT;
523
		}
524
525 11
		list( $calendarModel, $year, $month, $day ) = JulianDay::JD2Date( $jdValue, $calendarModel );
526
527 11
		if ( $precision <= self::PREC_YM ) {
528
			$day = false;
529
			if ( $precision === self::PREC_Y ) {
530
				$month = false;
531
			}
532
		}
533
534 11
		if ( $precision === self::PREC_YMDT ) {
535 9
			list( $hour, $minute, $second ) = JulianDay::JD2Time( $jdValue );
536
		}
537
538 11
		return new self( $calendarModel, $year, $month, $day, $hour, $minute, $second, $timezone );
0 ignored issues
show
It seems like $month defined by \SMW\DataValues\Time\Jul...dValue, $calendarModel) on line 525 can also be of type double or integer; however, SMWDITime::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $day defined by \SMW\DataValues\Time\Jul...dValue, $calendarModel) on line 525 can also be of type double; however, SMWDITime::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $hour defined by \SMW\DataValues\Time\JulianDay::JD2Time($jdValue) on line 535 can also be of type double; however, SMWDITime::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $minute defined by \SMW\DataValues\Time\JulianDay::JD2Time($jdValue) on line 535 can also be of type double; however, SMWDITime::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
It seems like $second defined by \SMW\DataValues\Time\JulianDay::JD2Time($jdValue) on line 535 can also be of type double; however, SMWDITime::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
539
	}
540
541
	/**
542
	 * Find out whether the given year number is a leap year.
543
	 * This calculation assumes that neither calendar has a year 0.
544
	 * @param $year integer year number
545
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
546
	 * @return boolean
547
	 */
548 6
	static public function isLeapYear( $year, $calendarmodel ) {
549 6
		$astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
550 6
		if ( $calendarmodel == self::CM_JULIAN ) {
551 4
			return ( $astroyear % 4 ) == 0;
552
		} else {
553 5
			return ( ( $astroyear % 400 ) == 0 ) ||
554 5
			       ( ( ( $astroyear % 4 ) == 0 ) && ( ( $astroyear % 100 ) != 0 ) );
555
		}
556
	}
557
558
	/**
559
	 * Find out how many days the given month had in the given year
560
	 * based on the specified calendar model.
561
	 * This calculation assumes that neither calendar has a year 0.
562
	 * @param $month integer month number
563
	 * @param $year integer year number
564
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
565
	 * @return boolean
566
	 */
567 232
	static public function getDayNumberForMonth( $month, $year, $calendarmodel ) {
568 232
		if ( $month !== 2 ) {
569 231
			return self::$m_daysofmonths[$month];
570 6
		} elseif ( self::isLeapYear( $year, $calendarmodel ) ) {
571 3
			return 29;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 29; (integer) is incompatible with the return type documented by SMWDITime::getDayNumberForMonth of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
572
		} else {
573 4
			return 28;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 28; (integer) is incompatible with the return type documented by SMWDITime::getDayNumberForMonth of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
574
		}
575
	}
576
577
	public function equals( SMWDataItem $di ) {
578
		if ( $di->getDIType() !== SMWDataItem::TYPE_TIME ) {
579
			return false;
580
		}
581
582
		return $di->getSortKey() === $this->getSortKey();
583
	}
584
585 232
	private function isOutOfBoundsBySome() {
586 232
		return ( $this->m_hours < 0 ) || ( $this->m_hours > 23 ) ||
587 232
		( $this->m_minutes < 0 ) || ( $this->m_minutes > 59 ) ||
588 232
		( $this->m_seconds < 0 ) || ( $this->m_seconds > 59 ) ||
589 232
		( $this->m_month < 1 ) || ( $this->m_month > 12 );
590
	}
591
592 232
	private function isOutOfBoundsByDayNumberOfMonth() {
593 232
		return $this->m_day > self::getDayNumberForMonth( $this->m_month, $this->m_year, $this->m_model );
594
	}
595
596 232
	private function setPrecisionLevelBy( $month, $day, $hour ) {
597 232
		if ( $month === false ) {
598 22
			$this->m_precision = self::PREC_Y;
599 226
		} elseif ( $day === false ) {
600 4
			$this->m_precision = self::PREC_YM;
601 226
		} elseif ( $hour === false ) {
602 31
			$this->m_precision = self::PREC_YMD;
603
		} else {
604 223
			$this->m_precision = self::PREC_YMDT;
605
		}
606 232
	}
607
608
}
609