Completed
Push — master ( 3cb9c7...e7e02a )
by f
08:11
created

src/Russian/FirstNamesInflection.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://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
        'аполлон',
52
        'аристарх',
53
        'аркадий',
54
        'арнольд',
55
        'арсений',
56
        'арсентий',
57
        'артем',
58
        'артём',
59
        'артемий',
60
        'артур',
61
        'аскольд',
62
        'афанасий',
63
        'богдан',
64
        'борис',
65
        'борислав',
66
        'бронислав',
67
        'вадим',
68
        'валентин',
69
        'валерий',
70
        'варлам',
71
        'василий',
72
        'венедикт',
73
        'вениамин',
74
        'веньямин',
75
        'венцеслав',
76
        'виктор',
77
        'виген',
78
        'вилен',
79
        'виталий',
80
        'владилен',
81
        'владимир',
82
        'владислав',
83
        'владлен',
84
        'вова',
85
        'всеволод',
86
        'всеслав',
87
        'вячеслав',
88
        'гавриил',
89
        'геннадий',
90
        'георгий',
91
        'герман',
92
        'глеб',
93
        'григорий',
94
        'давид',
95
        'даниил',
96
        'данил',
97
        'данила',
98
        'демьян',
99
        'денис',
100
        'димитрий',
101
        'дмитрий',
102
        'добрыня',
103
        'евгений',
104
        'евдоким',
105
        'евсей',
106
        'егор',
107
        'емельян',
108
        'еремей',
109
        'ермолай',
110
        'ерофей',
111
        'ефим',
112
        'захар',
113
        'иван',
114
        'игнат',
115
        'игорь',
116
        'илларион',
117
        'иларион',
118
        'илья',
119
        'иосиф',
120
        'казимир',
121
        'касьян',
122
        'кирилл',
123
        'кондрат',
124
        'константин',
125
        'кузьма',
126
        'лавр',
127
        'лаврентий',
128
        'лазарь',
129
        'ларион',
130
        'лев',
131
        'леонард',
132
        'леонид',
133
        'лука',
134
        'максим',
135
        'марат',
136
        'мартын',
137
        'матвей',
138
        'мефодий',
139
        'мирон',
140
        'михаил',
141
        'моисей',
142
        'назар',
143
        'никита',
144
        'николай',
145
        'олег',
146
        'осип',
147
        'остап',
148
        'павел',
149
        'панкрат',
150
        'пантелей',
151
        'парамон',
152
        'пётр',
153
        'петр',
154
        'платон',
155
        'потап',
156
        'прохор',
157
        'роберт',
158
        'ростислав',
159
        'савва',
160
        'савелий',
161
        'семён',
162
        'семен',
163
        'сергей',
164
        'сидор',
165
        'спартак',
166
        'тарас',
167
        'терентий',
168
        'тимофей',
169
        'тимур',
170
        'тихон',
171
        'ульян',
172
        'фёдор',
173
        'федор',
174
        'федот',
175
        'феликс',
176
        'фирс',
177
        'фома',
178
        'харитон',
179
        'харлам',
180
        'эдуард',
181
        'эммануил',
182
        'эраст',
183
        'юлиан',
184
        'юлий',
185
        'юрий',
186
        'яков',
187
        'ян',
188
        'ярослав',
189
    ];
190
191
    protected static $womenNames = [
192
        'авдотья',
193
        'аврора',
194
        'агата',
195
        'агния',
196
        'агриппина',
197
        'ада',
198
        'аксинья',
199
        'алевтина',
200
        'александра',
201
        'алёна',
202
        'алена',
203
        'алина',
204
        'алиса',
205
        'алла',
206
        'альбина',
207
        'амалия',
208
        'анастасия',
209
        'ангелина',
210
        'анжела',
211
        'анжелика',
212
        'анна',
213
        'антонина',
214
        'анфиса',
215
        'арина',
216
        'белла',
217
        'божена',
218
        'валентина',
219
        'валерия',
220
        'ванда',
221
        'варвара',
222
        'василина',
223
        'василиса',
224
        'вера',
225
        'вероника',
226
        'виктория',
227
        'виола',
228
        'виолетта',
229
        'вита',
230
        'виталия',
231
        'владислава',
232
        'власта',
233
        'галина',
234
        'глафира',
235
        'дарья',
236
        'диана',
237
        'дина',
238
        'ева',
239
        'евгения',
240
        'евдокия',
241
        'евлампия',
242
        'екатерина',
243
        'елена',
244
        'елизавета',
245
        'ефросиния',
246
        'ефросинья',
247
        'жанна',
248
        'зиновия',
249
        'злата',
250
        'зоя',
251
        'ивонна',
252
        'изольда',
253
        'илона',
254
        'инга',
255
        'инесса',
256
        'инна',
257
        'ирина',
258
        'ия',
259
        'капитолина',
260
        'карина',
261
        'каролина',
262
        'кира',
263
        'клавдия',
264
        'клара',
265
        'клеопатра',
266
        'кристина',
267
        'ксения',
268
        'лада',
269
        'лариса',
270
        'лиана',
271
        'лидия',
272
        'лилия',
273
        'лина',
274
        'лия',
275
        'лора',
276
        'любава',
277
        'любовь',
278
        'людмила',
279
        'майя',
280
        'маргарита',
281
        'марианна',
282
        'мариетта',
283
        'марина',
284
        'мария',
285
        'марья',
286
        'марта',
287
        'марфа',
288
        'марьяна',
289
        'матрёна',
290
        'матрена',
291
        'матрона',
292
        'милена',
293
        'милослава',
294
        'мирослава',
295
        'муза',
296
        'надежда',
297
        'настасия',
298
        'настасья',
299
        'наталия',
300
        'наталья',
301
        'нелли',
302
        'ника',
303
        'нина',
304
        'нинель',
305
        'нонна',
306
        'оксана',
307
        'олимпиада',
308
        'ольга',
309
        'пелагея',
310
        'полина',
311
        'прасковья',
312
        'раиса',
313
        'рената',
314
        'римма',
315
        'роза',
316
        'роксана',
317
        'руфь',
318
        'сарра',
319
        'светлана',
320
        'серафима',
321
        'снежана',
322
        'софья',
323
        'софия',
324
        'стелла',
325
        'степанида',
326
        'стефания',
327
        'таисия',
328
        'таисья',
329
        'тамара',
330
        'татьяна',
331
        'ульяна',
332
        'устиния',
333
        'устинья',
334
        'фаина',
335
        'фёкла',
336
        'фекла',
337
        'феодора',
338
        'хаврония',
339
        'христина',
340
        'эвелина',
341
        'эдита',
342
        'элеонора',
343
        'элла',
344
        'эльвира',
345
        'эмилия',
346
        'эмма',
347
        'юдифь',
348
        'юлиана',
349
        'юлия',
350
        'ядвига',
351
        'яна',
352
        'ярослава',
353
    ];
354
355
    protected static $immutableNames = [
356
        'николя',
357
    ];
358
359
    /**
360
     * Checks if name is mutable
361
     * @param string $name
362
     * @param null|string $gender
363
     * @return bool
364
     */
365 714
    public static function isMutable($name, $gender = null)
366
    {
367 714
        $name = S::lower($name);
368
369 714
        if (in_array($name, self::$immutableNames, true)) {
370
            return false;
371
        }
372
373 714
        if ($gender === null) {
374
            $gender = self::detectGender($name);
375
        }
376
377
        // man rules
378 714
        if ($gender === self::MALE) {
379
            // soft consonant
380 449
            if (S::lower(S::slice($name, -1)) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
381 14
                return true;
382 435
            } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, ['й', /*'Ч', 'Щ'*/]))) { // hard consonant
383 257
                return true;
384 178
            } elseif (S::slice($name, -1) == 'й') {
385 104
                return true;
386 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 367 can also be of type false; however, morphos\S::slice() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
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...
387 74
                return true;
388
            }
389 265
        } else if ($gender === self::FEMALE) {
390
            // soft consonant
391 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 367 can also be of type false; however, morphos\S::slice() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
392 4
                return true;
393 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 367 can also be of type false; however, morphos\S::slice() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
394 5
                return true;
395
            }
396
        }
397
398
        // common rules
399 326
        if ((in_array(S::slice($name, -1), ['а', 'я']) && !self::isVowel(S::slice($name, -2, -1))) || in_array(S::slice($name, -2), ['ия', 'ья', 'ея', 'оя'])) {
400 275
            return true;
401
        }
402
403 51
        return false;
404
    }
405
406
    /**
407
     * @param $name
408
     * @return string
409
     */
410 509
    public static function detectGender($name)
411
    {
412 509
        $name = S::lower($name);
413 509
        if (in_array($name, self::$menNames)) {
414 120
            return self::MALE;
415 389
        } elseif (in_array($name, self::$womenNames)) {
416 110
            return self::FEMALE;
417
        }
418
419 279
        $man = $woman = 0;
420 279
        $last1 = S::slice($name, -1);
421 279
        $last2 = S::slice($name, -2);
422 279
        $last3 = S::slice($name, -3);
423
424
        // try to detect gender by some statistical rules
425
        //
426 279
        if ($last1 == 'й') {
427 48
            $man += 0.9;
428
        }
429 279
        if ($last1 == 'ь') {
430
            $man += 0.02;
431
        }
432 279
        if (in_array($last1, self::$consonants)) {
433 199
            $man += 0.01;
434
        }
435 279
        if (in_array($last2, ['он', 'ов', 'ав', 'ам', 'ол', 'ан', 'рд', 'мп'])) {
436 47
            $man += 0.3;
437
        }
438 279
        if (in_array($last2, ['вь', 'фь', 'ль'])) {
439
            $woman += 0.1;
440
        }
441 279
        if (in_array($last2, ['ла'])) {
442 4
            $woman += 0.04;
443
        }
444 279
        if (in_array($last2, ['то', 'ма'])) {
445
            $man += 0.01;
446
        }
447 279 View Code Duplication
        if (in_array($last3, ['лья', 'вва', 'ока', 'ука', 'ита'])) {
448 2
            $man += 0.2;
449
        }
450 279
        if (in_array($last3, ['има'])) {
451
            $woman += 0.15;
452
        }
453 279 View Code Duplication
        if (in_array($last3, ['лия', 'ния', 'сия', 'дра', 'лла', 'кла', 'опа'])) {
454 3
            $woman += 0.5;
455
        }
456 279
        if (in_array(S::slice($name, -4), ['льда', 'фира', 'нина', 'лита', 'алья'])) {
457
            $woman += 0.5;
458
        }
459
460 279
        return $man == $woman ? null
461 279
            : ($man > $woman ? self::MALE : self::FEMALE);
462
    }
463
464
    /**
465
     * @param string $name
466
     * @param null|string $gender
467
     * @return array
468
     */
469 138
    public static function getCases($name, $gender = null)
470
    {
471 138
        $name = S::lower($name);
472
473 138
        if (self::isMutable($name, $gender)) {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 471 can also be of type false; however, morphos\Russian\FirstNamesInflection::isMutable() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
474
            // common rules for ия and я
475 138
            if (S::slice($name, -2) == 'ия') {
476 20
                $prefix = S::name(S::slice($name, 0, -1));
477
                return [
478 20
                    self::IMENIT => $prefix.'я',
479 20
                    self::RODIT => $prefix.'и',
480 20
                    self::DAT => $prefix.'и',
481 20
                    self::VINIT => $prefix.'ю',
482 20
                    self::TVORIT => $prefix.'ей',
483 20
                    self::PREDLOJ => $prefix.'и',
484
                ];
485 118
            } elseif (S::slice($name, -1) == 'я') {
0 ignored issues
show
It seems like $name defined by \morphos\S::lower($name) on line 471 can also be of type false; however, morphos\S::slice() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
486 10
                $prefix = S::name(S::slice($name, 0, -1));
487
                return [
488 10
                    self::IMENIT => $prefix.'я',
489 10
                    self::RODIT => $prefix.'и',
490 10
                    self::DAT => $prefix.'е',
491 10
                    self::VINIT => $prefix.'ю',
492 10
                    self::TVORIT => $prefix.'ей',
493 10
                    self::PREDLOJ => $prefix.'е',
494
                ];
495
            }
496
497 108
            if (!in_array($name, self::$immutableNames, true)) {
498 108
                if ($gender === null) {
499
                    $gender = self::detectGender($name);
500
                }
501 108
                if ($gender === self::MALE || $name === 'саша') {
502 82
                    if (($result = self::getCasesMan($name)) !== null) {
503 82
                        return $result;
504
                    }
505 26
                } elseif ($gender === self::FEMALE) {
506 26
                    if (($result = self::getCasesWoman($name)) !== null) {
507 26
                        return $result;
508
                    }
509
                }
510
            }
511
        }
512
513
        $name = S::name($name);
514
        return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT, self::PREDLOJ), $name);
515
    }
516
517
    /**
518
     * @param string $name
519
     * @return array|null
520
     */
521 82
    protected static function getCasesMan($name)
522
    {
523
        // special cases for Лев, Павел
524 82
        if (isset(self::$exceptions[$name])) {
525
            return self::$exceptions[$name];
526 82
        } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, ['й', /*'Ч', 'Щ'*/]))) { // hard consonant
527 22
			if (in_array(S::slice($name, -2), ['ек', 'ёк'])) { // Витек, Санек
528
                // case for foreign names like Салмонбек
529 6
                if (self::isConsonant(S::slice($name, -4, -3)))
530 2
                    $prefix = S::name(S::slice($name, 0, -2)).'ек';
531
                else
532 6
				    $prefix = S::name(S::slice($name, 0, -2)).'ьк';
533
			} else {
534 16
                if ($name === 'пётр')
535
                    $prefix = S::name(str_replace('ё', 'е', $name));
536
                else
537 16
				    $prefix = S::name($name);
538
            }
539
            return [
540 22
                self::IMENIT => S::name($name),
541 22
                self::RODIT => $prefix.'а',
542 22
                self::DAT => $prefix.'у',
543 22
                self::VINIT => $prefix.'а',
544 22
                self::TVORIT => RussianLanguage::isHissingConsonant(S::slice($name, -1)) || S::slice($name, -1) == 'ц' ? $prefix.'ем' : $prefix.'ом',
545 22
                self::PREDLOJ => $prefix.'е',
546
            ];
547 60 View Code Duplication
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) { // soft consonant
548 10
            $prefix = S::name(S::slice($name, 0, -1));
549
            return [
550 10
                self::IMENIT => $prefix.'ь',
551 10
                self::RODIT => $prefix.'я',
552 10
                self::DAT => $prefix.'ю',
553 10
                self::VINIT => $prefix.'я',
554 10
                self::TVORIT => $prefix.'ем',
555 10
                self::PREDLOJ => $prefix.'е',
556
            ];
557 50
        } elseif (in_array(S::slice($name, -2), ['ай', 'ей', 'ой', 'уй', 'яй', 'юй', 'ий'])) {
558 24
            $prefix = S::name(S::slice($name, 0, -1));
559 24
            $postfix = S::slice($name, -2) == 'ий' ? 'и' : 'е';
560
            return [
561 24
                self::IMENIT => $prefix.'й',
562 24
                self::RODIT => $prefix.'я',
563 24
                self::DAT => $prefix.'ю',
564 24
                self::VINIT => $prefix.'я',
565 24
                self::TVORIT => $prefix.'ем',
566 24
                self::PREDLOJ => $prefix.$postfix,
567
            ];
568 26
        } elseif (S::slice($name, -1) == 'а' && self::isConsonant($before = S::slice($name, -2, -1)) && !in_array($before, [/*'г', 'к', 'х', */'ц'])) {
569 22
            $prefix = S::name(S::slice($name, 0, -1));
570 22
            $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, ['г', 'к', 'х'])) ? 'и' : 'ы';
571
            return [
572 22
                self::IMENIT => $prefix.'а',
573 22
                self::RODIT => $prefix.$postfix,
574 22
                self::DAT => $prefix.'е',
575 22
                self::VINIT => $prefix.'у',
576 22
                self::TVORIT => $prefix.($before === 'ш' ? 'е' : 'о').'й',
577 22
                self::PREDLOJ => $prefix.'е',
578
            ];
579 4
        } elseif (S::slice($name, -2) == 'ло' || S::slice($name, -2) == 'ко') {
580 4
            $prefix = S::name(S::slice($name, 0, -1));
581 4
            $postfix = S::slice($name, -2, -1) == 'к' ? 'и' : 'ы';
582
            return [
583 4
                self::IMENIT => $prefix.'о',
584 4
                self::RODIT =>  $prefix.$postfix,
585 4
                self::DAT => $prefix.'е',
586 4
                self::VINIT => $prefix.'у',
587 4
                self::TVORIT => $prefix.'ой',
588 4
                self::PREDLOJ => $prefix.'е',
589
            ];
590
        }
591
592
        return null;
593
    }
594
595
    /**
596
     * @param string $name
597
     * @return array|null
598
     */
599 26
    protected static function getCasesWoman($name)
600
    {
601 26
        if (S::slice($name, -1) == 'а' && !self::isVowel($before = (S::slice($name, -2, -1)))) {
602 17
            $prefix = S::name(S::slice($name, 0, -1));
603 17
            if ($before != 'ц') {
604 14
                $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, ['г', 'к', 'х'])) ? 'и' : 'ы';
605
                return [
606 14
                    self::IMENIT => $prefix.'а',
607 14
                    self::RODIT => $prefix.$postfix,
608 14
                    self::DAT => $prefix.'е',
609 14
                    self::VINIT => $prefix.'у',
610 14
                    self::TVORIT => $prefix.'ой',
611 14
                    self::PREDLOJ => $prefix.'е',
612
                ];
613
            } else {
614
                return [
615 3
                    self::IMENIT => $prefix.'а',
616 3
                    self::RODIT => $prefix.'ы',
617 3
                    self::DAT => $prefix.'е',
618 3
                    self::VINIT => $prefix.'у',
619 3
                    self::TVORIT => $prefix.'ей',
620 3
                    self::PREDLOJ => $prefix.'е',
621
                ];
622
            }
623 9 View Code Duplication
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
624 4
            $prefix = S::name(S::slice($name, 0, -1));
625
            return [
626 4
                self::IMENIT => $prefix.'ь',
627 4
                self::RODIT => $prefix.'и',
628 4
                self::DAT => $prefix.'и',
629 4
                self::VINIT => $prefix.'ь',
630 4
                self::TVORIT => $prefix.'ью',
631 4
                self::PREDLOJ => $prefix.'и',
632
            ];
633 5
        } elseif (RussianLanguage::isHissingConsonant(S::slice($name, -1))) {
634 5
            $prefix = S::name($name);
635
            return [
636 5
                self::IMENIT => $prefix,
637 5
                self::RODIT => $prefix.'и',
638 5
                self::DAT => $prefix.'и',
639 5
                self::VINIT => $prefix,
640 5
                self::TVORIT => $prefix.'ью',
641 5
                self::PREDLOJ => $prefix.'и',
642
            ];
643
        }
644
        return null;
645
    }
646
647
    /**
648
     * @param string $name
649
     * @param string $case
650
     * @param null|string $gender
651
     * @return string
652
     * @throws \Exception
653
     */
654 52
    public static function getCase($name, $case, $gender = null)
655
    {
656 52
        $case = self::canonizeCase($case);
657 52
        $forms = self::getCases($name, $gender);
658 52
        return $forms[$case];
659
    }
660
}
661