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 |
||
29 | class SMWDITime extends SMWDataItem { |
||
|
|||
30 | |||
31 | const CM_GREGORIAN = 1; |
||
32 | const CM_JULIAN = 2; |
||
33 | |||
34 | const PREC_Y = 0; |
||
35 | const PREC_YM = 1; |
||
36 | const PREC_YMD = 2; |
||
37 | const PREC_YMDT = 3; |
||
38 | |||
39 | /** |
||
40 | * The year before which we do not accept anything but year numbers and |
||
41 | * largely discourage calendar models. |
||
42 | */ |
||
43 | const PREHISTORY = -10000; |
||
44 | |||
45 | /** |
||
46 | * Maximal number of days in a given month. |
||
47 | * @var array |
||
48 | */ |
||
49 | 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 ); |
||
50 | |||
51 | /** |
||
52 | * Precision SMWDITime::PREC_Y, SMWDITime::PREC_YM, |
||
53 | * SMWDITime::PREC_YMD, or SMWDITime::PREC_YMDT. |
||
54 | * @var integer |
||
55 | */ |
||
56 | protected $m_precision; |
||
57 | /** |
||
58 | * Calendar model: SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN. |
||
59 | * @var integer |
||
60 | */ |
||
61 | protected $m_model; |
||
62 | /** |
||
63 | * Number of year, possibly negative. |
||
64 | * @var integer |
||
65 | */ |
||
66 | protected $m_year; |
||
67 | /** |
||
68 | * Number of month. |
||
69 | * @var integer |
||
70 | */ |
||
71 | protected $m_month; |
||
72 | /** |
||
73 | * Number of day. |
||
74 | * @var integer |
||
75 | */ |
||
76 | protected $m_day; |
||
77 | /** |
||
78 | * Hours of the day. |
||
79 | * @var integer |
||
80 | */ |
||
81 | protected $m_hours; |
||
82 | /** |
||
83 | * Minutes of the hour. |
||
84 | * @var integer |
||
85 | */ |
||
86 | protected $m_minutes; |
||
87 | /** |
||
88 | * Seconds of the minute. |
||
89 | * @var integer |
||
90 | */ |
||
91 | protected $m_seconds; |
||
92 | |||
93 | /** |
||
94 | * @var integer |
||
95 | */ |
||
96 | protected $timezone; |
||
97 | |||
98 | /** |
||
99 | * @var integer|null |
||
100 | */ |
||
101 | protected $era = null; |
||
102 | |||
103 | /** |
||
104 | * @var integer |
||
105 | */ |
||
106 | protected $julianDay = null; |
||
107 | |||
108 | /** |
||
109 | * Create a time data item. All time components other than the year can |
||
110 | 144 | * be false to indicate that they are not specified. This will affect |
|
111 | * the internal precision setting. The missing values are initialised |
||
112 | * to minimal values (0 or 1) for internal calculations. |
||
113 | 144 | * |
|
114 | * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
||
115 | * @param $year integer number of the year (possibly negative) |
||
116 | * @param $month mixed integer number or false |
||
117 | 144 | * @param $day mixed integer number or false |
|
118 | * @param $hour mixed integer number or false |
||
119 | * @param $minute mixed integer number or false |
||
120 | * @param $second mixed integer number or false |
||
121 | 144 | * @param integer|false $timezone |
|
122 | 144 | * |
|
123 | 144 | * @todo Implement more validation here. |
|
124 | 144 | */ |
|
125 | 144 | public function __construct( $calendarmodel, $year, $month = false, $day = false, |
|
126 | 144 | $hour = false, $minute = false, $second = false, $timezone = false ) { |
|
127 | 144 | ||
128 | if ( ( $calendarmodel != self::CM_GREGORIAN ) && ( $calendarmodel != self::CM_JULIAN ) ) { |
||
129 | 144 | throw new DataItemException( "Unsupported calendar model constant \"$calendarmodel\"." ); |
|
130 | } |
||
131 | 144 | ||
132 | 144 | if ( $year == 0 ) { |
|
133 | 144 | throw new DataItemException( "There is no year 0 in Gregorian and Julian calendars." ); |
|
134 | 144 | } |
|
135 | |||
136 | $this->m_model = $calendarmodel; |
||
137 | $this->m_year = intval( $year ); |
||
138 | 144 | $this->m_month = $month != false ? intval( $month ) : 1; |
|
139 | $this->m_day = $day != false ? intval( $day ) : 1; |
||
140 | $this->m_hours = $hour !== false ? intval( $hour ) : 0; |
||
141 | $this->m_minutes = $minute !== false ? intval( $minute ) : 0; |
||
142 | 144 | $this->m_seconds = $second !== false ? floatval( $second ) : 0; |
|
143 | 16 | ||
144 | 144 | $this->timezone = $timezone !== false ? intval( $timezone ) : 0; |
|
145 | 2 | $year = strval( $year ); |
|
146 | 139 | $this->era = $year{0} === '+' ? 1 : ( $year{0} === '-' ? -1 : 0 ); |
|
147 | 21 | ||
148 | 21 | ||
149 | 138 | if ( ( $this->m_hours < 0 ) || ( $this->m_hours > 23 ) || |
|
150 | ( $this->m_minutes < 0 ) || ( $this->m_minutes > 59 ) || |
||
151 | 144 | ( $this->m_seconds < 0 ) || ( $this->m_seconds > 59 ) || |
|
152 | ( $this->m_month < 1 ) || ( $this->m_month > 12 ) ) { |
||
153 | 146 | throw new DataItemException( "Part of the date is out of bounds." ); |
|
154 | 146 | } |
|
155 | |||
156 | if ( $this->m_day > self::getDayNumberForMonth( $this->m_month, $this->m_year, $this->m_model ) ) { |
||
157 | 20 | throw new DataItemException( "Month {$this->m_month} in year {$this->m_year} did not have {$this->m_day} days in this calendar model." ); |
|
158 | 20 | } |
|
159 | |||
160 | if ( $month === false ) { |
||
161 | 43 | $this->m_precision = self::PREC_Y; |
|
162 | 43 | } elseif ( $day === false ) { |
|
163 | $this->m_precision = self::PREC_YM; |
||
164 | } elseif ( $hour === false ) { |
||
165 | 43 | $this->m_precision = self::PREC_YMD; |
|
166 | 43 | } else { |
|
167 | $this->m_precision = self::PREC_YMDT; |
||
168 | } |
||
169 | 43 | } |
|
170 | 43 | ||
171 | /** |
||
172 | * @since 1.6 |
||
173 | 43 | * |
|
174 | 43 | * @return integer |
|
175 | */ |
||
176 | public function getDIType() { |
||
179 | |||
180 | /** |
||
181 | 41 | * @since 1.6 |
|
182 | 41 | * |
|
183 | * @return integer |
||
184 | */ |
||
185 | 41 | public function getCalendarModel() { |
|
188 | |||
189 | /** |
||
190 | * @since 1.6 |
||
191 | * |
||
192 | * @return integer |
||
193 | */ |
||
194 | 1 | public function getPrecision() { |
|
195 | return $this->m_precision; |
||
196 | } |
||
197 | 1 | ||
198 | 1 | /** |
|
199 | 1 | * Indicates whether a user explicitly used an era marker even for a positive |
|
200 | * year. |
||
201 | 1 | * |
|
202 | * - [-1] indicates BC(E) |
||
203 | * - [0]/null indicates no era marker |
||
204 | * - [1] indicates AD/CE was used |
||
205 | * |
||
206 | * @since 2.4 |
||
207 | * |
||
208 | * @return integer |
||
209 | */ |
||
210 | public function getEra() { |
||
211 | return $this->era; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * @since 1.6 |
||
216 | * |
||
217 | * @return integer |
||
218 | */ |
||
219 | public function getYear() { |
||
220 | return $this->m_year; |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * @since 1.6 |
||
225 | * |
||
226 | * @return integer |
||
227 | */ |
||
228 | public function getMonth() { |
||
229 | return $this->m_month; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | 1 | * @since 1.6 |
|
234 | * |
||
235 | 1 | * @return integer |
|
236 | */ |
||
237 | public function getDay() { |
||
238 | return $this->m_day; |
||
239 | 1 | } |
|
240 | |||
241 | /** |
||
242 | * @since 1.6 |
||
243 | * |
||
244 | * @return integer |
||
245 | 1 | */ |
|
246 | public function getHour() { |
||
249 | 1 | ||
250 | 1 | /** |
|
251 | 1 | * @since 1.6 |
|
252 | 1 | * |
|
253 | * @return integer |
||
254 | 1 | */ |
|
255 | public function getMinute() { |
||
256 | return $this->m_minutes; |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * @since 1.6 |
||
261 | * |
||
262 | * @return integer |
||
263 | */ |
||
264 | public function getSecond() { |
||
265 | return $this->m_seconds; |
||
266 | 136 | } |
|
267 | 136 | ||
268 | /** |
||
269 | 136 | * @since 2.4 |
|
270 | * |
||
271 | * @return string |
||
272 | */ |
||
273 | 136 | public function getCalendarModelLiteral() { |
|
274 | 136 | ||
275 | 136 | $literal = array( |
|
276 | 136 | self::CM_GREGORIAN => '', |
|
277 | 136 | self::CM_JULIAN => 'JL' |
|
278 | 136 | ); |
|
279 | 136 | ||
280 | 136 | return $literal[$this->m_model]; |
|
281 | 136 | } |
|
282 | |||
283 | /** |
||
284 | * @since 2.4 |
||
285 | * |
||
286 | * @param DateTime $dateTime |
||
287 | * |
||
288 | * @return SMWDITime|false |
||
289 | */ |
||
290 | public static function newFromDateTime( DateTime $dateTime ) { |
||
291 | |||
292 | $calendarModel = self::CM_JULIAN; |
||
293 | 135 | ||
294 | 135 | $year = $dateTime->format( 'Y' ); |
|
295 | 135 | $month = $dateTime->format( 'm' ); |
|
296 | 135 | $day = $dateTime->format( 'd' ); |
|
297 | 135 | ||
298 | 135 | if ( ( $year > 1582 ) || |
|
299 | 135 | ( ( $year == 1582 ) && ( $month > 10 ) ) || |
|
300 | 135 | ( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) { |
|
301 | 135 | $calendarModel = self::CM_GREGORIAN; |
|
302 | 135 | } |
|
303 | 135 | ||
304 | 135 | return self::doUnserialize( $calendarModel . '/' . $dateTime->format( 'Y/m/d/H/i/s.u' ) ); |
|
305 | } |
||
306 | |||
307 | /** |
||
308 | * @since 2.4 |
||
309 | * |
||
310 | * @return DateTime |
||
311 | */ |
||
312 | public function asDateTime() { |
||
313 | |||
314 | $year = str_pad( $this->m_year , 4, '0', STR_PAD_LEFT ); |
||
315 | |||
316 | // Avoid "Failed to parse time string (-900-02-02 00:00:00) at |
||
317 | 41 | // position 7 (-): Double timezone specification" |
|
318 | 41 | if ( $this->m_year < 0 ) { |
|
319 | 41 | $year = '-' . str_pad( $this->m_year * -1 , 4, '0', STR_PAD_LEFT ); |
|
320 | } |
||
321 | 3 | ||
322 | // Avoid "Failed to parse time string (1300-11-02 12:03:25.888499949) at |
||
323 | // at position 11 (1): The timezone could not ..." |
||
324 | $seconds = number_format( str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), 7, '.', '' ); |
||
325 | |||
326 | $time = $year . '-' . |
||
327 | str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ) . '-' . |
||
328 | str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ) . ' ' . |
||
329 | str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ) . ':' . |
||
330 | str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ) . ':' . |
||
331 | $seconds; |
||
332 | |||
333 | return new DateTime( $time ); |
||
334 | 142 | } |
|
335 | 142 | ||
336 | 142 | /** |
|
337 | 142 | * Creates and returns a new instance of SMWDITime from a MW timestamp. |
|
338 | * |
||
339 | 3 | * @since 1.8 |
|
340 | * |
||
341 | * @param string $timestamp must be in format |
||
342 | * |
||
343 | 145 | * @return SMWDITime|false |
|
344 | 145 | */ |
|
345 | 145 | public static function newFromTimestamp( $timestamp ) { |
|
346 | $timestamp = wfTimestamp( TS_MW, (string)$timestamp ); |
||
347 | |||
348 | 144 | if ( $timestamp === false ) { |
|
349 | 144 | return false; |
|
350 | } |
||
351 | 144 | ||
352 | 139 | return new self( |
|
353 | 139 | self::CM_GREGORIAN, |
|
354 | substr( $timestamp, 0, 4 ), |
||
355 | 144 | substr( $timestamp, 4, 2 ), |
|
356 | 139 | substr( $timestamp, 6, 2 ), |
|
357 | 139 | substr( $timestamp, 8, 2 ), |
|
358 | substr( $timestamp, 10, 2 ), |
||
359 | 144 | substr( $timestamp, 12, 2 ) |
|
360 | 138 | ); |
|
361 | 138 | } |
|
362 | |||
363 | 144 | /** |
|
364 | * Returns a MW timestamp representation of the value. |
||
365 | * |
||
366 | * @since 1.6.2 |
||
367 | * |
||
368 | * @param $outputtype |
||
369 | * |
||
370 | * @return mixed |
||
371 | 16 | */ |
|
372 | 16 | public function getMwTimestamp( $outputtype = TS_UNIX ) { |
|
373 | 16 | return wfTimestamp( |
|
374 | $outputtype, |
||
375 | 16 | implode( '', array( |
|
376 | 16 | str_pad( $this->m_year, 4, '0', STR_PAD_LEFT ), |
|
377 | str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ), |
||
378 | 16 | str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ), |
|
379 | str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ), |
||
380 | str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ), |
||
381 | str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), |
||
382 | 16 | ) ) |
|
383 | 16 | ); |
|
384 | 5 | } |
|
385 | |||
386 | 16 | /** |
|
387 | * Get the data in the specified calendar model. This might require |
||
388 | 16 | * conversion. |
|
389 | * @note Conversion can be unreliable for very large absolute year |
||
390 | * numbers when the internal calculations hit floating point accuracy. |
||
391 | * Callers might want to avoid this (calendar models make little sense |
||
392 | 16 | * in such cases anyway). |
|
393 | * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
||
394 | * @return SMWDITime |
||
395 | */ |
||
396 | public function getForCalendarModel( $calendarmodel ) { |
||
397 | if ( $calendarmodel == $this->m_model ) { |
||
398 | return $this; |
||
399 | } else { |
||
400 | return self::newFromJD( $this->getJD(), $calendarmodel, $this->m_precision ); |
||
401 | } |
||
402 | } |
||
403 | 6 | ||
404 | 6 | /** |
|
405 | 6 | * Return a number that helps comparing time data items. For |
|
406 | * dates in the Julian Day era (roughly from 4713 BCE onwards), we use |
||
407 | * the Julian Day number. For earlier dates, the (negative) year number |
||
408 | * with a fraction for the date is used (times are ignored). This |
||
409 | * avoids calculation errors that would occur for very ancient dates |
||
410 | * if the JD number was used there. |
||
411 | 6 | * @return double sortkey |
|
412 | 6 | */ |
|
413 | 6 | public function getSortKey() { |
|
414 | 2 | $jd = ( $this->m_year >= -4713 ) ? $jd = $this->getJD() : -1; |
|
415 | if ( $jd > 0 ) { |
||
416 | 6 | return $jd; |
|
417 | } else { |
||
418 | return $this->m_year - 1 + ( $this->m_month - 1 ) / 12 + ( $this->m_day - 1 ) / 12 / 31; |
||
419 | } |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * @since 1.6 |
||
424 | * |
||
425 | * @return double |
||
426 | */ |
||
427 | public function getJD() { |
||
428 | |||
429 | 145 | if ( $this->julianDay === null ) { |
|
430 | 145 | $this->julianDay = JulianDay::get( $this ); |
|
431 | 145 | } |
|
432 | 145 | ||
433 | 145 | return $this->julianDay; |
|
434 | 145 | } |
|
435 | 145 | ||
436 | /** |
||
437 | 6 | * @since 1.6 |
|
438 | 6 | * |
|
439 | 6 | * @return string |
|
440 | */ |
||
441 | public function getSerialization() { |
||
442 | $result = strval( $this->m_model ) . '/' . ( $this->era > 0 ? '+' : '' ) . strval( $this->m_year ); |
||
443 | |||
444 | if ( $this->m_precision >= self::PREC_YM ) { |
||
445 | $result .= '/' . strval( $this->m_month ); |
||
446 | } |
||
447 | |||
448 | if ( $this->m_precision >= self::PREC_YMD ) { |
||
449 | $result .= '/' . strval( $this->m_day ); |
||
450 | } |
||
451 | 145 | ||
452 | 145 | if ( $this->m_precision >= self::PREC_YMDT ) { |
|
453 | $result .= '/' . strval( $this->m_hours ) . '/' . strval( $this->m_minutes ) . '/' . strval( $this->m_seconds ) . '/' . strval( $this->timezone ); |
||
454 | } |
||
455 | |||
456 | return $result; |
||
457 | } |
||
458 | |||
459 | /** |
||
460 | * Create a data item from the provided serialization string. |
||
461 | * |
||
462 | * @return SMWDITime |
||
463 | */ |
||
464 | public static function doUnserialize( $serialization ) { |
||
465 | 6 | $parts = explode( '/', $serialization, 8 ); |
|
466 | 6 | $values = array(); |
|
467 | 6 | ||
468 | 6 | for ( $i = 0; $i < 8; $i += 1 ) { |
|
469 | 6 | if ( $i < count( $parts ) ) { |
|
470 | 6 | ||
471 | 6 | if ( !is_numeric( $parts[$i] ) ) { |
|
472 | 6 | throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid datetime specification." ); |
|
473 | 6 | } |
|
474 | 6 | ||
475 | 6 | $values[$i] = $i == 6 ? floatval( $parts[$i] ) : intval( $parts[$i] ); |
|
476 | 6 | ||
477 | 6 | // Find out whether the input contained an explicit AD/CE era marker |
|
478 | 6 | if ( $i == 1 ) { |
|
479 | 6 | $values[$i] = ( $parts[1]{0} === '+' ? '+' : '' ) . $values[$i]; |
|
480 | } |
||
481 | 6 | } else { |
|
482 | 6 | $values[$i] = false; |
|
483 | 6 | } |
|
484 | 6 | } |
|
485 | 2 | ||
486 | 2 | if ( count( $parts ) <= 1 ) { |
|
487 | 2 | throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." ); |
|
488 | 2 | } |
|
489 | |||
490 | 2 | return new self( $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6], $values[7] ); |
|
491 | 2 | } |
|
492 | 2 | ||
493 | /** |
||
494 | 6 | * Create a new time data item from the specified Julian Day number, |
|
495 | 6 | * calendar model, presicion, and type ID. |
|
496 | * |
||
497 | * @param $jdvalue double Julian Day number |
||
498 | * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
||
499 | * @param $precision integer one of SMWDITime::PREC_Y, SMWDITime::PREC_YM, SMWDITime::PREC_YMD, SMWDITime::PREC_YMDT |
||
500 | * |
||
501 | * @return SMWDITime object |
||
502 | */ |
||
503 | public static function newFromJD( $jdvalue, $calendarmodel, $precision ) { |
||
506 | 6 | ||
507 | 6 | /** |
|
508 | 6 | * Find out whether the given year number is a leap year. |
|
509 | 6 | * This calculation assumes that neither calendar has a year 0. |
|
510 | 6 | * @param $year integer year number |
|
511 | 6 | * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
|
512 | 6 | * @return boolean |
|
513 | */ |
||
514 | static public function isLeapYear( $year, $calendarmodel ) { |
||
515 | $astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year; |
||
516 | if ( $calendarmodel == self::CM_JULIAN ) { |
||
517 | return ( $astroyear % 4 ) == 0; |
||
518 | } else { |
||
519 | return ( ( $astroyear % 400 ) == 0 ) || |
||
520 | ( ( ( $astroyear % 4 ) == 0 ) && ( ( $astroyear % 100 ) != 0 ) ); |
||
521 | } |
||
522 | 137 | } |
|
523 | 137 | ||
524 | 137 | /** |
|
525 | 3 | * Find out how many days the given month had in the given year |
|
526 | * based on the specified calendar model. |
||
527 | 137 | * This calculation assumes that neither calendar has a year 0. |
|
528 | 137 | * @param $month integer month number |
|
529 | * @param $year integer year number |
||
530 | * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN |
||
531 | * @return boolean |
||
532 | */ |
||
533 | static public function getDayNumberForMonth( $month, $year, $calendarmodel ) { |
||
542 | 144 | ||
543 | 34 | public function equals( SMWDataItem $di ) { |
|
550 | } |
||
551 |
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.