Completed
Push — master ( 77f43f...02554f )
by mw
34:19
created

SMWDITime::__construct()   F

Complexity

Conditions 22
Paths 386

Size

Total Lines 42
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 22.1298
Metric Value
dl 0
loc 42
ccs 29
cts 31
cp 0.9355
rs 3.5977
cc 22
eloc 29
nc 386
nop 8
crap 22.1298

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
use SMW\DataItemException;
4
5
/**
6
 * This class implements time data items.
7
 * Such data items represent a unique point in time, given in either Julian or
8
 * Gregorian notation (possibly proleptic), and a precision setting that states
9
 * which of the components year, month, day, time were specified expicitly.
10
 * Even when not specified, the data item always assumes default values for the
11
 * missing parts, so the item really captures one point in time, no intervals.
12
 * Times are always assumed to be in UTC.
13
 *
14
 * "Y0K issue": Neither the Gregorian nor the Julian calendar assume a year 0,
15
 * i.e. the year 1 BC(E) was followed by 1 AD/CE. See
16
 * http://en.wikipedia.org/wiki/Year_zero
17
 * This implementation adheres to this convention and disallows year 0. The
18
 * stored year numbers use positive numbers for CE and negative numbers for
19
 * BCE. This is not just relevant for the question of how many years have
20
 * (exactly) passed since a given date, but also for the location of leap
21
 * years.
22
 *
23
 * @since 1.6
24
 *
25
 * @author Markus Krötzsch
26
 * @ingroup SMWDataItems
27
 */
28
class SMWDITime extends SMWDataItem {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
29
30
	const CM_GREGORIAN = 1;
31
	const CM_JULIAN = 2;
32
33
	const PREC_Y    = 0;
34
	const PREC_YM   = 1;
35
	const PREC_YMD  = 2;
36
	const PREC_YMDT = 3;
37
38
	const PREHISTORY = -10000;
39
40
	/**
41
	 * Maximal number of days in a given month.
42
	 * @var array
43
	 */
44
	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 );
45
46
	/**
47
	 * Precision SMWDITime::PREC_Y, SMWDITime::PREC_YM,
48
	 * SMWDITime::PREC_YMD, or SMWDITime::PREC_YMDT.
49
	 * @var integer
50
	 */
51
	protected $m_precision;
52
	/**
53
	 * Calendar model: SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN.
54
	 * @var integer
55
	 */
56
	protected $m_model;
57
	/**
58
	 * Number of year, possibly negative.
59
	 * @var integer
60
	 */
61
	protected $m_year;
62
	/**
63
	 * Number of month.
64
	 * @var integer
65
	 */
66
	protected $m_month;
67
	/**
68
	 * Number of day.
69
	 * @var integer
70
	 */
71
	protected $m_day;
72
	/**
73
	 * Hours of the day.
74
	 * @var integer
75
	 */
76
	protected $m_hours;
77
	/**
78
	 * Minutes of the hour.
79
	 * @var integer
80
	 */
81
	protected $m_minutes;
82
	/**
83
	 * Seconds of the minute.
84
	 * @var integer
85
	 */
86
	protected $m_seconds;
87
88
	/**
89
	 * @var integer
90
	 */
91
	protected $timezone;
92
93
	/**
94
	 * Create a time data item. All time components other than the year can
95
	 * be false to indicate that they are not specified. This will affect
96
	 * the internal precision setting. The missing values are initialised
97
	 * to minimal values (0 or 1) for internal calculations.
98
	 *
99
	 * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
100
	 * @param $year integer number of the year (possibly negative)
101
	 * @param $month mixed integer number or false
102 143
	 * @param $day mixed integer number or false
103
	 * @param $hour mixed integer number or false
104 143
	 * @param $minute mixed integer number or false
105
	 * @param $second mixed integer number or false
106
	 * @param integer|false $timezone
107 143
	 *
108
	 * @todo Implement more validation here.
109
	 */
110 143
	public function __construct( $calendarmodel, $year, $month = false, $day = false,
111 143
	                             $hour = false, $minute = false, $second = false, $timezone = false ) {
112 143
113 143
		if ( ( $calendarmodel != self::CM_GREGORIAN ) && ( $calendarmodel != self::CM_JULIAN ) ) {
114 143
			throw new DataItemException( "Unsupported calendar model constant \"$calendarmodel\"." );
115 143
		}
116 143
117 143
		if ( $year == 0 ) {
118 143
			throw new DataItemException( "There is no year 0 in Gregorian and Julian calendars." );
119 143
		}
120 143
121
		$this->m_model   = $calendarmodel;
122
		$this->m_year    = intval( $year );
123 143
		$this->m_month   = $month != false ? intval( $month ) : 1;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
124
		$this->m_day     = $day != false ? intval( $day ) : 1;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
125
		$this->m_hours   = $hour !== false ? intval( $hour ) : 0;
126 143
		$this->m_minutes = $minute !== false ? intval( $minute ) : 0;
127 15
		$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...
128 143
129 2
		$this->timezone = $timezone !== false ? intval( $timezone ) : 0;
130 138
131 21
		if ( ( $this->m_hours < 0 ) || ( $this->m_hours > 23 ) ||
132 21
		     ( $this->m_minutes < 0 ) || ( $this->m_minutes > 59 ) ||
133 137
		     ( $this->m_seconds < 0 ) || ( $this->m_seconds > 59 ) ||
134
		     ( $this->m_month < 1 ) || ( $this->m_month > 12 ) ) {
135 143
			throw new DataItemException( "Part of the date is out of bounds." );
136
		}
137 145
138 145
		if ( $this->m_day > self::getDayNumberForMonth( $this->m_month, $this->m_year, $this->m_model ) ) {
139
			throw new DataItemException( "Month {$this->m_month} in year {$this->m_year} did not have {$this->m_day} days in this calendar model." );
140
		}
141 12
142 12
		if ( $month === false ) {
143
			$this->m_precision = self::PREC_Y;
144
		} elseif ( $day === false ) {
145 42
			$this->m_precision = self::PREC_YM;
146 42
		} elseif ( $hour === false ) {
147
			$this->m_precision = self::PREC_YMD;
148
		} else {
149 42
			$this->m_precision = self::PREC_YMDT;
150 42
		}
151
	}
152
153 42
	public function getDIType() {
154 42
		return SMWDataItem::TYPE_TIME;
155
	}
156
157 42
	public function getCalendarModel() {
158 42
		return $this->m_model;
159
	}
160
161 40
	public function getPrecision() {
162 40
		return $this->m_precision;
163
	}
164
165 40
	public function getYear() {
166 40
		return $this->m_year;
167
	}
168
169 40
	public function getMonth() {
170 40
		return $this->m_month;
171
	}
172
173
	public function getDay() {
174
		return $this->m_day;
175
	}
176
177
	public function getHour() {
178
		return $this->m_hours;
179
	}
180
181
	public function getMinute() {
182 135
		return $this->m_minutes;
183 135
	}
184
185 135
	public function getSecond() {
186
		return $this->m_seconds;
187
	}
188
189 135
	/**
190 135
	 * @since 2.4
191 135
	 *
192 135
	 * @return string
193 135
	 */
194 135
	public function getCalendarModelLiteral() {
195 135
196 135
		$literal = array(
197 135
			self::CM_GREGORIAN => '',
198
			self::CM_JULIAN    => 'JL'
199
		);
200
201
		return $literal[$this->m_model];
202
	}
203
204
	/**
205
	 * @since 2.4
206
	 *
207
	 * @param DateTime $dateTime
208
	 *
209 134
	 * @return SMWDITime|false
210 134
	 */
211 134
	public static function newFromDateTime( DateTime $dateTime ) {
212 134
213 134
		$calendarModel = self::CM_JULIAN;
214 134
215 134
		$year = $dateTime->format( 'Y' );
216 134
		$month = $dateTime->format( 'm' );
217 134
		$day = $dateTime->format( 'd' );
218 134
219 134
		if ( ( $year > 1582 ) ||
220 134
			( ( $year == 1582 ) && ( $month > 10 ) ) ||
221
			( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
222
			$calendarModel = self::CM_GREGORIAN;
223
		}
224
225
		return self::doUnserialize( $calendarModel . '/' . $dateTime->format( 'Y/m/d/H/i/s.u' ) );
226
	}
227
228
	/**
229
	 * @since 2.4
230
	 *
231
	 * @return DateTime
232
	 */
233 40
	public function asDateTime() {
234 40
235 40
		$year = str_pad( $this->m_year , 4, '0', STR_PAD_LEFT );
236
237 2
		// Avoid "Failed to parse time string (-900-02-02 00:00:00) at
238
		// position 7 (-): Double timezone specification"
239
		if ( $this->m_year < 0 ) {
240
			$year = '-' . str_pad( $this->m_year * -1 , 4, '0', STR_PAD_LEFT );
241
		}
242
243
		// Avoid "Failed to parse time string (1300-11-02 12:03:25.888499949) at
244
		// at position 11 (1): The timezone could not ..."
245
		$seconds = number_format( str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), 7, '.', '' );
246
247
		$time = $year . '-' .
248
			str_pad( $this->m_month, 2, '0', STR_PAD_LEFT )     . '-' .
249
			str_pad( $this->m_day, 2, '0', STR_PAD_LEFT )       . ' ' .
250 141
			str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT )     . ':' .
251 141
			str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT )   . ':' .
252 141
			$seconds;
253 141
254
		return new DateTime( $time );
255 2
	}
256
257
	/**
258
	 * Creates and returns a new instance of SMWDITime from a MW timestamp.
259 144
	 *
260 144
	 * @since 1.8
261 144
	 *
262
	 * @param string $timestamp must be in format
263
	 *
264 143
	 * @return SMWDITime|false
265 143
	 */
266 143
	public static function newFromTimestamp( $timestamp ) {
267 138
		$timestamp = wfTimestamp( TS_MW, (string)$timestamp );
268 138
269 143
		if ( $timestamp === false ) {
270 138
			return false;
271 138
		}
272 143
273 137
		return new self(
274 137
			self::CM_GREGORIAN,
275 143
			substr( $timestamp, 0, 4 ),
276
			substr( $timestamp, 4, 2 ),
0 ignored issues
show
Documentation introduced by
substr($timestamp, 4, 2) is of type string, but the function expects a boolean.

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...
277
			substr( $timestamp, 6, 2 ),
0 ignored issues
show
Documentation introduced by
substr($timestamp, 6, 2) is of type string, but the function expects a boolean.

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...
278
			substr( $timestamp, 8, 2 ),
0 ignored issues
show
Documentation introduced by
substr($timestamp, 8, 2) is of type string, but the function expects a boolean.

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...
279
			substr( $timestamp, 10, 2 ),
0 ignored issues
show
Documentation introduced by
substr($timestamp, 10, 2) is of type string, but the function expects a boolean.

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...
280
			substr( $timestamp, 12, 2 )
0 ignored issues
show
Documentation introduced by
substr($timestamp, 12, 2) is of type string, but the function expects a boolean.

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...
281
		);
282
	}
283 16
284 16
	/**
285 16
	 * Returns a MW timestamp representation of the value.
286
	 *
287 16
	 * @since 1.6.2
288 16
	 *
289 16
	 * @param $outputtype
290 16
	 *
291 16
	 * @return mixed
292
	 */
293
	public function getMwTimestamp( $outputtype = TS_UNIX ) {
294 16
		return wfTimestamp(
295 2
			$outputtype,
296
			implode( '', array(
297 16
				str_pad( $this->m_year, 4, '0', STR_PAD_LEFT ),
298
				str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ),
299 16
				str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ),
300
				str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ),
301
				str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ),
302
				str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ),
303 16
			) )
304
		);
305
	}
306
307
	/**
308
	 * Get the data in the specified calendar model. This might require
309
	 * conversion.
310
	 * @note Conversion can be unreliable for very large absolute year
311
	 * numbers when the internal calculations hit floating point accuracy.
312
	 * Callers might want to avoid this (calendar models make little sense
313
	 * in such cases anyway).
314 5
	 * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
315 5
	 * @return SMWDITime
316 5
	 */
317
	public function getForCalendarModel( $calendarmodel ) {
318
		if ( $calendarmodel == $this->m_model ) {
319
			return $this;
320
		} else {
321
			return self::newFromJD( $this->getJD(), $calendarmodel, $this->m_precision );
322 5
		}
323 5
	}
324 5
325 2
	/**
326
	 * Return a number that helps comparing time data items. For
327 5
	 * dates in the Julian Day era (roughly from 4713 BCE onwards), we use
328
	 * the Julian Day number. For earlier dates, the (negative) year number
329
	 * with a fraction for the date is used (times are ignored). This
330
	 * avoids calculation errors that would occur for very ancient dates
331
	 * if the JD number was used there.
332
	 * @return double sortkey
333
	 */
334
	public function getSortKey() {
335
		$jd = ( $this->m_year >= -4713 ) ? $jd = $this->getJD() : -1;
336
		if ( $jd > 0 ) {
337
			return $jd;
338
		} else {
339
			return $this->m_year - 1 + ( $this->m_month - 1 ) / 12 + ( $this->m_day - 1 ) / 12 / 31;
340 144
		}
341 144
	}
342 144
343 144
	public function getJD() {
344 144
		return self::date2JD( $this->m_year, $this->m_month, $this->m_day, $this->m_model ) +
345 144
		       self::time2JDoffset( $this->m_hours, $this->m_minutes, $this->m_seconds );
346 144
	}
347
348 5
	public function getSerialization() {
349 5
		$result = strval( $this->m_model ) . '/' . strval( $this->m_year );
350 5
351
		if ( $this->m_precision >= self::PREC_YM ) {
352
			$result .= '/' . strval( $this->m_month );
353
		}
354
355
		if ( $this->m_precision >= self::PREC_YMD ) {
356
			$result .= '/' . strval( $this->m_day );
357
		}
358
359
		if ( $this->m_precision >= self::PREC_YMDT ) {
360
			$result .= '/' . strval( $this->m_hours ) . '/' . strval( $this->m_minutes ) . '/' . strval( $this->m_seconds ) . '/' . strval( $this->timezone );
361
		}
362 144
363 144
		return $result;
364
	}
365
366
	/**
367
	 * Create a data item from the provided serialization string.
368
	 *
369
	 * @return SMWDITime
370
	 */
371
	public static function doUnserialize( $serialization ) {
372
		$parts = explode( '/', $serialization, 8 );
373
		$values = array();
374
375
		for ( $i = 0; $i < 8; $i += 1 ) {
376 5
			if ( $i < count( $parts ) ) {
377 5
378 5
				if ( !is_numeric( $parts[$i] ) ) {
379 5
					throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid datetime specification." );
380 5
				}
381 5
382 5
				$values[$i] = $i == 6 ? floatval( $parts[$i] ) : intval( $parts[$i] );
383 5
			} else {
384 5
				$values[$i] = false;
385 5
			}
386 5
		}
387 5
388 5
		if ( count( $parts ) <= 1 ) {
389 5
			throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
390 5
		}
391
392 5
		return new self( $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6], $values[7] );
393 5
	}
394 5
395 5
	/**
396 2
	 * Create a new time data item from the specified Julian Day number,
397 2
	 * calendar model, presicion, and type ID.
398 2
	 * @param $jdvalue double Julian Day number
399 2
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
400
	 * @param $precision integer one of SMWDITime::PREC_Y, SMWDITime::PREC_YM, SMWDITime::PREC_YMD, SMWDITime::PREC_YMDT
401 2
	 * @return SMWDITime object
402 2
	 */
403 2
	public static function newFromJD( $jdvalue, $calendarmodel, $precision ) {
404
		list( $year, $month, $day ) = self::JD2Date( $jdvalue, $calendarmodel );
405 5
		if ( $precision <= self::PREC_YM ) {
406 5
			$day = false;
407
			if ( $precision == self::PREC_Y ) {
408
				$month = false;
409
			}
410
		}
411
		if ( $precision == self::PREC_YMDT ) {
412
			list( $hour, $minute, $second ) = self::JD2Time( $jdvalue );
413
		} else {
414
			$hour = $minute = $second = false;
415 5
		}
416 5
		return new SMWDITime( $calendarmodel, $year, $month, $day, $hour, $minute, $second );
0 ignored issues
show
Bug introduced by
It seems like $month defined by self::JD2Date($jdvalue, $calendarmodel) on line 404 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...
Bug introduced by
It seems like $day defined by self::JD2Date($jdvalue, $calendarmodel) on line 404 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...
Bug introduced by
It seems like $hour defined by self::JD2Time($jdvalue) on line 412 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...
Bug introduced by
It seems like $minute defined by self::JD2Time($jdvalue) on line 412 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...
Bug introduced by
It seems like $second defined by self::JD2Time($jdvalue) on line 412 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...
417 5
	}
418 5
419 5
	/**
420 5
	 * Compute the Julian Day number from a given date in the specified
421 5
	 * calendar model. This calculation assumes that neither calendar
422 5
	 * has a year 0.
423 5
	 * @param $year integer representing the year
424
	 * @param $month integer representing the month
425
	 * @param $day integer representing the day
426
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
427
	 * @return float Julian Day number
428
	 */
429
	static public function date2JD( $year, $month, $day, $calendarmodel ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
430
		$astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
431
		if ( $calendarmodel == self::CM_GREGORIAN ) {
432
			$a = intval( ( 14 - $month ) / 12 );
433 4
			$y = $astroyear + 4800 - $a;
434 4
			$m = $month + 12 * $a - 3;
435 4
			return $day + floor( ( 153 * $m + 2 ) / 5 ) + 365 * $y + floor( $y / 4 ) - floor( $y / 100 ) + floor( $y / 400 ) - 32045.5;
436 2
		} else {
437
			$y2 = ( $month <= 2 ) ? ( $astroyear - 1 ) : $astroyear;
438 4
			$m2 = ( $month <= 2 ) ? ( $month + 12 ) : $month;
439 4
			return floor( ( 365.25 * ( $y2 + 4716 ) ) ) + floor( ( 30.6001 * ( $m2 + 1 ) ) ) + $day - 1524.5;
440
		}
441
	}
442
443
	/**
444
	 * Compute the offset for the Julian Day number from a given time.
445
	 * This computation is the same for all calendar models.
446
	 * @param $hours integer representing the hour
447
	 * @param $minutes integer representing the minutes
448
	 * @param $seconds integer representing the seconds
449
	 * @return float offset for a Julian Day number to get this time
450
	 */
451
	static public function time2JDoffset( $hours, $minutes, $seconds ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
452 143
		return ( $hours / 24 ) + ( $minutes / ( 60 * 24 ) ) + ( $seconds / ( 3600 * 24 ) );
453 143
	}
454 143
455 4
	/**
456 2
	 * Convert a Julian Day number to a date in the given calendar model.
457
	 * This calculation assumes that neither calendar has a year 0.
458 2
	 * @note The algorithm may fail for some cases, in particular since the
459
	 * conversion to Gregorian needs positive JD. If this happens, wrong
460
	 * values will be returned. Avoid date conversions before 10000 BCE.
461
	 * @param $jdvalue float number of Julian Days
462
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
463
	 * @return array( yearnumber, monthnumber, daynumber )
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
464
	 */
465
	static public function JD2Date( $jdvalue, $calendarmodel ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
466
		if ( $calendarmodel == self::CM_GREGORIAN ) {
467
			$jdvalue += 2921940; // add the days of 8000 years (this algorithm only works for positive JD)
468
			$j = floor( $jdvalue + 0.5 ) + 32044;
469
			$g = floor( $j / 146097 );
470
			$dg = $j % 146097;
471
			$c = floor( ( ( floor( $dg / 36524 ) + 1 ) * 3 ) / 4 );
472
			$dc = $dg - $c * 36524;
473
			$b = floor( $dc / 1461 );
474
			$db = $dc % 1461;
475
			$a = floor( ( ( floor( $db / 365 ) + 1 ) * 3 ) / 4 );
476
			$da = $db - ( $a * 365 );
477
			$y = $g * 400 + $c * 100 + $b * 4 + $a;
478
			$m = floor( ( $da * 5 + 308 ) / 153 ) - 2;
479
			$d = $da - floor( ( ( $m + 4 ) * 153 ) / 5 ) + 122;
480
481
			$year  = $y - 4800 + floor( ( $m + 2 ) / 12 ) - 8000;
482
			$month = ( ( $m + 2 ) % 12 + 1 );
483
			$day   = $d + 1;
484
		} else {
485
			$b = floor( $jdvalue + 0.5 ) + 1524;
486
			$c = floor( ( $b - 122.1 ) / 365.25 );
487
			$d = floor( 365.25 * $c );
488
			$e = floor( ( $b - $d ) / 30.6001 );
489
490
			$month = floor( ( $e < 14 ) ? ( $e - 1 ) : ( $e - 13 ) );
491
			$year = floor( ( $month > 2 ) ? ( $c - 4716 ) : ( $c - 4715 ) );
492
			$day   = ( $b - $d - floor( 30.6001 * $e ) );
493
		}
494
		$year  = ( $year < 1 ) ? ( $year - 1 ) : $year; // correct "year 0" to -1 (= 1 BC(E))
495
		return array( $year, $month, $day );
496
	}
497
498
	/**
499
	 * Extract the time from a Julian Day number and return it as a string.
500
	 * This conversion is the same for all calendar models.
501
	 * @param $jdvalue float number of Julian Days
502
	 * @return array( hours, minutes, seconds )
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
503
	 */
504
	static public function JD2Time( $jdvalue ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
505
		$wjd = $jdvalue + 0.5;
506
		$fraction = $wjd - floor( $wjd );
507
		$time = round( $fraction * 3600 * 24 );
508
		$hours = floor( $time / 3600 );
509
		$time = $time - $hours * 3600;
510
		$minutes = floor( $time / 60 );
511
		$seconds = floor( $time - $minutes * 60 );
512
		return array( $hours, $minutes, $seconds );
513
	}
514
515
	/**
516
	 * Find out whether the given year number is a leap year.
517
	 * This calculation assumes that neither calendar has a year 0.
518
	 * @param $year integer year number
519
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
520
	 * @return boolean
521
	 */
522
	static public function isLeapYear( $year, $calendarmodel ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
523
		$astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
524
		if ( $calendarmodel == self::CM_JULIAN ) {
525
			return ( $astroyear % 4 ) == 0;
526
		} else {
527
			return ( ( $astroyear % 400 ) == 0 ) ||
528
			       ( ( ( $astroyear % 4 ) == 0 ) && ( ( $astroyear % 100 ) != 0 ) );
529
		}
530
	}
531
532
	/**
533
	 * Find out how many days the given month had in the given year
534
	 * based on the specified calendar model.
535
	 * This calculation assumes that neither calendar has a year 0.
536
	 * @param $month integer month number
537
	 * @param $year integer year number
538
	 * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
539
	 * @return boolean
540
	 */
541
	static public function getDayNumberForMonth( $month, $year, $calendarmodel ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
542
		if ( $month !== 2 ) {
543
			return self::$m_daysofmonths[$month];
544
		} elseif ( self::isLeapYear( $year, $calendarmodel ) ) {
545
			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...
546
		} else {
547
			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...
548
		}
549
	}
550
551
	public function equals( SMWDataItem $di ) {
552
		if ( $di->getDIType() !== SMWDataItem::TYPE_TIME ) {
553
			return false;
554
		}
555
556
		return $di->getSortKey() === $this->getSortKey();
557
	}
558
}
559