Completed
Push — master ( 6a872b...95bb87 )
by f
01:14
created

src/Russian/NounPluralization.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace morphos\Russian;
3
4
use morphos\S;
5
6
/**
7
 * Rules are from http://morpher.ru/Russian/Noun.aspx
8
 */
9
class NounPluralization extends \morphos\NounPluralization implements Cases
10
{
11
    use RussianLanguage, CasesHelper;
12
13
    const ONE = 1;
14
    const TWO_FOUR = 2;
15
    const FIVE_OTHER = 3;
16
17
    protected static $neuterExceptions = array(
18
        'поле',
19
        'море',
20
    );
21
22
    protected static $genitiveExceptions = array(
23
        'письмо' => 'писем',
24
        'пятно' => 'пятен',
25
        'кресло' => 'кресел',
26
        'коромысло' => 'коромысел',
27
        'ядро' => 'ядер',
28
        'блюдце' => 'блюдец',
29
        'полотенце' => 'полотенец',
30
    );
31
32
    protected static $immutableWords = array(
33
        'евро',
34
        'пенни',
35
    );
36
37
    protected static $runawayVowelsExceptions = array(
38
        'писе*ц',
39
        'песе*ц',
40
        'глото*к',
41
    );
42
43
    protected static $runawayVowelsNormalized = false;
44
45
    protected static function getRunAwayVowelsList()
46
    {
47
        if (self::$runawayVowelsNormalized === false) {
48
            self::$runawayVowelsNormalized = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type boolean of property $runawayVowelsNormalized.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
49
            foreach (self::$runawayVowelsExceptions as $word) {
50
                self::$runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1;
51
            }
52
        }
53
        return self::$runawayVowelsNormalized;
54
    }
55
56
    public static function pluralize($word, $count = 2, $animateness = false)
57
    {
58
        switch (self::getNumeralForm($count)) {
59
            case self::ONE:
60
                return $word;
61
            case self::TWO_FOUR:
62
                return NounDeclension::getCase($word, self::RODIT, $animateness);
63
            case self::FIVE_OTHER:
64
                return NounPluralization::getCase($word, self::RODIT, $animateness);
65
        }
66
    }
67
68
    public static function getNumeralForm($count)
69
    {
70
        if ($count > 100) {
71
            $count %= 100;
72
        }
73
        $ending = $count % 10;
74
        if (($count > 20 && $ending == 1) || $count == 1) {
75
            return self::ONE;
76
        } elseif (($count > 20 && in_array($ending, range(2, 4))) || in_array($count, range(2, 4))) {
77
            return self::TWO_FOUR;
78
        } else {
79
            return self::FIVE_OTHER;
80
        }
81
    }
82
83
    public static function getCase($word, $case, $animateness = false)
84
    {
85
        $case = self::canonizeCase($case);
86
        $forms = self::getCases($word, $animateness);
87
        return $forms[$case];
88
    }
89
90
    public static function getCases($word, $animateness = false)
91
    {
92
        $word = S::lower($word);
93
94
        if (in_array($word, self::$immutableWords)) {
95
            return array(
96
                self::IMENIT => $word,
97
                self::RODIT => $word,
98
                self::DAT => $word,
99
                self::VINIT => $word,
100
                self::TVORIT => $word,
101
                self::PREDLOJ => self::choosePrepositionByFirstLetter($word, 'об', 'о').' '.$word,
102
            );
103
        }
104
105
        // Адъективное склонение (Сущ, образованные от прилагательных и причастий) - прохожий, существительное
106 View Code Duplication
        if (in_array(S::slice($word, -2), array('ой', 'ий', 'ый', 'ая', 'ое', 'ее')) && $word != 'гений') {
0 ignored issues
show
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...
107
            return self::declinateAdjective($word, $animateness);
108
        }
109
110
        // Субстантивное склонение (существительные)
111
        return self::declinateSubstative($word, $animateness);
112
    }
113
114
    protected static function declinateSubstative($word, $animateness)
115
    {
116
        $prefix = S::slice($word, 0, -1);
117
        $last = S::slice($word, -1);
118
119
        $runaway_vowels_list = static::getRunAwayVowelsList();
120
        if (isset($runaway_vowels_list[$word])) {
121
            $vowel_offset = $runaway_vowels_list[$word];
122
            $word = S::slice($word, 0, $vowel_offset) . S::slice($word, $vowel_offset + 1);
123
        }
124
125
        if (($declension = NounDeclension::getDeclension($word)) == NounDeclension::SECOND_DECLENSION) {
126
            $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) == 'и'));
127
            $prefix = NounDeclension::getPrefixOfSecondDeclension($word, $last);
128
        } elseif ($declension == NounDeclension::FIRST_DECLENSION) {
129
            $soft_last = self::checkLastConsonantSoftness($word);
130
        } else {
131
            $soft_last = S::slice($word, -2) == 'сь';
132
        }
133
134
        $forms = array();
135
136
        if ($last == 'ч' || in_array(S::slice($word, -2), array('чь', 'сь')) || (self::isVowel($last) && in_array(S::slice($word, -2, -1), array('ч', 'к')))) { // before ч, чь, сь, ч+vowel, к+vowel
137
            $forms[Cases::IMENIT] = $prefix.'и';
138
        } elseif ($last == 'н' || $last == 'ц') {
139
            $forms[Cases::IMENIT] = $prefix.'ы';
140 View Code Duplication
        } else {
0 ignored issues
show
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...
141
            $forms[Cases::IMENIT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а');
142
        }
143
144
        // RODIT
145
        if (isset(self::$genitiveExceptions[$word])) {
146
            $forms[Cases::RODIT] = self::$genitiveExceptions[$word];
147
        } elseif (in_array($last, array('о', 'е'))) {
148
            // exceptions
149
            if (in_array($word, self::$neuterExceptions)) {
150
                $forms[Cases::RODIT] = $prefix.'ей';
151 View Code Duplication
            } elseif (S::slice($word, -2, -1) == 'и') {
0 ignored issues
show
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...
152
                $forms[Cases::RODIT] = $prefix.'й';
153
            } else {
154
                $forms[Cases::RODIT] = $prefix;
155
            }
156
        } elseif (S::slice($word, -2) == 'ка') { // words ending with -ка: чашка, вилка, ложка, тарелка, копейка, батарейка
157
            if (S::slice($word, -3, -2) == 'л') {
158
                $forms[Cases::RODIT] = S::slice($word, 0, -2).'ок';
159 View Code Duplication
            } elseif (S::slice($word, -3, -2) == 'й') {
0 ignored issues
show
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...
160
                $forms[Cases::RODIT] = S::slice($word, 0, -3).'ек';
161
            } else {
162
                $forms[Cases::RODIT] = S::slice($word, 0, -2).'ек';
163
            }
164
        } elseif (in_array($last, array('а'))) { // обида, ябеда
165
            $forms[Cases::RODIT] = $prefix;
166
        } elseif (in_array($last, array('я'))) { // молния
167
            $forms[Cases::RODIT] = $prefix.'й';
168
        } elseif (RussianLanguage::isHissingConsonant($last) || ($soft_last && $last != 'й') || in_array(S::slice($word, -2), array('чь', 'сь'))) {
169
            $forms[Cases::RODIT] = $prefix.'ей';
170 View Code Duplication
        } elseif ($last == 'й' || S::slice($word, -2) == 'яц') { // месяц
0 ignored issues
show
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...
171
            $forms[Cases::RODIT] = $prefix.'ев';
172
        } else { // (self::isConsonant($last) && !RussianLanguage::isHissingConsonant($last))
173
            $forms[Cases::RODIT] = $prefix.'ов';
174
        }
175
176
        // DAT
177
        $forms[Cases::DAT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ям', $prefix.'ам');
178
179
        // VINIT
180
        $forms[Cases::VINIT] = NounDeclension::getVinitCaseByAnimateness($forms, $animateness);
181
182
        // TVORIT
183
        // my personal rule
184
        if ($last == 'ь' && $declension == NounDeclension::THIRD_DECLENSION && !in_array(S::slice($word, -2), array('чь', 'сь'))) {
185
            $forms[Cases::TVORIT] = $prefix.'ми';
186
        } else {
187
            $forms[Cases::TVORIT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ями', $prefix.'ами');
188
        }
189
190
        // PREDLOJ
191
        $forms[Cases::PREDLOJ] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ях', $prefix.'ах');
192
        $forms[Cases::PREDLOJ] = self::choosePrepositionByFirstLetter($forms[Cases::PREDLOJ], 'об', 'о').' '.$forms[Cases::PREDLOJ];
193
        return $forms;
194
    }
195
196
    /**
197
     * Rules are from http://rusgram.narod.ru/1216-1231.html
198
     */
199
    protected static function declinateAdjective($word, $animateness)
200
    {
201
        $prefix = S::slice($word, 0, -2);
202
        $vowel = self::isHissingConsonant(S::slice($prefix, -1)) ? 'и' : 'ы';
203
        return array(
204
            Cases::IMENIT => $prefix.$vowel.'е',
205
            Cases::RODIT => $prefix.$vowel.'х',
206
            Cases::DAT => $prefix.$vowel.'м',
207
            Cases::VINIT => $prefix.$vowel.($animateness ? 'х' : 'е'),
208
            Cases::TVORIT => $prefix.$vowel.'ми',
209
            Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.$vowel.'х',
210
        );
211
    }
212
}
213