Issues (116)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Russian/GeographicalNamesInflection.php (1 issue)

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: https://ru.wikipedia.org/wiki/%D0%A1%D0%BA%D0%BB%D0%BE%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B3%D0%B5%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B9_%D0%B2_%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%BE%D0%BC_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B5
8
 */
9
class GeographicalNamesInflection extends \morphos\BaseInflection implements Cases
10
{
11
    use RussianLanguage, CasesHelper;
12
13
    /** @var bool Настройка склоняемости славянских топонимов на -ов(о), -ев(о), -ин(о), -ын(о) */
14
    public static $inflectSlavicNames = true;
15
16
    /** @var string[]  */
17
    protected static $abbreviations = [
18
        'сша',
19
        'оаэ',
20
        'ссср',
21
        'юар',
22
    ];
23
24
    /** @var string[]  */
25
    protected static $delimiters = [
26
        ' ',
27
        '-на-',
28
        '-эль-',
29
        '-де-',
30
        '-сюр-',
31
        '-ан-',
32
        '-ла-',
33
        '-',
34
    ];
35
36
    /** @var string[]  */
37
    protected static $ovAbnormalExceptions = [
38
        'осташков',
39
    ];
40
41
    /**
42
     * @var string[] Immutable names or name parts
43
     */
44
    protected static $immutableNames = [
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
    /** @var string[] */
90
    protected static $immutableTriggerPrefixes = [
91
        'спас',
92
        'усть',
93
        'соль',
94
    ];
95
96
    /** @var string[]  */
97
    protected static $runawayVowelsExceptions = [
98
        'торжо*к',
99
        'волоче*к',
100
        'орё*л',
101
        'египе*т',
102
        'лунине*ц',
103
        'городо*к',
104
        'новогрудо*к',
105
        'острове*ц',
106
        'черепове*ц',
107
    ];
108
109
    /**
110
     * @var string[]
111
     * @phpstan-var array<string, string>
112
     */
113
    protected static $misspellings = [
114
        'орел' => 'орёл',
115
        'рублево' => 'рублёво',
116
    ];
117
118
    /**
119
     * @return int[]|false[]
120
     */
121 13 View Code Duplication
    protected static function getRunAwayVowelsList()
0 ignored issues
show
This method seems to be duplicated in 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...
122
    {
123 13
        $runawayVowelsNormalized = [];
124 13
        foreach (static::$runawayVowelsExceptions as $word) {
125 13
            $runawayVowelsNormalized[str_replace('*', '', $word)] = S::indexOf($word, '*') - 1;
126
        }
127 13
        return $runawayVowelsNormalized;
128
    }
129
130
    /**
131
     * Проверяет, склоняемо ли название
132
     * @param string $name Название
133
     * @return bool
134
     */
135 2
    public static function isMutable($name)
136
    {
137 2
        $name = S::lower($name);
138
139
        // // ends with 'ы' or 'и': plural form
140
        // if (in_array(S::slice($name, -1), array('и', 'ы')))
141
        //     return false;
142
143 2
        if (in_array($name, static::$abbreviations, true) || in_array($name, static::$immutableNames, true)) {
144 2
            return false;
145
        }
146
147
        if (strpos($name, ' ') !== false) {
148
            // explode() is not applicable because Geographical unit may have few words
149
            $first_part = S::slice($name, 0, S::findFirstPosition($name, ' '));
150
            $last_part = S::slice($name,
151
                S::findLastPosition($name, ' ') + 1);
152
153
            // город N, село N, хутор N, район N, поселок N, округ N, республика N
154
            // N область, N край, N район, N волость
155
            if (in_array($first_part, ['город', 'село', 'хутор', 'район', 'поселок', 'округ', 'республика'], true)
156
                || in_array($last_part, ['край', 'область', 'район', 'волость'], true)) {
157
                return true;
158
            }
159
160
            // пгт N
161
            if ($first_part === 'пгт')
162
                return false;
163
        }
164
165
        if (strpos($name, '-') !== false && S::stringContains($name, static::$immutableTriggerPrefixes)) {
166
            return false;
167
        }
168
169
        // ends with 'е' or 'о', but not with 'ово/ёво/ево/ино/ыно'
170
        if (in_array(S::slice($name, -1), ['е', 'о'], true)
171
            && !in_array(S::slice($name, -3, -1), ['ов', 'ёв', 'ев', 'ин', 'ын'], true)) {
172
            return (bool)static::$inflectSlavicNames;
173
        }
174
        return true;
175
    }
176
177
    /**
178
     * Получение всех форм названия
179
     * @param string $name
180
     * @return string[]
181
     * @phpstan-return array<string, string>
182
     * @throws \Exception
183
     */
184 48
    public static function getCases($name)
185
    {
186 48
        $name = S::lower($name);
187
188
        // Проверка на неизменяемость
189 48
        if (in_array($name, static::$immutableNames, true)
190 48
            || (strpos($name, '-') !== false && S::stringContains($name, static::$immutableTriggerPrefixes))
191
        ) {
192 2
            return array_fill_keys(
193 2
                [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE]
194 2
                , S::name($name));
195
        }
196
197
        // Проверка на сложное название
198 48
        if (strpos($name, ' ') !== false) {
199 9
            $first_part = S::slice($name, 0, S::findFirstPosition($name, ' '));
200
            // город N, село N, хутор N, пгт N
201 9
            if (in_array($first_part, ['город', 'село', 'хутор', 'пгт', 'район', 'поселок', 'округ', 'республика'], true)) {
202 3
                if ($first_part === 'пгт')
203
                    return array_fill_keys(
204
                        [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE],
205
                        'пгт '.S::name(S::slice($name, 4)));
206
207 3
                if ($first_part === 'республика') {
208 1
                    $prefix = array_map(['\\morphos\\S', 'name'], NounDeclension::getCases($first_part));
209
                } else {
210 2
                    $prefix = NounDeclension::getCases($first_part);
211
                }
212 3
                $prefix[Cases::LOCATIVE] = $prefix[Cases::PREDLOJ];
213
214 3
                return static::composeCasesFromWords([$prefix,
215 3
                    array_fill_keys(
216 3
                        array_merge(static::getAllCases(), [\morphos\Russian\Cases::LOCATIVE]),
217 3
                        S::name(S::slice($name, S::length($first_part) + 1)))
218
                ]);
219
            }
220
221 6
            $last_part = S::slice($name,
222 6
                S::findLastPosition($name, ' ') + 1);
223
            // N область, N край
224 6
            if (in_array($last_part, ['край', 'область', 'район', 'волость'], true)) {
225 2
                $last_part_cases = NounDeclension::getCases($last_part);
226 2
                $last_part_cases[Cases::LOCATIVE] = $last_part_cases[Cases::PREDLOJ];
227 2
                return static::composeCasesFromWords(
228
                    [
229 2
                        static::getCases(S::slice($name, 0, S::findLastPosition($name, ' '))),
230 2
                        $last_part_cases,
231
                    ]);
232
            }
233
        }
234
235
        // Сложное название с разделителем
236 45
        foreach (static::$delimiters as $delimiter) {
237 45
            if (strpos($name, $delimiter) !== false) {
238 7
                $parts = explode($delimiter, $name);
239 7
                $result = [];
240 7
                foreach ($parts as $i => $part) {
241 7
                    $result[$i] = static::getCases($part);
242
                }
243 45
                return static::composeCasesFromWords($result, $delimiter);
244
            }
245
        }
246
247
        // Исправление ошибок
248 45
        if (array_key_exists($name, static::$misspellings)) {
249
            $name = static::$misspellings[$name];
250
        }
251
252
        // Само склонение
253 45
        if (!in_array($name, static::$abbreviations, true)) {
254 43
            switch (S::slice($name, -2)) {
255
                // Нижний, Русский
256 43
                case 'ий':
257 3
                    $prefix = S::name(S::slice($name, 0, -2));
258
                    return [
259 3
                        static::IMENIT => $prefix.'ий',
260 3
                        static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'),
261 3
                        static::DAT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'),
262 3
                        static::VINIT => $prefix.'ий',
263 3
                        static::TVORIT => $prefix.'им',
264 3
                        static::PREDLOJ => $prefix.(static::chooseEndingBySonority($prefix, 'ем', 'ом')),
265 3
                        static::LOCATIVE => $prefix.(static::chooseEndingBySonority($prefix, 'ем', 'ом')),
266
                    ];
267
268
                // Ростовская
269 42
                case 'ая':
270 1
                    $prefix = S::name(S::slice($name, 0, -2));
271
                    return [
272 1
                        static::IMENIT => $prefix.'ая',
273 1
                        static::RODIT => $prefix.'ой',
274 1
                        static::DAT => $prefix.'ой',
275 1
                        static::VINIT => $prefix.'ую',
276 1
                        static::TVORIT => $prefix.'ой',
277 1
                        static::PREDLOJ => $prefix.'ой',
278 1
                        static::LOCATIVE => $prefix.'ой',
279
                    ];
280
281
                // Нижняя, Верхняя, Средняя
282 41
                case 'яя':
283
                    $prefix = S::name(S::slice($name, 0, -2));
284
                    return [
285
                        static::IMENIT => $prefix.'яя',
286
                        static::RODIT => $prefix.'ей',
287
                        static::DAT => $prefix.'ей',
288
                        static::VINIT => $prefix.'юю',
289
                        static::TVORIT => $prefix.'ей',
290
                        static::PREDLOJ => $prefix.'ей',
291
                        static::LOCATIVE => $prefix.'ей',
292
                    ];
293
294
                // Россошь
295 41
                case 'шь':
296
                // Пермь, Кемь
297 40
                case 'мь':
298
                // Рязань, Назрань
299 39
                case 'нь':
300
                // Сысерть
301 38
                case 'ть':
302
                // Керчь
303 38
                case 'чь':
304 4
                    $prefix = S::name(S::slice($name, 0, -1));
305
                    return [
306 4
                        static::IMENIT => $prefix.'ь',
307 4
                        static::RODIT => $prefix.'и',
308 4
                        static::DAT => $prefix.'и',
309 4
                        static::VINIT => $prefix.'ь',
310 4
                        static::TVORIT => $prefix.'ью',
311 4
                        static::PREDLOJ => $prefix.'и',
312 4
                        static::LOCATIVE => $prefix.'и',
313
                    ];
314
315
                // Грозный, Благодарный
316 37
                case 'ый':
317 2
                    $prefix = S::name(S::slice($name, 0, -2));
318
                    return [
319 2
                        static::IMENIT => $prefix.'ый',
320 2
                        static::RODIT => $prefix.'ого',
321 2
                        static::DAT => $prefix.'ому',
322 2
                        static::VINIT => $prefix.'ый',
323 2
                        static::TVORIT => $prefix.'ым',
324 2
                        static::PREDLOJ => $prefix.'ом',
325 2
                        static::LOCATIVE => $prefix.'ом',
326
                    ];
327
328
                // Ставрополь, Ярославль, Электросталь
329 35
                case 'ль':
330 2
                    $prefix = S::name(S::slice($name, 0, -1));
331
332 2
                    if ($name === 'электросталь')
333
                        return [
334 1
                            static::IMENIT => $prefix.'ь',
335 1
                            static::RODIT => $prefix.'и',
336 1
                            static::DAT => $prefix.'и',
337 1
                            static::VINIT => $prefix.'ь',
338 1
                            static::TVORIT => $prefix.'ью',
339 1
                            static::PREDLOJ => $prefix.'и',
340 1
                            static::LOCATIVE => $prefix.'и',
341
                        ];
342
343
                    return [
344 1
                        static::IMENIT => $prefix.'ь',
345 1
                        static::RODIT => $prefix.'я',
346 1
                        static::DAT => $prefix.'ю',
347 1
                        static::VINIT => $prefix.'ь',
348 1
                        static::TVORIT => $prefix.'ем',
349 1
                        static::PREDLOJ => $prefix.'е',
350 1
                        static::LOCATIVE => $prefix.'е',
351
                    ];
352
353
                // Адыгея, Чечня
354 33
                case 'ея':
355 32
                case 'ня':
356 1
                    $prefix = S::name(S::slice($name, 0, -1));
357
                    return [
358 1
                        static::IMENIT => S::name($name),
359 1
                        static::RODIT => $prefix.'е',
360 1
                        static::DAT => $prefix.'е',
361 1
                        static::VINIT => $prefix.'я',
362 1
                        static::TVORIT => $prefix.'ей',
363 1
                        static::PREDLOJ => $prefix.'е',
364 1
                        static::LOCATIVE => $prefix.'е',
365
                    ];
366
367
                // Тверь, Анадырь
368 32
                case 'рь':
369 2
                    $prefix = S::name(S::slice($name, 0, -1));
370 2
                    $last_vowel = S::slice($prefix, -2, -1);
371
                    return [
372 2
                        static::IMENIT => $prefix . 'ь',
373 2
                        static::RODIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'я'),
374 2
                        static::DAT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'ю'),
375 2
                        static::VINIT => $prefix . 'ь',
376 2
                        static::TVORIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'ью' : 'ем'),
377 2
                        static::PREDLOJ => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'е'),
378 2
                        static::LOCATIVE => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'е'),
379
                    ];
380
381
                // Березники, Ессентуки
382 30
                case 'ки':
383
                // Старые Дороги
384 29
                case 'ги':
385
                // Ушачи, Ивацевичи
386 29
                case 'чи':
387 3
                    $prefix = S::name(S::slice($name, 0, -1));
388
                    return [
389 3
                        static::IMENIT => $prefix . 'и',
390 3
                        static::RODIT => ($name === 'луки'
391 1
                            ? $prefix
392 2
                            : (S::slice($name, -2) === 'чи'
393 1
                                ? $prefix . 'ей'
394 3
                                : $prefix . 'ов')),
395 3
                        static::DAT => $prefix . 'ам',
396 3
                        static::VINIT => $prefix . 'и',
397 3
                        static::TVORIT => $prefix . 'ами',
398 3
                        static::PREDLOJ => $prefix . 'ах',
399 3
                        static::LOCATIVE => $prefix . 'ах',
400
                    ];
401
402
                // Набережные
403 28
                case 'ые':
404
                // Великие
405 28
                case 'ие':
406 2
                    $prefix = S::name(S::slice($name, 0, -1));
407
                    return [
408 2
                        static::IMENIT => $prefix . 'е',
409 2
                        static::RODIT => $prefix . 'х',
410 2
                        static::DAT => $prefix . 'м',
411 2
                        static::VINIT => $prefix . 'е',
412 2
                        static::TVORIT => $prefix . 'ми',
413 2
                        static::PREDLOJ => $prefix . 'х',
414 2
                        static::LOCATIVE => $prefix . 'х',
415
                    ];
416
417
                // Челны
418 27
                case 'ны':
419
                // Мосты
420 26
                case 'ты':
421
                // Столбцы
422 26
                case 'цы':
423 2
                    $prefix = S::name(S::slice($name, 0, -1));
424
                    return [
425 2
                        static::IMENIT => $prefix . 'ы',
426 2
                        static::RODIT => $prefix . 'ов',
427 2
                        static::DAT => $prefix . 'ам',
428 2
                        static::VINIT => $prefix . 'ы',
429 2
                        static::TVORIT => $prefix . 'ами',
430 2
                        static::PREDLOJ => $prefix . 'ах',
431 2
                        static::LOCATIVE => $prefix . 'ах',
432
                    ];
433
434
                // Глубокое
435 25
                case 'ое':
436 1
                    $prefix = S::name(S::slice($name, 0, -2));
437
                    return [
438 1
                        static::IMENIT => $prefix.'ое',
439 1
                        static::RODIT => $prefix.'ого',
440 1
                        static::DAT => $prefix.'ому',
441 1
                        static::VINIT => $prefix.'ое',
442 1
                        static::TVORIT => $prefix.'им',
443 1
                        static::PREDLOJ => $prefix.'ом',
444 1
                        static::LOCATIVE => $prefix.'ом',
445
                    ];
446
            }
447
448 24
            switch (S::slice($name, -1)) {
449 24
                case 'р':
450
                    // Бор
451
                    $prefix = S::name(S::slice($name, 0, -1));
452
                    return [
453
                        static::IMENIT => $prefix.'р',
454
                        static::RODIT => $prefix.'ра',
455
                        static::DAT => $prefix.'ру',
456
                        static::VINIT => $prefix.'р',
457
                        static::TVORIT => $prefix.'ром',
458
                        static::PREDLOJ => $prefix.'ре',
459
                        static::LOCATIVE => $prefix.'ру',
460
                    ];
461
462 24
                case 'ы':
463
                    // Чебоксары, Шахты
464 1
                    $prefix = S::name(S::slice($name, 0, -1));
465
                    return [
466 1
                        static::IMENIT => $prefix.'ы',
467 1
                        static::RODIT => $prefix,
468 1
                        static::DAT => $prefix.'ам',
469 1
                        static::VINIT => $prefix.'ы',
470 1
                        static::TVORIT => $prefix.'ами',
471 1
                        static::PREDLOJ => $prefix.'ах',
472 1
                        static::LOCATIVE => $prefix.'ах',
473
                    ];
474
475 23
                case 'я':
476
                    // Азия
477 1
                    $prefix = S::name(S::slice($name, 0, -1));
478
                    return [
479 1
                        static::IMENIT => S::name($name),
480 1
                        static::RODIT => $prefix.'и',
481 1
                        static::DAT => $prefix.'и',
482 1
                        static::VINIT => $prefix.'ю',
483 1
                        static::TVORIT => $prefix.'ей',
484 1
                        static::PREDLOJ => $prefix.'и',
485 1
                        static::LOCATIVE => $prefix.'и',
486
                    ];
487
488 22
                case 'а':
489
                    // Москва, Рига
490 5
                    $prefix = S::name(S::slice($name, 0, -1));
491
                    return [
492 5
                        static::IMENIT => $prefix.'а',
493 5
                        static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -2, -1)) || static::isHissingConsonant(S::slice($name, -2, -1)) ? 'и' : 'ы'),
494 5
                        static::DAT => $prefix.'е',
495 5
                        static::VINIT => $prefix.'у',
496 5
                        static::TVORIT => $prefix.'ой',
497 5
                        static::PREDLOJ => $prefix.'е',
498 5
                        static::LOCATIVE => $prefix.'е',
499
                    ];
500
501 17
                case 'й':
502
                    // Ишимбай
503 2
                    $prefix = S::name(S::slice($name, 0, -1));
504
                    return [
505 2
                        static::IMENIT => $prefix . 'й',
506 2
                        static::RODIT => $prefix . 'я',
507 2
                        static::DAT => $prefix . 'ю',
508 2
                        static::VINIT => $prefix . 'й',
509 2
                        static::TVORIT => $prefix . 'ем',
510 2
                        static::PREDLOJ => $prefix . 'е',
511 2
                        static::LOCATIVE => $prefix . 'е',
512
                    ];
513
            }
514
515 15
            if (static::isConsonant($last_char = S::slice($name,  -1)) && !in_array($name, static::$ovAbnormalExceptions, true)) {
516 13
                $runaway_vowels_list = static::getRunAwayVowelsList();
517
518
                // if run-away vowel in name
519 13
                if (isset($runaway_vowels_list[$name])) {
520 4
                    $runaway_vowel_offset = $runaway_vowels_list[$name];
521 4
                    $prefix = S::name(S::slice($name, 0, $runaway_vowel_offset) . S::slice($name, $runaway_vowel_offset + 1));
522
                } else {
523 9
                    $prefix = S::name($name);
524
                }
525
526
                // Париж, Валаам, Киев
527
                return [
528 13
                    static::IMENIT => S::name($name),
529 13
                    static::RODIT => $prefix . 'а',
530 13
                    static::DAT => $prefix . 'у',
531 13
                    static::VINIT => S::name($name),
532 13
                    static::TVORIT => $prefix . (
533 13
                        static::isVelarConsonant(S::slice($name, -2, -1))
534 13
                            || static::isHissingConsonant($last_char)
535 13
                        ? 'ем' : 'ом'),
536 13
                    static::PREDLOJ => $prefix . 'е',
537 13
                    static::LOCATIVE => $prefix.($name === 'крым' ? 'у' : 'е'),
538
                ];
539
            }
540
541
            // ов, ово, ёв, ёво, ев, ево, ...
542 3
            $suffixes = ['ов', 'ёв', 'ев', 'ин', 'ын'];
543 3
            if (static::$inflectSlavicNames && (
544 3
                (in_array(S::slice($name, -1), ['е', 'о'], true) && in_array(S::slice($name, -3, -1), $suffixes, true))
545 3
                || in_array(S::slice($name, -2), $suffixes, true)
546
                )) {
547
                // ово, ёво, ...
548 2
                if (in_array(S::slice($name, -3, -1), $suffixes, true)) {
549 1
                    $prefix = S::name(S::slice($name, 0, -1));
550
                }
551
                // ов, её, ...
552 1
                elseif (in_array(S::slice($name, -2), $suffixes, true)) {
553 1
                    $prefix = S::name($name);
554
                } else {
555
                    $prefix = '';
556
                }
557
558
                return [
559 2
                    static::IMENIT => S::name($name),
560 2
                    static::RODIT => $prefix.'а',
561 2
                    static::DAT => $prefix.'у',
562 2
                    static::VINIT => S::name($name),
563 2
                    static::TVORIT => $name !== 'осташков' ? $prefix.'ом' : $prefix.'ым',
564 2
                    static::PREDLOJ => $prefix.'е',
565 2
                    static::LOCATIVE => $prefix.'е',
566
                ];
567
            }
568
        }
569
570
        // if no rules matches or name is immutable
571 3
        $name = in_array($name, static::$abbreviations, true) ? S::upper($name) : S::name($name);
572 3
        return array_fill_keys(
573 3
            [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE],
574 3
            $name);
575
    }
576
577
    /**
578
     * Получение одной формы (падежа) названия.
579
     * @param string $name  Название
580
     * @param string $case Падеж. Одна из констант {@see \morphos\Russian\Cases} или {@see \morphos\Cases}.
581
     * @see \morphos\Russian\Cases
582
     * @return string
583
     * @throws \Exception
584
     */
585
    public static function getCase($name, $case)
586
    {
587
        $case = static::canonizeCase($case);
588
        $forms = static::getCases($name);
589
        return $forms[$case];
590
    }
591
}
592