Completed
Pull Request — master (#148)
by
unknown
03:51 queued 02:25
created

YearMonthTimeParser::stringParse()   C

Complexity

Conditions 14
Paths 68

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 56
ccs 36
cts 36
cp 1
rs 6.2666
c 0
b 0
f 0
cc 14
nc 68
nop 1
crap 14

How to fix   Long Method    Complexity   

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:

1
<?php
2
3
namespace ValueParsers;
4
5
use DataValues\TimeValue;
6
7
/**
8
 * A parser that accepts various date formats with month precision. Prefers month/year order when
9
 * both numbers are valid months, e.g. "12/10" is December 2010. Should be called before
10
 * YearTimeParser when you want to accept both formats, because strings like "1 999" may either
11
 * represent a month and a year or a year with digit grouping.
12
 *
13
 * @since 0.8.4
14
 *
15
 * @license GPL-2.0+
16
 * @author Addshore
17
 * @author Thiemo Kreuz
18
 */
19
class YearMonthTimeParser extends StringValueParser {
20
21
	const FORMAT_NAME = 'year-month';
22
23
	/**
24
	 * @var int[] Array mapping localized month names to month numbers (1 to 12).
25
	 */
26
	private $monthNumbers;
27
28
	/**
29
	 * @var ValueParser
30
	 */
31
	private $isoTimestampParser;
32
33
	/**
34
	 * @var EraParser
35
	 */
36
	private $eraParser;
37
38
	/**
39
	 * @see StringValueParser::__construct
40
	 *
41
	 * @param MonthNameProvider $monthNameProvider
42
	 * @param ParserOptions|null $options
43
	 * @param EraParser|null $eraParser
44
	 */
45 78
	public function __construct(
46
		MonthNameProvider $monthNameProvider,
47
		ParserOptions $options = null,
48
		EraParser $eraParser = null
49
	) {
50 78
		parent::__construct( $options );
51
52 78
		$languageCode = $this->getOption( ValueParser::OPT_LANG );
53 78
		$this->monthNumbers = $monthNameProvider->getMonthNumbers( $languageCode );
54 78
		$this->isoTimestampParser = new IsoTimestampParser( null, $this->options );
55 78
		$this->eraParser = $eraParser ?: new EraParser();
56 78
	}
57
58
	/**
59
	 * @see StringValueParser::stringParse
60
	 *
61
	 * @param string $value
62
	 *
63
	 * @throws ParseException
64
	 * @return TimeValue
65
	 */
66 71
	protected function stringParse( $value ) {
67 71
		$trimmedValue = trim( $value );
68 71
		switch ( $trimmedValue[0] ) {
69 71
			case '+':
70 71
			case '-':
71
				// don't let EraParser strip it, we will handle it ourselves
72 9
				$newValue = $trimmedValue;
73 9
				$eraWasSpecified = false;
74 9
				$sign = '';
75 9
				break;
76
			default:
77 62
				list( $sign, $newValue ) = $this->eraParser->parse( $trimmedValue );
78 62
				if ( $newValue !== $trimmedValue ) {
79 9
					$eraWasSpecified = true;
80
				} else {
81 53
					$eraWasSpecified = false;
82 53
					$sign = '';
83
				}
84 62
				break;
85
		}
86
87
		// Matches year and month separated by a separator.
88
		// \p{L} matches letters outside the ASCII range.
89 71
		$regex = '/^(-?[\d\p{L}]+)\s*?[\/\-\s.,]\s*(-?[\d\p{L}]+)$/u';
90 71
		if ( !preg_match( $regex, $newValue, $matches ) ) {
91 11
			throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
92
		}
93 60
		list( , $a, $b ) = $matches;
94
95
		// if era was specified, fail on a minus sign
96 60
		$intRegex = $eraWasSpecified ? '/^\d+$/' : '/^-?\d+$/';
97 60
		$aIsInt = preg_match( $intRegex, $a );
98 60
		$bIsInt = preg_match( $intRegex, $b );
99
100 60
		if ( $aIsInt && $bIsInt ) {
101 30
			if ( $this->canBeMonth( $a ) ) {
102 21
				return $this->getTimeFromYearMonth( $sign . $b, $a );
103 9
			} elseif ( $this->canBeMonth( $b ) ) {
104 9
				return $this->getTimeFromYearMonth( $sign . $a, $b );
105
			}
106 30
		} elseif ( $aIsInt ) {
107 5
			$month = $this->parseMonth( $b );
108
109 5
			if ( $month ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
110 5
				return $this->getTimeFromYearMonth( $sign . $a, $month );
111
			}
112 25
		} elseif ( $bIsInt ) {
113 22
			$month = $this->parseMonth( $a );
114
115 22
			if ( $month ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
116 20
				return $this->getTimeFromYearMonth( $sign . $b, $month );
117
			}
118
		}
119
120 10
		throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
121
	}
122
123
	/**
124
	 * @param string $month
125
	 *
126
	 * @return int|null
127
	 */
128 27
	private function parseMonth( $month ) {
129 27
		foreach ( $this->monthNumbers as $monthName => $i ) {
130 27
			if ( strcasecmp( $monthName, $month ) === 0 ) {
131 25
				return $i;
132
			}
133
		}
134
135 2
		return null;
136
	}
137
138
	/**
139
	 * @param string $year
140
	 * @param string $month as a canonical month number
141
	 *
142
	 * @return TimeValue
143
	 */
144 50
	private function getTimeFromYearMonth( $year, $month ) {
145 50
		if ( $year[0] !== '-' && $year[0] !== '+' ) {
146 40
			$year = '+' . $year;
147
		}
148
149 50
		return $this->isoTimestampParser->parse( sprintf( '%s-%02s-00T00:00:00Z', $year, $month ) );
150
	}
151
152
	/**
153
	 * @param string $value
154
	 *
155
	 * @return bool can the given value be a month?
156
	 */
157 30
	private function canBeMonth( $value ) {
158 30
		return $value >= 0 && $value <= 12;
159
	}
160
161
}
162