Test Failed
Pull Request — master (#148)
by
unknown
06:51
created

YearMonthTimeParser::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
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 70
	 * @param MonthNameProvider $monthNameProvider
42
	 * @param ParserOptions|null $options
43
	 * @param EraParser|null $eraParser
44
	 */
45 70
	public function __construct(
46
		MonthNameProvider $monthNameProvider,
47 70
		ParserOptions $options = null,
48 70
		EraParser $eraParser = null
49 70
	) {
50 70
		parent::__construct( $options );
51
52
		$languageCode = $this->getOption( ValueParser::OPT_LANG );
53
		$this->monthNumbers = $monthNameProvider->getMonthNumbers( $languageCode );
54
		$this->isoTimestampParser = new IsoTimestampParser( null, $this->options );
55
		$this->eraParser = $eraParser ?: new EraParser();
56
	}
57
58
	/**
59
	 * @see StringValueParser::stringParse
60 63
	 *
61
	 * @param string $value
62
	 *
63 63
	 * @throws ParseException
64 63
	 * @return TimeValue
65 11
	 */
66
	protected function stringParse( $value ) {
67 52
		$trimmedValue = trim( $value );
68
		list( $sign, $newValue ) = $this->eraParser->parse( $trimmedValue );
69 52
		if ( $newValue !== $trimmedValue ) {
70 52
			$eraWasSpecified = true;
71
		} else {
72 52
			$eraWasSpecified = false;
73 30
			$sign = '';
74 21
		}
75 9
76 9
		// Matches year and month separated by a separator.
77
		// \p{L} matches letters outside the ASCII range.
78 22
		$regex = '/^(-?[\d\p{L}]+)\s*?[\/\-\s.,]\s*(-?[\d\p{L}]+)$/u';
79 3
		if ( !preg_match( $regex, $newValue, $matches ) ) {
80
			throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
81 3
		}
82 3
		list( , $a, $b ) = $matches;
83
84 19
		// if era was specified, fail on a minus sign
85 18
		$intRegex = $eraWasSpecified ? '/^\d+$/' : '/^-?\d+$/'
86
		$aIsInt = preg_match( $intRegex, $a );
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_VARIABLE
Loading history...
87 18
		$bIsInt = preg_match( $intRegex, $b );
88 16
89
		if ( $aIsInt && $bIsInt ) {
90
			if ( $this->canBeMonth( $a ) ) {
91
				return $this->getTimeFromYearMonth( $sign . $b, $a );
92 8
			} elseif ( $this->canBeMonth( $b ) ) {
93
				return $this->getTimeFromYearMonth( $sign . $a, $b );
94
			}
95
		} elseif ( $aIsInt ) {
96
			$month = $this->parseMonth( $b );
97
98
			if ( $month ) {
99
				return $this->getTimeFromYearMonth( $sign . $a, $month );
100 21
			}
101 21
		} elseif ( $bIsInt ) {
102 21
			$month = $this->parseMonth( $a );
103 19
104
			if ( $month ) {
105
				return $this->getTimeFromYearMonth( $sign . $b, $month );
106
			}
107 2
		}
108
109
		throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
110
	}
111
112
	/**
113
	 * @param string $month
114
	 *
115
	 * @return int|null
116 44
	 */
117 44
	private function parseMonth( $month ) {
118 40
		foreach ( $this->monthNumbers as $monthName => $i ) {
119
			if ( strcasecmp( $monthName, $month ) === 0 ) {
120
				return $i;
121 44
			}
122
		}
123
124
		return null;
125
	}
126
127
	/**
128
	 * @param string $year
129 30
	 * @param string $month as a canonical month number
130 30
	 *
131
	 * @return TimeValue
132
	 */
133
	private function getTimeFromYearMonth( $year, $month ) {
134
		if ( $year[0] !== '-' && $year[0] !== '+' ) {
135
			$year = '+' . $year;
136
		}
137
138
		return $this->isoTimestampParser->parse( sprintf( '%s-%02s-00T00:00:00Z', $year, $month ) );
139
	}
140
141
	/**
142
	 * @param string $value
143
	 *
144
	 * @return bool can the given value be a month?
145
	 */
146
	private function canBeMonth( $value ) {
147
		return $value >= 0 && $value <= 12;
148
	}
149
150
}
151