FirstNamesInflection::detectGender()   F
last analyzed

Complexity

Conditions 16
Paths 8194

Size

Total Lines 53

Duplication

Lines 6
Ratio 11.32 %

Code Coverage

Tests 30
CRAP Score 16.4163

Importance

Changes 0
Metric Value
cc 16
nc 8194
nop 1
dl 6
loc 53
ccs 30
cts 34
cp 0.8824
crap 16.4163
rs 1.4
c 0
b 0
f 0

How to fix   Long Method    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\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
    /**
15
     * @var string[][]
16
     * @phpstan-var array<string, array<string, string>>
17
     */
18
    protected static $exceptions = [
19
        'лев' => [
20
            self::IMENIT => 'Лев',
21
            self::RODIT => 'Льва',
22
            self::DAT => 'Льву',
23
            self::VINIT => 'Льва',
24
            self::TVORIT => 'Львом',
25
            self::PREDLOJ => 'Льве',
26
        ],
27
        'павел' => [
28
            self::IMENIT => 'Павел',
29
            self::RODIT => 'Павла',
30
            self::DAT => 'Павлу',
31
            self::VINIT => 'Павла',
32
            self::TVORIT => 'Павлом',
33
            self::PREDLOJ => 'Павле',
34
        ]
35
    ];
36
37
    /** @var string[]  */
38
    protected static $menNames = [
39
        'абрам', 'аверьян', 'авраам', 'агафон', 'адам', 'азар', 'акакий', 'аким', 'аксён', 'александр', 'алексей',
40
        'альберт', 'анатолий', 'андрей', 'андрон', 'антип', 'антон', 'аполлон', 'аристарх', 'аркадий', 'арнольд',
41
        'арсений', 'арсентий', 'артем', 'артём', 'артемий', 'артур', 'аскольд', 'афанасий', 'богдан', 'борис',
42
        'борислав', 'бронислав', 'вадим', 'валентин', 'валерий', 'варлам', 'василий', 'венедикт', 'вениамин',
43
        'веньямин', 'венцеслав', 'виктор', 'виген', 'вилен', 'виталий', 'владилен', 'владимир', 'владислав', 'владлен',
44
        'вова', 'всеволод', 'всеслав', 'вячеслав', 'гавриил', 'геннадий', 'георгий', 'герман', 'глеб', 'григорий',
45
        'давид', 'даниил', 'данил', 'данила', 'демьян', 'денис', 'димитрий', 'дмитрий', 'добрыня', 'евгений', 'евдоким',
46
        'евсей', 'егор', 'емельян', 'еремей', 'ермолай', 'ерофей', 'ефим', 'захар', 'иван', 'игнат', 'игорь',
47
        'илларион', 'иларион', 'илья', 'иосиф', 'казимир', 'касьян', 'кирилл', 'кондрат', 'константин', 'кузьма',
48
        'лавр', 'лаврентий', 'лазарь', 'ларион', 'лев', 'леонард', 'леонид', 'лука', 'максим', 'марат', 'мартын',
49
        'матвей', 'мефодий', 'мирон', 'михаил', 'моисей', 'назар', 'никита', 'николай', 'олег', 'осип', 'остап',
50
        'павел', 'панкрат', 'пантелей', 'парамон', 'пётр', 'петр', 'платон', 'потап', 'прохор', 'роберт', 'ростислав',
51
        'савва', 'савелий', 'семён', 'семен', 'сергей', 'сидор', 'спартак', 'тарас', 'терентий', 'тимофей', 'тимур',
52
        'тихон', 'ульян', 'фёдор', 'федор', 'федот', 'феликс', 'фирс', 'фома', 'харитон', 'харлам', 'эдуард',
53
        'эммануил', 'эраст', 'юлиан', 'юлий', 'юрий', 'яков', 'ян', 'ярослав',
54
    ];
55
56
    /** @var string[]  */
57
    protected static $womenNames = [
58
        'авдотья', 'аврора', 'агата', 'агния', 'агриппина', 'ада', 'аксинья', 'алевтина', 'александра', 'алёна',
59
        'алена', 'алина', 'алиса', 'алла', 'альбина', 'амалия', 'анастасия', 'ангелина', 'анжела', 'анжелика', 'анна',
60
        'антонина', 'анфиса', 'арина', 'белла', 'божена', 'валентина', 'валерия', 'ванда', 'варвара', 'василина',
61
        'василиса', 'вера', 'вероника', 'виктория', 'виола', 'виолетта', 'вита', 'виталия', 'владислава', 'власта',
62
        'галина', 'глафира', 'дарья', 'диана', 'дина', 'ева', 'евгения', 'евдокия', 'евлампия', 'екатерина', 'елена',
63
        'елизавета', 'ефросиния', 'ефросинья', 'жанна', 'зиновия', 'злата', 'зоя', 'ивонна', 'изольда', 'илона', 'инга',
64
        'инесса', 'инна', 'ирина', 'ия', 'капитолина', 'карина', 'каролина', 'кира', 'клавдия', 'клара', 'клеопатра',
65
        'кристина', 'ксения', 'лада', 'лариса', 'лиана', 'лидия', 'лилия', 'лина', 'лия', 'лора', 'любава', 'любовь',
66
        'людмила', 'майя', 'маргарита', 'марианна', 'мариетта', 'марина', 'мария', 'марья', 'марта', 'марфа', 'марьяна',
67
        'матрёна', 'матрена', 'матрона', 'милена', 'милослава', 'мирослава', 'муза', 'надежда', 'настасия', 'настасья',
68
        'наталия', 'наталья', 'нелли', 'ника', 'нина', 'нинель', 'нонна', 'оксана', 'олимпиада', 'ольга', 'пелагея',
69
        'полина', 'прасковья', 'раиса', 'рената', 'римма', 'роза', 'роксана', 'руфь', 'сарра', 'светлана', 'серафима',
70
        'снежана', 'софья', 'софия', 'стелла', 'степанида', 'стефания', 'таисия', 'таисья', 'тамара', 'татьяна',
71
        'ульяна', 'устиния', 'устинья', 'фаина', 'фёкла', 'фекла', 'феодора', 'хаврония', 'христина', 'эвелина',
72
        'эдита', 'элеонора', 'элла', 'эльвира', 'эмилия', 'эмма', 'юдифь', 'юлиана', 'юлия', 'ядвига', 'яна',
73
        'ярослава',
74
    ];
75
76
    /** @var string[]  */
77
    protected static $immutableNames = [
78
        'николя',
79
    ];
80
81
    /**
82
     * Checks if name is mutable
83
     * @param string $name
84
     * @param null|string $gender
85
     * @return bool
86
     */
87 716
    public static function isMutable($name, $gender = null)
88
    {
89 716
        $name = S::lower($name);
90
91 716
        if (in_array($name, static::$immutableNames, true)) {
92
            return false;
93
        }
94
95 716
        if ($gender === null) {
96
            $gender = static::detectGender($name);
97
        }
98
99
        // man rules
100 716
        if ($gender === static::MALE) {
101
            // soft consonant
102 451
            if (S::lower(S::slice($name, -1)) == 'ь' && static::isConsonant(S::slice($name, -2, -1))) {
103 14
                return true;
104 437
            } elseif (in_array(S::slice($name, -1), array_diff(static::$consonants, ['й', /*'Ч', 'Щ'*/]), true)) { // hard consonant
105 259
                return true;
106 178
            } elseif (S::slice($name, -1) == 'й') {
107 104
                return true;
108 74 View Code Duplication
            } else if (in_array(S::slice($name, -2), ['ло', 'ко'], true)) {
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...
109 74
                return true;
110
            }
111 265
        } else if ($gender === static::FEMALE) {
112
            // soft consonant
113 265
            if (S::lower(S::slice($name, -1)) == 'ь' && static::isConsonant(S::slice($name, -2, -1))) {
114 4
                return true;
115 261
            } else if (static::isHissingConsonant(S::slice($name, -1))) {
116 5
                return true;
117
            }
118
        }
119
120
        // common rules
121 326
        if ((in_array(S::slice($name, -1), ['а', 'я']) && !static::isVowel(S::slice($name, -2, -1))) || in_array(S::slice($name, -2), ['ия', 'ья', 'ея', 'оя'], true)) {
122 275
            return true;
123
        }
124
125 51
        return false;
126
    }
127
128
    /**
129
     * @param string $name
130
     * @return string
131
     */
132 510
    public static function detectGender($name)
133
    {
134 510
        $name = S::lower($name);
135 510
        if (in_array($name, static::$menNames, true)) {
136 121
            return static::MALE;
137 390
        } elseif (in_array($name, static::$womenNames, true)) {
138 111
            return static::FEMALE;
139
        }
140
141 279
        $man = $woman = 0;
142 279
        $last1 = S::slice($name, -1);
143 279
        $last2 = S::slice($name, -2);
144 279
        $last3 = S::slice($name, -3);
145
146
        // try to detect gender by some statistical rules
147
        //
148 279
        if ($last1 == 'й') {
149 48
            $man += 0.9;
150
        }
151 279
        if ($last1 == 'ь') {
152
            $man += 0.02;
153
        }
154 279
        if (in_array($last1, static::$consonants, true)) {
155 199
            $man += 0.01;
156
        }
157 279
        if (in_array($last2, ['он', 'ов', 'ав', 'ам', 'ол', 'ан', 'рд', 'мп'], true)) {
158 47
            $man += 0.3;
159
        }
160 279
        if (in_array($last2, ['вь', 'фь', 'ль'], true)) {
161
            $woman += 0.1;
162
        }
163 279
        if (in_array($last2, ['ла'], true)) {
164 4
            $woman += 0.04;
165
        }
166 279
        if (in_array($last2, ['то', 'ма'], true)) {
167
            $man += 0.01;
168
        }
169 279 View Code Duplication
        if (in_array($last3, ['лья', 'вва', 'ока', 'ука', 'ита'], true)) {
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...
170 2
            $man += 0.2;
171
        }
172 279
        if (in_array($last3, ['има'], true)) {
173
            $woman += 0.15;
174
        }
175 279 View Code Duplication
        if (in_array($last3, ['лия', 'ния', 'сия', 'дра', 'лла', 'кла', 'опа'], true)) {
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...
176 3
            $woman += 0.5;
177
        }
178 279
        if (in_array(S::slice($name, -4), ['льда', 'фира', 'нина', 'лита', 'алья', 'аида'], true)) {
179 3
            $woman += 0.5;
180
        }
181
182 279
        return $man === $woman ? null
183 279
            : ($man > $woman ? static::MALE : static::FEMALE);
184
    }
185
186
    /**
187
     * @param string $name
188
     * @param null|string $gender
189
     * @return string[]
190
     * @phpstan-return array<string, string>
191
     */
192 140
    public static function getCases($name, $gender = null)
193
    {
194 140
        $name = S::lower($name);
195
196 140
        if (static::isMutable($name, $gender)) {
197
            // common rules for ия and я
198 140
            if (S::slice($name, -2) == 'ия') {
199 20
                $prefix = S::name(S::slice($name, 0, -1));
200
                return [
201 20
                    static::IMENIT => $prefix.'я',
202 20
                    static::RODIT => $prefix.'и',
203 20
                    static::DAT => $prefix.'и',
204 20
                    static::VINIT => $prefix.'ю',
205 20
                    static::TVORIT => $prefix.'ей',
206 20
                    static::PREDLOJ => $prefix.'и',
207
                ];
208 120 View Code Duplication
            } elseif (S::slice($name, -1) == 'я') {
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...
209 10
                $prefix = S::name(S::slice($name, 0, -1));
210
                return [
211 10
                    static::IMENIT => $prefix . 'я',
212 10
                    static::RODIT => $prefix . 'и',
213 10
                    static::DAT => $prefix . 'е',
214 10
                    static::VINIT => $prefix . 'ю',
215 10
                    static::TVORIT => $prefix . 'ей',
216 10
                    static::PREDLOJ => $prefix . 'е',
217
                ];
218
            }
219
220 110
            if (!in_array($name, static::$immutableNames, true)) {
221 110
                if ($gender === null) {
222
                    $gender = static::detectGender($name);
223
                }
224 110
                if ($gender === static::MALE || $name === 'саша') {
225 84
                    if (($result = static::getCasesMan($name)) !== null) {
226 84
                        return $result;
0 ignored issues
show
Best Practice introduced by
The expression return $result; seems to be an array, but some of its elements' types (boolean) are incompatible with the return type documented by morphos\Russian\FirstNamesInflection::getCases of type string[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
227
                    }
228 26
                } elseif ($gender === static::FEMALE) {
229 26
                    if (($result = static::getCasesWoman($name)) !== null) {
230 26
                        return $result;
0 ignored issues
show
Best Practice introduced by
The expression return $result; seems to be an array, but some of its elements' types (boolean) are incompatible with the return type documented by morphos\Russian\FirstNamesInflection::getCases of type string[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

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