Completed
Push — master ( 708613...9f9161 )
by f
01:19
created

NounDeclension::declinateSecondDeclension()   C

Complexity

Conditions 12
Paths 30

Size

Total Lines 44
Code Lines 22

Duplication

Lines 3
Ratio 6.82 %

Importance

Changes 0
Metric Value
cc 12
eloc 22
nc 30
nop 2
dl 3
loc 44
rs 5.1612
c 0
b 0
f 0

How to fix   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
namespace morphos\Russian;
3
4
use morphos\Gender;
5
use morphos\S;
6
7
/**
8
 * Rules are from http://morpher.ru/Russian/Noun.aspx
9
 */
10
class NounDeclension extends \morphos\BaseInflection implements Cases, Gender
11
{
12
    use RussianLanguage, CasesHelper;
13
14
    const FIRST_DECLENSION = 1;
15
    const SECOND_DECLENSION = 2;
16
    const THIRD_DECLENSION = 3;
17
18
    /**
19
     * These words has 2 declension type.
20
     */
21
    protected static $abnormalExceptions = array(
22
        'бремя',
23
        'вымя',
24
        'темя',
25
        'пламя',
26
        'стремя',
27
        'пламя',
28
        'время',
29
        'знамя',
30
        'имя',
31
        'племя',
32
        'семя',
33
        'путь' => array('путь', 'пути', 'пути', 'путь', 'путем', 'о пути'),
34
        'дитя' => array('дитя', 'дитяти', 'дитяти', 'дитя', 'дитятей', 'о дитяти')
35
    );
36
37
    protected static $masculineWithSoft = array(
38
        'олень',
39
        'конь',
40
        'ячмень',
41
        'путь',
42
        'зверь',
43
        'шкворень',
44
        'пельмень',
45
        'тюлень',
46
        'выхухоль',
47
        'табель',
48
        'рояль',
49
        'шампунь',
50
        'конь',
51
        'лось',
52
        'гвоздь',
53
        'медведь',
54
        'рубль',
55
        'дождь',
56
    );
57
58
    protected static $masculineWithSoftAndRunAwayVowels = array(
59
        'день',
60
        'пень',
61
        'парень',
62
        'камень',
63
        'корень',
64
        'трутень',
65
    );
66
67
    protected static $immutableWords = array(
68
        'евро',
69
        'пенни',
70
    );
71
72
    public static function isMutable($word, $animateness = false)
0 ignored issues
show
Unused Code introduced by
The parameter $animateness is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
73
    {
74
        $word = S::lower($word);
75 View Code Duplication
        if (in_array(S::slice($word, -1), array('у', 'и', 'е', 'о', 'ю')) || in_array($word, self::$immutableWords)) {
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...
76
            return false;
77
        }
78
        return true;
79
    }
80
81
    public static function detectGender($word)
82
    {
83
    	$word = S::lower($word);
84
    	$last = S::slice($word, -1);
85
		// пытаемся угадать род объекта, хотя бы примерно, чтобы правильно склонять
86
		if (S::slice($word, -2) == 'мя' || in_array($last, ['о', 'е', 'и', 'у']))
87
			return self::NEUTER;
88
89
		if (in_array($last, ['а', 'я']) ||
90
			($last == 'ь' && !in_array($word, self::$masculineWithSoft) && !in_array($word, self::$masculineWithSoftAndRunAwayVowels)))
91
			return self::FEMALE;
92
93
		return self::MALE;
94
    }
95
96
    public static function getDeclension($word)
97
    {
98
        $word = S::lower($word);
99
        $last = S::slice($word, -1);
100
        if (isset(self::$abnormalExceptions[$word]) || in_array($word, self::$abnormalExceptions)) {
101
            return 2;
102
        }
103
104
        if (in_array($last, ['а', 'я']) && S::slice($word, -2) != 'мя') {
105
            return 1;
106
        } elseif (self::isConsonant($last) || in_array($last, ['о', 'е', 'ё']) || ($last == 'ь' && self::isConsonant(S::slice($word, -2, -1)) && !self::isHissingConsonant(S::slice($word, -2, -1)) && (in_array($word, self::$masculineWithSoft)) || in_array($word, self::$masculineWithSoftAndRunAwayVowels))) {
107
            return 2;
108
        } else {
109
            return 3;
110
        }
111
    }
112
113
    public static function getCases($word, $animateness = false)
114
    {
115
        $word = S::lower($word);
116
117
        // Адъективное склонение (Сущ, образованные от прилагательных и причастий) - прохожий, существительное
118 View Code Duplication
        if (in_array(S::slice($word, -2), array('ой', 'ий', 'ый', 'ая', 'ое', 'ее')) && $word != 'гений') {
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...
119
            return self::declinateAdjective($word, $animateness);
120
        }
121
122
        // Субстантивное склонение (существительные)
123
        if (in_array($word, self::$immutableWords)) {
124
            return array(
125
                self::IMENIT => $word,
126
                self::RODIT => $word,
127
                self::DAT => $word,
128
                self::VINIT => $word,
129
                self::TVORIT => $word,
130
                self::PREDLOJ => self::choosePrepositionByFirstLetter($word, 'об', 'о').' '.$word,
131
            );
132
        } elseif (isset(self::$abnormalExceptions[$word])) {
133
            return array_combine(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT, self::PREDLOJ), self::$abnormalExceptions[$word]);
134
        } elseif (in_array($word, self::$abnormalExceptions)) {
135
            $prefix = S::slice($word, 0, -1);
136
            return array(
137
                self::IMENIT => $word,
138
                self::RODIT => $prefix.'ени',
139
                self::DAT => $prefix.'ени',
140
                self::VINIT => $word,
141
                self::TVORIT => $prefix.'енем',
142
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'ени',
143
            );
144
        }
145
146
        switch (self::getDeclension($word)) {
147
            case self::FIRST_DECLENSION:
148
                return self::declinateFirstDeclension($word);
149
            case self::SECOND_DECLENSION:
150
                return self::declinateSecondDeclension($word, $animateness);
151
            case self::THIRD_DECLENSION:
152
                return self::declinateThirdDeclension($word);
153
        }
154
    }
155
156
    public static function declinateFirstDeclension($word)
157
    {
158
        $word = S::lower($word);
159
        $prefix = S::slice($word, 0, -1);
160
        $last = S::slice($word, -1);
161
        $soft_last = self::checkLastConsonantSoftness($word);
162
        $forms =  array(
163
            Cases::IMENIT => $word,
164
        );
165
166
        // RODIT
167
        $forms[Cases::RODIT] = self::chooseVowelAfterConsonant($last, $soft_last || (in_array(S::slice($word, -2, -1), array('г', 'к', 'х'))), $prefix.'и', $prefix.'ы');
168
169
        // DAT
170
        $forms[Cases::DAT] = self::getPredCaseOf12Declensions($word, $last, $prefix);
171
172
        // VINIT
173
        $forms[Cases::VINIT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ю', $prefix.'у');
174
175
        // TVORIT
176
        if ($last == 'ь') {
177
            $forms[Cases::TVORIT] = $prefix.'ой';
178 View Code Duplication
        } else {
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...
179
            $forms[Cases::TVORIT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'ей', $prefix.'ой');
180
        }
181
182
        // 	if ($last == 'й' || (self::isConsonant($last) && !self::isHissingConsonant($last)) || self::checkLastConsonantSoftness($word))
183
        // 	$forms[Cases::TVORIT] = $prefix.'ей';
184
        // else
185
        // 	$forms[Cases::TVORIT] = $prefix.'ой'; # http://morpher.ru/Russian/Spelling.aspx#sibilant
186
187
        // PREDLOJ the same as DAT
188
        $forms[Cases::PREDLOJ] = self::choosePrepositionByFirstLetter($forms[Cases::DAT], 'об', 'о').' '.$forms[Cases::DAT];
189
        return $forms;
190
    }
191
192
    public static function declinateSecondDeclension($word, $animateness = false)
193
    {
194
        $word = S::lower($word);
195
        $last = S::slice($word, -1);
196
        $soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && ((self::isConsonant(S::slice($word, -2, -1)) && !self::isHissingConsonant(S::slice($word, -2, -1))) || S::slice($word, -2, -1) == 'и'));
197
        $prefix = self::getPrefixOfSecondDeclension($word, $last);
198
        $forms =  array(
199
            Cases::IMENIT => $word,
200
        );
201
202
        // RODIT
203
        $forms[Cases::RODIT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а');
204
205
        // DAT
206
        $forms[Cases::DAT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'ю', $prefix.'у');
207
208
        // VINIT
209
        if (in_array($last, ['о', 'е', 'ё'])) {
210
            $forms[Cases::VINIT] = $word;
211
        } else {
212
            $forms[Cases::VINIT] = self::getVinitCaseByAnimateness($forms, $animateness);
213
        }
214
215
        // TVORIT
216
        // if ($last == 'ь')
217
        // 	$forms[Cases::TVORIT] = $prefix.'ом';
218
        // else if ($last == 'й' || (self::isConsonant($last) && !self::isHissingConsonant($last)))
219
        // 	$forms[Cases::TVORIT] = $prefix.'ем';
220
        // else
221
        // 	$forms[Cases::TVORIT] = $prefix.'ом'; # http://morpher.ru/Russian/Spelling.aspx#sibilant
222
        if (self::isHissingConsonant($last) || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && self::isHissingConsonant(S::slice($word, -2, -1))) || $last == 'ц') {
223
            $forms[Cases::TVORIT] = $prefix.'ем';
224 View Code Duplication
        } elseif (in_array($last, ['й'/*, 'ч', 'щ'*/]) || $soft_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...
225
            $forms[Cases::TVORIT] = $prefix.'ем';
226
        } else {
227
            $forms[Cases::TVORIT] = $prefix.'ом';
228
        }
229
230
        // PREDLOJ
231
        $forms[Cases::PREDLOJ] = self::getPredCaseOf12Declensions($word, $last, $prefix);
232
        $forms[Cases::PREDLOJ] = self::choosePrepositionByFirstLetter($forms[Cases::PREDLOJ], 'об', 'о').' '.$forms[Cases::PREDLOJ];
233
234
        return $forms;
235
    }
236
237
    public static function declinateThirdDeclension($word)
238
    {
239
        $word = S::lower($word);
240
        $prefix = S::slice($word, 0, -1);
241
        return array(
242
            Cases::IMENIT => $word,
243
            Cases::RODIT => $prefix.'и',
244
            Cases::DAT => $prefix.'и',
245
            Cases::VINIT => $word,
246
            Cases::TVORIT => $prefix.'ью',
247
            Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
248
        );
249
    }
250
251
    /**
252
     * Rules are from http://rusgram.narod.ru/1216-1231.html
253
     */
254
    public static function declinateAdjective($word, $animateness)
0 ignored issues
show
Unused Code introduced by
The parameter $animateness is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
255
    {
256
        $prefix = S::slice($word, 0, -2);
257
258
        switch (S::slice($word, -2)) {
259
            // Male adjectives
260
            case 'ой':
261 View Code Duplication
            case 'ый':
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...
262
                return array(
263
                    Cases::IMENIT => $word,
264
                    Cases::RODIT => $prefix.'ого',
265
                    Cases::DAT => $prefix.'ому',
266
                    Cases::VINIT => $word,
267
                    Cases::TVORIT => $prefix.'ым',
268
                    Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'ом',
269
                );
270
271 View Code Duplication
            case 'ий':
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...
272
                return array(
273
                    Cases::IMENIT => $word,
274
                    Cases::RODIT => $prefix.'его',
275
                    Cases::DAT => $prefix.'ему',
276
                    Cases::VINIT => $prefix.'его',
277
                    Cases::TVORIT => $prefix.'им',
278
                    Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'ем',
279
                );
280
281
            // Neuter adjectives
282
            case 'ое':
283
            case 'ее':
284
                $prefix = S::slice($word, 0, -1);
285
                return array(
286
                    Cases::IMENIT => $word,
287
                    Cases::RODIT => $prefix.'го',
288
                    Cases::DAT => $prefix.'му',
289
                    Cases::VINIT => $word,
290
                    Cases::TVORIT => S::slice($word, 0, -2).(S::slice($word, -2, -1) == 'о' ? 'ы' : 'и').'м',
291
                    Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'м',
292
                );
293
294
            // Female adjectives
295
            case 'ая':
296
                $ending = self::isHissingConsonant(S::slice($prefix, -1)) ? 'ей' : 'ой';
297
                return array(
298
                    Cases::IMENIT => $word,
299
                    Cases::RODIT => $prefix.$ending,
300
                    Cases::DAT => $prefix.$ending,
301
                    Cases::VINIT => $prefix.'ую',
302
                    Cases::TVORIT => $prefix.$ending,
303
                    Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.$ending,
304
                );
305
        }
306
    }
307
308
    public static function getCase($word, $case, $animateness = false)
309
    {
310
        $case = self::canonizeCase($case);
311
        $forms = self::getCases($word, $animateness);
312
        return $forms[$case];
313
    }
314
315
    public static function getPrefixOfSecondDeclension($word, $last)
316
    {
317
        // слова с бегающей гласной в корне
318
        if (in_array($word, self::$masculineWithSoftAndRunAwayVowels)) {
319
            $prefix = S::slice($word, 0, -3).S::slice($word, -2, -1);
320
        } elseif (in_array($last, ['о', 'е', 'ё', 'ь', 'й'])) {
321
            $prefix = S::slice($word, 0, -1);
322
        }
323
        // уменьшительные формы слов (котенок) и слова с суффиксом ок
324
        elseif (S::slice($word, -2) == 'ок' && S::length($word) > 3) {
325
            $prefix = S::slice($word, 0, -2).'к';
326
        } else {
327
            $prefix = $word;
328
        }
329
        return $prefix;
330
    }
331
332
    public static function getVinitCaseByAnimateness(array $forms, $animate)
333
    {
334
        if ($animate) {
335
            return $forms[Cases::RODIT];
336
        } else {
337
            return $forms[Cases::IMENIT];
338
        }
339
    }
340
341
    public static function getPredCaseOf12Declensions($word, $last, $prefix)
342
    {
343
        if (in_array(S::slice($word, -2), array('ий', 'ие'))) {
344
            if ($last == 'ё') {
345
                return $prefix.'е';
346
            } else {
347
                return $prefix.'и';
348
            }
349
        } else {
350
            return $prefix.'е';
351
        }
352
    }
353
}
354