Completed
Push — 2.1.x ( 7b2a9a...8f7c87 )
by f
01:29
created

GeneralDeclension::getForms()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 18
nc 5
nop 2
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
1
<?php
2
namespace morphos\Russian;
3
4
/**
5
 * Rules are from http://morpher.ru/Russian/Noun.aspx
6
 */
7
class GeneralDeclension extends \morphos\GeneralDeclension implements Cases {
8
	use RussianLanguage;
9
10
	const FIRST_DECLENSION = 1;
11
	const SECOND_DECLENSION = 2;
12
	const THIRD_DECLENSION = 3;
13
14
	const FIRST_SCHOOL_DECLENSION = 2;
15
	const SECOND_SCHOOL_DECLENSION = 1;
16
	const THIRD_SCHOOL_DECLENSION = 3;
17
18
	protected $exceptions = array(
19
		'бремя',
20
		'вымя',
21
		'темя',
22
		'пламя',
23
		'стремя',
24
		'пламя',
25
		'время',
26
		'знамя',
27
		'имя',
28
		'племя',
29
		'семя',
30
	);
31
32
	public function hasForms($word, $animate = false) {
33
		$word = lower($word);
34
		if (in_array(slice($word, -1), array('у', 'и', 'е', 'о', 'ю')))
35
			return false;
36
		return true;
37
	}
38
39
	public function getDeclension($word) {
40
		$word = lower($word);
41
		$last = slice($word, -1);
42
		if ($this->isConsonant($last) || in_array($last, ['о', 'е', 'ё']) || ($last == 'ь' && $this->isConsonant(slice($word, -2, -1)) && !$this->isHissingConsonant(slice($word, -2, -1)))) {
43
			return  1;
44
		} else if (in_array($last, ['а', 'я']) && slice($word, -2) != 'мя') {
45
			return 2;
46
		} else {
47
			return 3;
48
		}
49
	}
50
51
	public function getForms($word, $animate = false) {
52
		$word = lower($word);
53
54
		if (isset($this->exceptions[$word])) {
55
			$prefix = slice($word, -1);
56
			return array(
57
				self::IMENIT => $word,
58
				self::RODIT => $prefix.'и',
59
				self::DAT => $prefix.'и',
60
				self::VINIT => $word,
61
				self::TVORIT => $prefix,
62
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
63
			);
64
		}
65
66
		switch ($this->getDeclension($word)) {
67
			case self::FIRST_DECLENSION:
68
				return $this->declinateFirstDeclension($word, $animate);
69
			case self::SECOND_DECLENSION:
70
				return $this->declinateSecondDeclension($word);
71
			case self::THIRD_DECLENSION:
72
				return $this->declinateThirdDeclension($word);
73
		}
74
	}
75
76
	public function declinateFirstDeclension($word, $animate = false) {
77
		$word = lower($word);
78
		$last = slice($word, -1);
79
		$soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && $this->isConsonant(slice($word, -2, -1)));
80
		$prefix = $this->getPrefixOfFirstDeclension($word, $last);
81
		$forms =  array(
82
			Cases::IMENIT_1 => $word,
83
		);
84
85
		// RODIT_2
86
		$forms[Cases::RODIT_2] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а');
87
88
		// DAT_3
89
		$forms[Cases::DAT_3] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ю', $prefix.'у');
90
91
		// VINIT_4
92
		if (in_array($last, ['о', 'е', 'ё']))
93
			$forms[Cases::VINIT_4] = $word;
94
		else {
95
			$forms[Cases::VINIT_4] = $this->getVinitCaseByAnimateness($forms, $animate);
96
		}
97
98
		// TVORIT_5
99
		// if ($last == 'ь')
100
		// 	$forms[Cases::TVORIT_5] = $prefix.'ом';
101
		// else if ($last == 'й' || ($this->isConsonant($last) && !$this->isHissingConsonant($last)))
102
		// 	$forms[Cases::TVORIT_5] = $prefix.'ем';
103
		// else
104
		// 	$forms[Cases::TVORIT_5] = $prefix.'ом'; # http://morpher.ru/Russian/Spelling.aspx#sibilant
105
		if ($this->isHissingConsonant($last) || $last == 'ц') {
106
			$forms[Cases::TVORIT_5] = $prefix.'ем';
107
		} else if (in_array($last, ['й'/*, 'ч', 'щ'*/]) || $soft_last) {
108
			$forms[Cases::TVORIT_5] = $prefix.'ем';
109
		} else {
110
			$forms[Cases::TVORIT_5] = $prefix.'ом';
111
		}
112
113
		// PREDLOJ_6
114
		$forms[Cases::PREDLOJ_6] = $this->getPredCaseOf12Declensions($word, $last, $prefix);
115
		$forms[Cases::PREDLOJ_6] = $this->choosePrepositionByFirstLetter($forms[Cases::PREDLOJ_6], 'об', 'о').' '.$forms[Cases::PREDLOJ_6];
116
117
		return $forms;
118
	}
119
120
	public function declinateSecondDeclension($word) {
121
		$word = lower($word);
122
		$prefix = slice($word, 0, -1);
123
		$last = slice($word, -1);
124
		$soft_last = $this->checkLastConsonantSoftness($word);
125
		$forms =  array(
126
			Cases::IMENIT_1 => $word,
127
		);
128
129
		// RODIT_2
130
		$forms[Cases::RODIT_2] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'и', $prefix.'ы');
131
132
		// DAT_3
133
		$forms[Cases::DAT_3] = $this->getPredCaseOf12Declensions($word, $last, $prefix);
134
135
		// VINIT_4
136
		$forms[Cases::VINIT_4] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ю', $prefix.'у');
137
138
		// TVORIT_5
139
		if ($last == 'ь')
140
			$forms[Cases::TVORIT_5] = $prefix.'ой';
141
		else {
142
			$forms[Cases::TVORIT_5] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ей', $prefix.'ой');
143
		}
144
145
		// 	if ($last == 'й' || ($this->isConsonant($last) && !$this->isHissingConsonant($last)) || $this->checkLastConsonantSoftness($word))
146
		// 	$forms[Cases::TVORIT_5] = $prefix.'ей';
147
		// else
148
		// 	$forms[Cases::TVORIT_5] = $prefix.'ой'; # http://morpher.ru/Russian/Spelling.aspx#sibilant
149
150
		// PREDLOJ_6 the same as DAT_3
151
		$forms[Cases::PREDLOJ_6] = $this->choosePrepositionByFirstLetter($forms[Cases::DAT_3], 'об', 'о').' '.$forms[Cases::DAT_3];
152
		return $forms;
153
	}
154
155
	public function declinateThirdDeclension($word) {
156
		$word = lower($word);
157
		$prefix = slice($word, 0, -1);
158
		return array(
159
			Cases::IMENIT_1 => $word,
160
			Cases::RODIT_2 => $prefix.'и',
161
			Cases::DAT_3 => $prefix.'и',
162
			Cases::VINIT_4 => $word,
163
			Cases::TVORIT_5 => $prefix.'ью',
164
			Cases::PREDLOJ_6 => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
165
		);
166
	}
167
168
	public function pluralizeAllDeclensions($word, $animate = false) {
169
		$word = lower($word);
170
		$prefix = slice($word, 0, -1);
171
		$last = slice($word, -1);
172
173
		if (($declension = $this->getDeclension($word)) == self::FIRST_DECLENSION) {
174
			$soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && $this->isConsonant(slice($word, -2, -1)));
175
			$prefix = $this->getPrefixOfFirstDeclension($word, $last);
176
		} else if ($declension == self::SECOND_DECLENSION) {
177
			 $soft_last = $this->checkLastConsonantSoftness($word);
178
		} else {
179
			$soft_last = false;
180
		}
181
182
		$forms =  array(
183
			Cases::IMENIT_1 => $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а'),
184
		);
185
186
		// RODIT_2
187 View Code Duplication
		if ($this->isHissingConsonant($last) || ($soft_last && $last != 'й'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
			$forms[Cases::RODIT_2] = $prefix.'ей';
189
		else if ($last == 'й')
190
			$forms[Cases::RODIT_2] = $prefix.'ев';
191
		else // ($this->isConsonant($last) && !$this->isHissingConsonant($last))
192
			$forms[Cases::RODIT_2] = $prefix.'ов';
193
194
		// DAT_3
195
		$forms[Cases::DAT_3] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ям', $prefix.'ам');
196
197
		// VINIT_4
198
		$forms[Cases::VINIT_4] = $this->getVinitCaseByAnimateness($forms, $animate);
199
200
		// TVORIT_5
201
		// my personal rule
202 View Code Duplication
		if ($last == 'ь' && $declension == self::THIRD_DECLENSION) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
			$forms[Cases::TVORIT_5] = $prefix.'ми';
204
		} else {
205
			$forms[Cases::TVORIT_5] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ями', $prefix.'ами');
206
		}
207
208
		// PREDLOJ_6
209
		$forms[Cases::PREDLOJ_6] = $this->chooseVowelAfterConsonant($last, $soft_last, $prefix.'ях', $prefix.'ах');
210
		$forms[Cases::PREDLOJ_6] = $this->choosePrepositionByFirstLetter($forms[Cases::PREDLOJ_6], 'об', 'о').' '.$forms[Cases::PREDLOJ_6];
211
		return $forms;
212
	}
213
214
	public function getForm($word, $animate = false, $form) {
215
		$forms = $this->getForms($word, $animate);
216
		return $forms[$form];
217
	}
218
219
	protected function getPrefixOfFirstDeclension($word, $last) {
220
		if (in_array($last, ['о', 'е', 'ё', 'ь', 'й']))
221
			$prefix = slice($word, 0, -1);
222
		else
223
			$prefix = $word;
224
		return $prefix;
225
	}
226
227
	protected function getVinitCaseByAnimateness(array $forms, $animate) {
228
		if ($animate)
229
			return $forms[Cases::RODIT_2];
230
		else
231
			return $forms[Cases::IMENIT_1];
232
	}
233
234
	protected function getPredCaseOf12Declensions($word, $last, $prefix) {
235
		if (slice($word, -2) == 'ий') {
236
			if ($last == 'ё')
237
				return $prefix.'е';
238
			else
239
				return $prefix.'и';
240
		} else {
241
			return $prefix.'е';
242
		}
243
	}
244
245
	protected function chooseVowelAfterConsonant($last, $soft_last, $after_soft, $after_hard) {
246
		if ($this->isHissingConsonant($last) || $this->isVelarConsonant($last) || $soft_last) {
247
			return $after_soft;
248
		} else {
249
			return $after_hard;
250
		}
251
	}
252
}
253