Completed
Push — master ( b5bb9d...38f7c0 )
by f
02:02
created

src/Russian/FirstNamesInflection.php (18 issues)

call_checks.maybe_mismatching_type_passed_with_def

Bug Minor

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://www.imena.org/decl_mn.html / http://www.imena.org/decl_fn.html
8
 * and http://rus.omgpu.ru/2016/04/18/%D1%81%D0%BA%D0%BB%D0%BE%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BB%D0%B8%D1%87%D0%BD%D1%8B%D1%85-%D0%B8%D0%BC%D1%91%D0%BD/
9
 */
10
class FirstNamesInflection extends \morphos\NamesInflection implements Cases
11
{
12
    use RussianLanguage, CasesHelper;
13
14
    protected static $exceptions = [
15
        'лев' => [
16
            self::IMENIT => 'Лев',
17
            self::RODIT => 'Льва',
18
            self::DAT => 'Льву',
19
            self::VINIT => 'Льва',
20
            self::TVORIT => 'Львом',
21
            self::PREDLOJ => 'Льве',
22
        ],
23
        'павел' => [
24
            self::IMENIT => 'Павел',
25
            self::RODIT => 'Павла',
26
            self::DAT => 'Павлу',
27
            self::VINIT => 'Павла',
28
            self::TVORIT => 'Павлом',
29
            self::PREDLOJ => 'Павле',
30
        ]
31
    ];
32
33
    protected static $menNames = [
34
        'абрам', 'аверьян', 'авраам', 'агафон', 'адам', 'азар', 'акакий', 'аким', 'аксён', 'александр', 'алексей',
35
        'альберт', 'анатолий', 'андрей', 'андрон', 'антип', 'антон', 'аполлон', 'аристарх', 'аркадий', 'арнольд',
36
        'арсений', 'арсентий', 'артем', 'артём', 'артемий', 'артур', 'аскольд', 'афанасий', 'богдан', 'борис',
37
        'борислав', 'бронислав', 'вадим', 'валентин', 'валерий', 'варлам', 'василий', 'венедикт', 'вениамин',
38
        'веньямин', 'венцеслав', 'виктор', 'виген', 'вилен', 'виталий', 'владилен', 'владимир', 'владислав', 'владлен',
39
        'вова', 'всеволод', 'всеслав', 'вячеслав', 'гавриил', 'геннадий', 'георгий', 'герман', 'глеб', 'григорий',
40
        'давид', 'даниил', 'данил', 'данила', 'демьян', 'денис', 'димитрий', 'дмитрий', 'добрыня', 'евгений', 'евдоким',
41
        'евсей', 'егор', 'емельян', 'еремей', 'ермолай', 'ерофей', 'ефим', 'захар', 'иван', 'игнат', 'игорь',
42
        'илларион', 'иларион', 'илья', 'иосиф', 'казимир', 'касьян', 'кирилл', 'кондрат', 'константин', 'кузьма',
43
        'лавр', 'лаврентий', 'лазарь', 'ларион', 'лев', 'леонард', 'леонид', 'лука', 'максим', 'марат', 'мартын',
44
        'матвей', 'мефодий', 'мирон', 'михаил', 'моисей', 'назар', 'никита', 'николай', 'олег', 'осип', 'остап',
45
        'павел', 'панкрат', 'пантелей', 'парамон', 'пётр', 'петр', 'платон', 'потап', 'прохор', 'роберт', 'ростислав',
46
        'савва', 'савелий', 'семён', 'семен', 'сергей', 'сидор', 'спартак', 'тарас', 'терентий', 'тимофей', 'тимур',
47
        'тихон', 'ульян', 'фёдор', 'федор', 'федот', 'феликс', 'фирс', 'фома', 'харитон', 'харлам', 'эдуард',
48
        'эммануил', 'эраст', 'юлиан', 'юлий', 'юрий', 'яков', 'ян', 'ярослав',
49
    ];
50
51
    protected static $womenNames = [
52
        'авдотья', 'аврора', 'агата', 'агния', 'агриппина', 'ада', 'аксинья', 'алевтина', 'александра', 'алёна',
53
        'алена', 'алина', 'алиса', 'алла', 'альбина', 'амалия', 'анастасия', 'ангелина', 'анжела', 'анжелика', 'анна',
54
        'антонина', 'анфиса', 'арина', 'белла', 'божена', 'валентина', 'валерия', 'ванда', 'варвара', 'василина',
55
        'василиса', 'вера', 'вероника', 'виктория', 'виола', 'виолетта', 'вита', 'виталия', 'владислава', 'власта',
56
        'галина', 'глафира', 'дарья', 'диана', 'дина', 'ева', 'евгения', 'евдокия', 'евлампия', 'екатерина', 'елена',
57
        'елизавета', 'ефросиния', 'ефросинья', 'жанна', 'зиновия', 'злата', 'зоя', 'ивонна', 'изольда', 'илона', 'инга',
58
        'инесса', 'инна', 'ирина', 'ия', 'капитолина', 'карина', 'каролина', 'кира', 'клавдия', 'клара', 'клеопатра',
59
        'кристина', 'ксения', 'лада', 'лариса', 'лиана', 'лидия', 'лилия', 'лина', 'лия', 'лора', 'любава', 'любовь',
60
        'людмила', 'майя', 'маргарита', 'марианна', 'мариетта', 'марина', 'мария', 'марья', 'марта', 'марфа', 'марьяна',
61
        'матрёна', 'матрена', 'матрона', 'милена', 'милослава', 'мирослава', 'муза', 'надежда', 'настасия', 'настасья',
62
        'наталия', 'наталья', 'нелли', 'ника', 'нина', 'нинель', 'нонна', 'оксана', 'олимпиада', 'ольга', 'пелагея',
63
        'полина', 'прасковья', 'раиса', 'рената', 'римма', 'роза', 'роксана', 'руфь', 'сарра', 'светлана', 'серафима',
64
        'снежана', 'софья', 'софия', 'стелла', 'степанида', 'стефания', 'таисия', 'таисья', 'тамара', 'татьяна',
65
        'ульяна', 'устиния', 'устинья', 'фаина', 'фёкла', 'фекла', 'феодора', 'хаврония', 'христина', 'эвелина',
66
        'эдита', 'элеонора', 'элла', 'эльвира', 'эмилия', 'эмма', 'юдифь', 'юлиана', 'юлия', 'ядвига', 'яна',
67
        'ярослава',
68
    ];
69
70
    protected static $immutableNames = [
71
        'николя',
72
    ];
73
74
    /**
75
     * Checks if name is mutable
76
     * @param string $name
77
     * @param null|string $gender
78
     * @return bool
79
     */
80 716
    public static function isMutable($name, $gender = null)
81
    {
82 716
        $name = S::lower($name);
83
84 716
        if (in_array($name, self::$immutableNames, true)) {
85
            return false;
86
        }
87
88 716
        if ($gender === null) {
89
            $gender = self::detectGender($name);
90
        }
91
92
        // man rules
93 716
        if ($gender === self::MALE) {
94
            // soft consonant
95 451
            if (S::lower(S::slice($name, -1)) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
96 14
                return true;
97 437
            } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, ['й', /*'Ч', 'Щ'*/]), true)) { // hard consonant
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
98 259
                return true;
99 178
            } elseif (S::slice($name, -1) == 'й') {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
100 104
                return true;
101 74 View Code Duplication
            } else if (in_array(S::slice($name, -2), ['ло', 'ко'], true)) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
102 74
                return true;
103
            }
104 265
        } else if ($gender === self::FEMALE) {
105
            // soft consonant
106 265
            if (S::lower(S::slice($name, -1)) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
107 4
                return true;
108 261
            } else if (self::isHissingConsonant(S::slice($name, -1))) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
109 5
                return true;
110
            }
111
        }
112
113
        // common rules
114 326
        if ((in_array(S::slice($name, -1), ['а', 'я']) && !self::isVowel(S::slice($name, -2, -1))) || in_array(S::slice($name, -2), ['ия', 'ья', 'ея', 'оя'], true)) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 82 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
115 275
            return true;
116
        }
117
118 51
        return false;
119
    }
120
121
    /**
122
     * @param $name
123
     * @return string
124
     */
125 510
    public static function detectGender($name)
126
    {
127 510
        $name = S::lower($name);
128 510
        if (in_array($name, self::$menNames, true)) {
129 121
            return self::MALE;
130 390
        } elseif (in_array($name, self::$womenNames, true)) {
131 111
            return self::FEMALE;
132
        }
133
134 279
        $man = $woman = 0;
135 279
        $last1 = S::slice($name, -1);
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 127 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
136 279
        $last2 = S::slice($name, -2);
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 127 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
137 279
        $last3 = S::slice($name, -3);
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 127 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
138
139
        // try to detect gender by some statistical rules
140
        //
141 279
        if ($last1 == 'й') {
142 48
            $man += 0.9;
143
        }
144 279
        if ($last1 == 'ь') {
145
            $man += 0.02;
146
        }
147 279
        if (in_array($last1, self::$consonants, true)) {
148 199
            $man += 0.01;
149
        }
150 279
        if (in_array($last2, ['он', 'ов', 'ав', 'ам', 'ол', 'ан', 'рд', 'мп'], true)) {
151 47
            $man += 0.3;
152
        }
153 279
        if (in_array($last2, ['вь', 'фь', 'ль'], true)) {
154
            $woman += 0.1;
155
        }
156 279
        if (in_array($last2, ['ла'], true)) {
157 4
            $woman += 0.04;
158
        }
159 279
        if (in_array($last2, ['то', 'ма'], true)) {
160
            $man += 0.01;
161
        }
162 279 View Code Duplication
        if (in_array($last3, ['лья', 'вва', 'ока', 'ука', 'ита'], true)) {
163 2
            $man += 0.2;
164
        }
165 279
        if (in_array($last3, ['има'], true)) {
166
            $woman += 0.15;
167
        }
168 279 View Code Duplication
        if (in_array($last3, ['лия', 'ния', 'сия', 'дра', 'лла', 'кла', 'опа'], true)) {
169 3
            $woman += 0.5;
170
        }
171 279
        if (in_array(S::slice($name, -4), ['льда', 'фира', 'нина', 'лита', 'алья'], true)) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 127 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
172
            $woman += 0.5;
173
        }
174
175 279
        return $man === $woman ? null
176 279
            : ($man > $woman ? self::MALE : self::FEMALE);
177
    }
178
179
    /**
180
     * @param string $name
181
     * @param null|string $gender
182
     * @return array
183
     */
184 140
    public static function getCases($name, $gender = null)
185
    {
186 140
        $name = S::lower($name);
187
188 140
        if (self::isMutable($name, $gender)) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\Russian\FirstNamesInflection::isMutable() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
189
            // common rules for ия and я
190 140
            if (S::slice($name, -2) == 'ия') {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
191 20
                $prefix = S::name(S::slice($name, 0, -1));
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
192
                return [
193 20
                    self::IMENIT => $prefix.'я',
194 20
                    self::RODIT => $prefix.'и',
195 20
                    self::DAT => $prefix.'и',
196 20
                    self::VINIT => $prefix.'ю',
197 20
                    self::TVORIT => $prefix.'ей',
198 20
                    self::PREDLOJ => $prefix.'и',
199
                ];
200 120 View Code Duplication
            } elseif (S::slice($name, -1) == 'я') {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
201 10
                $prefix = S::name(S::slice($name, 0, -1));
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\S::slice() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
202
                return [
203 10
                    self::IMENIT => $prefix.'я',
204 10
                    self::RODIT => $prefix.'и',
205 10
                    self::DAT => $prefix.'е',
206 10
                    self::VINIT => $prefix.'ю',
207 10
                    self::TVORIT => $prefix.'ей',
208 10
                    self::PREDLOJ => $prefix.'е',
209
                ];
210
            }
211
212 110
            if (!in_array($name, self::$immutableNames, true)) {
213 110
                if ($gender === null) {
214
                    $gender = self::detectGender($name);
215
                }
216 110
                if ($gender === self::MALE || $name === 'саша') {
217 84
                    if (($result = self::getCasesMan($name)) !== null) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\Russian\FirstNam...flection::getCasesMan() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
218 84
                        return $result;
219
                    }
220 26
                } elseif ($gender === self::FEMALE) {
221 26
                    if (($result = self::getCasesWoman($name)) !== null) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 186 can also be of type boolean; however, morphos\Russian\FirstNam...ection::getCasesWoman() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
222 26
                        return $result;
223
                    }
224
                }
225
            }
226
        }
227
228
        $name = S::name($name);
229
        return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT, self::PREDLOJ), $name);
230
    }
231
232
    /**
233
     * @param string $name
234
     * @return array|null
235
     */
236 84
    protected static function getCasesMan($name)
237
    {
238
        // special cases for Лев, Павел
239 84
        if (isset(self::$exceptions[$name])) {
240
            return self::$exceptions[$name];
241 84
        } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, ['й', /*'Ч', 'Щ'*/]), true)) { // hard consonant
242 24
			if (in_array(S::slice($name, -2), ['ек', 'ёк'], true)) { // Витек, Санек
243
                // case for foreign names like Салмонбек
244 6
                if (self::isConsonant(S::slice($name, -4, -3)))
245 2
                    $prefix = S::name(S::slice($name, 0, -2)).'ек';
246
                else
247 6
				    $prefix = S::name(S::slice($name, 0, -2)).'ьк';
248
			} else {
249 18
                if ($name === 'пётр')
250
                    $prefix = S::name(str_replace('ё', 'е', $name));
251
                else
252 18
				    $prefix = S::name($name);
253
            }
254
            return [
255 24
                self::IMENIT => S::name($name),
256 24
                self::RODIT => $prefix.'а',
257 24
                self::DAT => $prefix.'у',
258 24
                self::VINIT => $prefix.'а',
259 24
                self::TVORIT => RussianLanguage::isHissingConsonant(S::slice($name, -1)) || S::slice($name, -1) == 'ц' ? $prefix.'ем' : $prefix.'ом',
260 24
                self::PREDLOJ => $prefix.'е',
261
            ];
262 60 View Code Duplication
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) { // soft consonant
263 10
            $prefix = S::name(S::slice($name, 0, -1));
264
            return [
265 10
                self::IMENIT => $prefix.'ь',
266 10
                self::RODIT => $prefix.'я',
267 10
                self::DAT => $prefix.'ю',
268 10
                self::VINIT => $prefix.'я',
269 10
                self::TVORIT => $prefix.'ем',
270 10
                self::PREDLOJ => $prefix.'е',
271
            ];
272 50
        } elseif (in_array(S::slice($name, -2), ['ай', 'ей', 'ой', 'уй', 'яй', 'юй', 'ий'], true)) {
273 24
            $prefix = S::name(S::slice($name, 0, -1));
274 24
            $postfix = S::slice($name, -2) == 'ий' ? 'и' : 'е';
275
            return [
276 24
                self::IMENIT => $prefix.'й',
277 24
                self::RODIT => $prefix.'я',
278 24
                self::DAT => $prefix.'ю',
279 24
                self::VINIT => $prefix.'я',
280 24
                self::TVORIT => $prefix.'ем',
281 24
                self::PREDLOJ => $prefix.$postfix,
282
            ];
283 26
        } elseif (S::slice($name, -1) == 'а' && self::isConsonant($before = S::slice($name, -2, -1)) && !in_array($before, [/*'г', 'к', 'х', */'ц'], true)) {
284 22
            $prefix = S::name(S::slice($name, 0, -1));
285 22
            $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, ['г', 'к', 'х'], true)) ? 'и' : 'ы';
286
            return [
287 22
                self::IMENIT => $prefix.'а',
288 22
                self::RODIT => $prefix.$postfix,
289 22
                self::DAT => $prefix.'е',
290 22
                self::VINIT => $prefix.'у',
291 22
                self::TVORIT => $prefix.($before === 'ш' ? 'е' : 'о').'й',
292 22
                self::PREDLOJ => $prefix.'е',
293
            ];
294 4
        } elseif (S::slice($name, -2) == 'ло' || S::slice($name, -2) == 'ко') {
295 4
            $prefix = S::name(S::slice($name, 0, -1));
296 4
            $postfix = S::slice($name, -2, -1) == 'к' ? 'и' : 'ы';
297
            return [
298 4
                self::IMENIT => $prefix.'о',
299 4
                self::RODIT =>  $prefix.$postfix,
300 4
                self::DAT => $prefix.'е',
301 4
                self::VINIT => $prefix.'у',
302 4
                self::TVORIT => $prefix.'ой',
303 4
                self::PREDLOJ => $prefix.'е',
304
            ];
305
        }
306
307
        return null;
308
    }
309
310
    /**
311
     * @param string $name
312
     * @return array|null
313
     */
314 26
    protected static function getCasesWoman($name)
315
    {
316 26
        if (S::slice($name, -1) == 'а' && !self::isVowel($before = (S::slice($name, -2, -1)))) {
317 17
            $prefix = S::name(S::slice($name, 0, -1));
318 17
            if ($before != 'ц') {
319 14
                $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, ['г', 'к', 'х'], true)) ? 'и' : 'ы';
320
                return [
321 14
                    self::IMENIT => $prefix.'а',
322 14
                    self::RODIT => $prefix.$postfix,
323 14
                    self::DAT => $prefix.'е',
324 14
                    self::VINIT => $prefix.'у',
325 14
                    self::TVORIT => $prefix.'ой',
326 14
                    self::PREDLOJ => $prefix.'е',
327
                ];
328 View Code Duplication
            } else {
329
                return [
330 3
                    self::IMENIT => $prefix.'а',
331 3
                    self::RODIT => $prefix.'ы',
332 3
                    self::DAT => $prefix.'е',
333 3
                    self::VINIT => $prefix.'у',
334 3
                    self::TVORIT => $prefix.'ей',
335 3
                    self::PREDLOJ => $prefix.'е',
336
                ];
337
            }
338 9 View Code Duplication
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
339 4
            $prefix = S::name(S::slice($name, 0, -1));
340
            return [
341 4
                self::IMENIT => $prefix.'ь',
342 4
                self::RODIT => $prefix.'и',
343 4
                self::DAT => $prefix.'и',
344 4
                self::VINIT => $prefix.'ь',
345 4
                self::TVORIT => $prefix.'ью',
346 4
                self::PREDLOJ => $prefix.'и',
347
            ];
348 5
        } elseif (RussianLanguage::isHissingConsonant(S::slice($name, -1))) {
349 5
            $prefix = S::name($name);
350
            return [
351 5
                self::IMENIT => $prefix,
352 5
                self::RODIT => $prefix.'и',
353 5
                self::DAT => $prefix.'и',
354 5
                self::VINIT => $prefix,
355 5
                self::TVORIT => $prefix.'ью',
356 5
                self::PREDLOJ => $prefix.'и',
357
            ];
358
        }
359
        return null;
360
    }
361
362
    /**
363
     * @param string $name
364
     * @param string $case
365
     * @param null|string $gender
366
     * @return string
367
     * @throws \Exception
368
     */
369 52
    public static function getCase($name, $case, $gender = null)
370
    {
371 52
        $case = self::canonizeCase($case);
372 52
        $forms = self::getCases($name, $gender);
373 52
        return $forms[$case];
374
    }
375
}
376