Complex classes like SMWDITime 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 SMWDITime, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class SMWDITime extends SMWDataItem { |
||
|
|||
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; |
|
124 | $this->m_day = $day != false ? intval( $day ) : 1; |
||
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; |
|
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() { |
|
156 | |||
157 | 42 | public function getCalendarModel() { |
|
160 | |||
161 | 40 | public function getPrecision() { |
|
164 | |||
165 | 40 | public function getYear() { |
|
168 | |||
169 | 40 | public function getMonth() { |
|
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() { |
||
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 ), |
||
277 | substr( $timestamp, 6, 2 ), |
||
278 | substr( $timestamp, 8, 2 ), |
||
279 | substr( $timestamp, 10, 2 ), |
||
280 | substr( $timestamp, 12, 2 ) |
||
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 ); |
|
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 ) { |
||
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 ) { |
||
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 ) |
||
464 | */ |
||
465 | static public function JD2Date( $jdvalue, $calendarmodel ) { |
||
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; |
||
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 ) |
||
503 | */ |
||
504 | static public function JD2Time( $jdvalue ) { |
||
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 ) { |
||
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 ) { |
||
550 | |||
551 | public function equals( SMWDataItem $di ) { |
||
558 | } |
||
559 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.