Completed
Push — master ( 0d947c...778e4d )
by f
01:51
created

GeographicalNamesInflection::getCase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
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
    protected static $abbreviations = [
14
        'сша',
15
        'оаэ',
16
        'ссср',
17
        'юар',
18
    ];
19
20
    protected static $delimiters = [
21
        ' ',
22
        '-на-',
23
        '-',
24
    ];
25
26
    protected static $ovAbnormalExceptions = [
27
        'осташков',
28
    ];
29
30
    protected static $immutableParts = [
31
        'санкт',
32
        'йошкар',
33
        'улан',
34
    ];
35
36
    protected static $runawayVowelsExceptions = [
37
        'торжо*к',
38
        'волоче*к',
39
        'орё*л',
40
    ];
41
42
    protected static $misspellings = [
43
        'орел' => 'орёл',
44
    ];
45
46
    /**
47
     * @return array|bool
48
     */
49 9 View Code Duplication
    protected static function getRunAwayVowelsList()
0 ignored issues
show
Duplication introduced by
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...
50
    {
51 9
        $runawayVowelsNormalized = [];
52 9
        foreach (static::$runawayVowelsExceptions as $word) {
53 9
            $runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1;
54
        }
55 9
        return $runawayVowelsNormalized;
56
    }
57
58
    /**
59
     * Проверяет, склоняемо ли название
60
     * @param string $name Название
61
     * @return bool
62
     */
63 2
    public static function isMutable($name)
64
    {
65 2
        $name = S::lower($name);
66
67
        // // ends with 'ы' or 'и': plural form
68
        // if (in_array(S::slice($name, -1), array('и', 'ы')))
69
        //     return false;
70
71 2
        if (in_array($name, static::$abbreviations, true) || in_array($name, static::$immutableParts, true)) {
72 2
            return false;
73
        }
74
75
        if (strpos($name, ' ') !== false) {
76
            // explode() is not applicable because Geographical unit may have few words
77
            $first_part = S::slice($name, 0, S::findFirstPosition($name, ' '));
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 65 can also be of type boolean; however, morphos\S::findFirstPosition() 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...
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 65 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...
78
            $last_part = S::slice($name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 65 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...
79
                S::findLastPosition($name, ' ') + 1);
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 65 can also be of type boolean; however, morphos\S::findLastPosition() 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...
80
81
            // город N, село N, хутор N, район N, поселок N, округ N, республика N
82
            // N область, N край
83
            if (in_array($first_part, ['город', 'село', 'хутор', 'район', 'поселок', 'округ', 'республика'], true)
84
                || in_array($last_part, ['край', 'область'], true)) {
85
                return true;
86
            }
87
88
            // пгт N
89
            if ($first_part === 'пгт')
90
                return false;
91
        }
92
93
        // ends with 'е' or 'о', but not with 'ово/ёво/ево/ино/ыно'
94
        if (in_array(S::slice($name, -1), ['е', 'о'], true) && !in_array(S::slice($name, -3, -1), ['ов', 'ёв', 'ев', 'ин', 'ын'], true)) {
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 65 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...
95
            return false;
96
        }
97
        return true;
98
    }
99
100
    /**
101
     * Получение всех форм названия
102
     * @param string $name
103
     * @return array
104
     * @throws \Exception
105
     */
106 39
    public static function getCases($name)
107
    {
108 39
        $name = S::lower($name);
109
110
        // Проверка на неизменяемость и сложное название
111 39
        if (in_array($name, static::$immutableParts, true)) {
112 1
            return array_fill_keys([static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ], S::name($name));
113
        }
114
115 39
        if (strpos($name, ' ') !== false) {
116 9
            $first_part = S::slice($name, 0, S::findFirstPosition($name, ' '));
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 can also be of type boolean; however, morphos\S::findFirstPosition() 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...
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 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...
117
            // город N, село N, хутор N, пгт N
118 9
            if (in_array($first_part, ['город', 'село', 'хутор', 'пгт', 'район', 'поселок', 'округ', 'республика'], true)) {
119 3
                if ($first_part !== 'пгт')
120 3
                    return static::composeCasesFromWords([
121 3
                        $first_part !== 'республика'
122 2
                            ? NounDeclension::getCases($first_part)
0 ignored issues
show
Security Bug introduced by
It seems like $first_part defined by \morphos\S::slice($name,...stPosition($name, ' ')) on line 116 can also be of type false; however, morphos\Russian\NounDeclension::getCases() 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...
123 3
                            : array_map(['\\morphos\\S', 'name'], NounDeclension::getCases($first_part)),
124 3
                        array_fill_keys(static::getAllCases(), S::name(S::slice($name, S::length($first_part) + 1)))
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 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...
125
                    ]);
126
                else
127
                    return array_fill_keys([static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ], 'пгт '.S::name(S::slice($name, 4)));
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 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...
128
            }
129
130 6
            $last_part = S::slice($name,
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 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...
131 6
                S::findLastPosition($name, ' ') + 1);
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 can also be of type boolean; however, morphos\S::findLastPosition() 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...
132
            // N область, N край
133 6
            if (in_array($last_part, ['край', 'область'], true)) {
134 2
                return static::composeCasesFromWords([static::getCases(S::slice($name, 0, S::findLastPosition($name, ' '))), NounDeclension::getCases($last_part)]);
0 ignored issues
show
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 can also be of type boolean; however, morphos\S::findLastPosition() 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...
Bug introduced by
It seems like $name defined by \morphos\S::lower($name) on line 108 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...
Security Bug introduced by
It seems like \morphos\S::slice($name,...stPosition($name, ' ')) targeting morphos\S::slice() can also be of type false; however, morphos\Russian\Geograph...sInflection::getCases() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $last_part defined by \morphos\S::slice($name,...sition($name, ' ') + 1) on line 130 can also be of type false; however, morphos\Russian\NounDeclension::getCases() 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...
135
            }
136
        }
137
138
        // Сложное название через пробел, '-' или '-на-'
139 36
        foreach (static::$delimiters as $delimiter) {
140 36
            if (strpos($name, $delimiter) !== false) {
141 6
                $parts = explode($delimiter, $name);
142 6
                $result = [];
143 6
                foreach ($parts as $i => $part) {
144 6
                    $result[$i] = static::getCases($part);
145
                }
146 36
                return static::composeCasesFromWords($result, $delimiter);
147
            }
148
        }
149
150
        // Исправление ошибок
151 36
        if (array_key_exists($name, static::$misspellings)) {
152
            $name = static::$misspellings[$name];
153
        }
154
155
        // Само склонение
156 36
        if (!in_array($name, static::$abbreviations, true)) {
157 34
            switch (S::slice($name, -2)) {
158
                // Нижний, Русский
159 34
                case 'ий':
160 3
                    $prefix = S::name(S::slice($name, 0, -2));
161
                    return [
162 3
                        static::IMENIT => $prefix.'ий',
163 3
                        static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'),
164 3
                        static::DAT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'),
165 3
                        static::VINIT => $prefix.'ий',
166 3
                        static::TVORIT => $prefix.'им',
167 3
                        static::PREDLOJ => $prefix.(static::chooseEndingBySonority($prefix, 'ем', 'ом')),
0 ignored issues
show
Bug introduced by
It seems like $prefix defined by \morphos\S::name(\morphos\S::slice($name, 0, -2)) on line 160 can also be of type boolean; however, morphos\Russian\RussianL...hooseEndingBySonority() 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...
168
                    ];
169
170
                // Ростовская
171 33
                case 'ая':
172 1
                    $prefix = S::name(S::slice($name, 0, -2));
173
                    return [
174 1
                        static::IMENIT => $prefix.'ая',
175 1
                        static::RODIT => $prefix.'ой',
176 1
                        static::DAT => $prefix.'ой',
177 1
                        static::VINIT => $prefix.'ую',
178 1
                        static::TVORIT => $prefix.'ой',
179 1
                        static::PREDLOJ => $prefix.'ой',
180
                    ];
181
182
                // Россошь
183 32
                case 'шь':
184 1
                    $prefix = S::name(S::slice($name, 0, -1));
185
                    return [
186 1
                        static::IMENIT => $prefix.'ь',
187 1
                        static::RODIT => $prefix.'и',
188 1
                        static::DAT => $prefix.'и',
189 1
                        static::VINIT => $prefix.'ь',
190 1
                        static::TVORIT => $prefix.'ью',
191 1
                        static::PREDLOJ => $prefix.'и',
192
                    ];
193
194
                // Грозный, Благодарный
195 31
                case 'ый':
196 2
                    $prefix = S::name(S::slice($name, 0, -2));
197
                    return [
198 2
                        static::IMENIT => $prefix.'ый',
199 2
                        static::RODIT => $prefix.'ого',
200 2
                        static::DAT => $prefix.'ому',
201 2
                        static::VINIT => $prefix.'ый',
202 2
                        static::TVORIT => $prefix.'ым',
203 2
                        static::PREDLOJ => $prefix.'ом',
204
                    ];
205
206
                // Ставрополь, Ярославль, Электросталь
207 29
                case 'ль':
208 2
                    $prefix = S::name(S::slice($name, 0, -1));
209
210 2 View Code Duplication
                    if ($name === 'электросталь')
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...
211
                        return [
212 1
                            static::IMENIT => $prefix.'ь',
213 1
                            static::RODIT => $prefix.'и',
214 1
                            static::DAT => $prefix.'и',
215 1
                            static::VINIT => $prefix.'ь',
216 1
                            static::TVORIT => $prefix.'ью',
217 1
                            static::PREDLOJ => $prefix.'и',
218
                        ];
219
220
                    return [
221 1
                        static::IMENIT => $prefix.'ь',
222 1
                        static::RODIT => $prefix.'я',
223 1
                        static::DAT => $prefix.'ю',
224 1
                        static::VINIT => $prefix.'ь',
225 1
                        static::TVORIT => $prefix.'ем',
226 1
                        static::PREDLOJ => $prefix.'е',
227
                    ];
228
229
                // Тверь, Анадырь
230 27
                case 'рь':
231 2
                    $prefix = S::name(S::slice($name, 0, -1));
232 2
                    $last_vowel = S::slice($prefix, -2, -1);
0 ignored issues
show
Bug introduced by
It seems like $prefix defined by \morphos\S::name(\morphos\S::slice($name, 0, -1)) on line 231 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...
233
                    return [
234 2
                        static::IMENIT => $prefix . 'ь',
235 2
                        static::RODIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'я'),
236 2
                        static::DAT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'ю'),
237 2
                        static::VINIT => $prefix . 'ь',
238 2
                        static::TVORIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'ью' : 'ем'),
239 2
                        static::PREDLOJ => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'е'),
240
                    ];
241
242
                // Березники, Ессентуки
243 25
                case 'ки':
244 2
                    $prefix = S::name(S::slice($name, 0, -1));
245
                    return [
246 2
                        static::IMENIT => $prefix . 'и',
247 2
                        static::RODIT => $name == 'луки' ? $prefix : $prefix . 'ов',
248 2
                        static::DAT => $prefix . 'ам',
249 2
                        static::VINIT => $prefix . 'и',
250 2
                        static::TVORIT => $prefix . 'ами',
251 2
                        static::PREDLOJ => $prefix . 'ах',
252
                    ];
253
254
                // Пермь, Кемь
255 24
                case 'мь':
256 1
                    $prefix = S::name(S::slice($name, 0, -1));
257
                    return [
258 1
                        static::IMENIT => $prefix . 'ь',
259 1
                        static::RODIT => $prefix . 'и',
260 1
                        static::DAT => $prefix . 'и',
261 1
                        static::VINIT => $prefix . 'ь',
262 1
                        static::TVORIT => $prefix . 'ью',
263 1
                        static::PREDLOJ => $prefix . 'и',
264
                    ];
265
266
                // Рязань, Назрань
267 23
                case 'нь':
268 1
                    $prefix = S::name(S::slice($name, 0, -1));
269
                    return [
270 1
                        static::IMENIT => $prefix . 'ь',
271 1
                        static::RODIT => $prefix . 'и',
272 1
                        static::DAT => $prefix . 'и',
273 1
                        static::VINIT => $prefix . 'ь',
274 1
                        static::TVORIT => $prefix . 'ью',
275 1
                        static::PREDLOJ => $prefix . 'и',
276
                    ];
277
278
                // Набережные
279 22
                case 'ые':
280 1
                    $prefix = S::name(S::slice($name, 0, -1));
281
                    return [
282 1
                        static::IMENIT => $prefix . 'е',
283 1
                        static::RODIT => $prefix . 'х',
284 1
                        static::DAT => $prefix . 'м',
285 1
                        static::VINIT => $prefix . 'е',
286 1
                        static::TVORIT => $prefix . 'ми',
287 1
                        static::PREDLOJ => $prefix . 'х',
288
                    ];
289
290
                // Челны
291 22
                case 'ны':
292 1
                    $prefix = S::name(S::slice($name, 0, -1));
293
                    return [
294 1
                        static::IMENIT => $prefix . 'ы',
295 1
                        static::RODIT => $prefix . 'ов',
296 1
                        static::DAT => $prefix . 'ам',
297 1
                        static::VINIT => $prefix . 'ы',
298 1
                        static::TVORIT => $prefix . 'ами',
299 1
                        static::PREDLOJ => $prefix . 'ах',
300
                    ];
301
302
                // Великие
303 21
                case 'ие':
304 1
                    $prefix = S::name(S::slice($name, 0, -1));
305
                    return [
306 1
                        static::IMENIT => $prefix.'е',
307 1
                        static::RODIT => $prefix.'х',
308 1
                        static::DAT => $prefix.'м',
309 1
                        static::VINIT => $prefix.'е',
310 1
                        static::TVORIT => $prefix.'ми',
311 1
                        static::PREDLOJ => $prefix.'х',
312
                    ];
313
314
                // Керчь
315 20
                case 'чь':
316 1
                    $prefix = S::name(S::slice($name, 0, -1));
317
                    return [
318 1
                        static::IMENIT => $prefix.'ь',
319 1
                        static::RODIT => $prefix.'и',
320 1
                        static::DAT => $prefix.'и',
321 1
                        static::VINIT => $prefix.'ь',
322 1
                        static::TVORIT => $prefix.'ью',
323 1
                        static::PREDLOJ => $prefix.'и',
324
                    ];
325
            }
326
327 19
            switch (S::slice($name, -1)) {
328 19
                case 'р':
329
                    // Бор
330
                    $prefix = S::name(S::slice($name, 0, -1));
331
                    return [
332
                        static::IMENIT => $prefix.'р',
333
                        static::RODIT => $prefix.'ра',
334
                        static::DAT => $prefix.'ру',
335
                        static::VINIT => $prefix.'р',
336
                        static::TVORIT => $prefix.'ром',
337
                        static::PREDLOJ => $prefix.'ру',
338
                    ];
339
340 19 View Code Duplication
                case 'ы':
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...
341
                    // Чебоксары, Шахты
342 1
                    $prefix = S::name(S::slice($name, 0, -1));
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
                    ];
351
352 18
                case 'я':
353
                    // Азия
354 1
                    $prefix = S::name(S::slice($name, 0, -1));
355
                    return [
356 1
                        static::IMENIT => S::name($name),
357 1
                        static::RODIT => $prefix.'и',
358 1
                        static::DAT => $prefix.'и',
359 1
                        static::VINIT => $prefix.'ю',
360 1
                        static::TVORIT => $prefix.'ей',
361 1
                        static::PREDLOJ => $prefix.'и',
362
                    ];
363
364 17 View Code Duplication
                case 'а':
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...
365
                    // Москва, Рига
366 5
                    $prefix = S::name(S::slice($name, 0, -1));
367
                    return [
368 5
                        static::IMENIT => $prefix.'а',
369 5
                        static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -2, -1)) ? 'и' : 'ы'),
370 5
                        static::DAT => $prefix.'е',
371 5
                        static::VINIT => $prefix.'у',
372 5
                        static::TVORIT => $prefix.'ой',
373 5
                        static::PREDLOJ => $prefix.'е',
374
                    ];
375
376 12
                case 'й':
377
                    // Ишимбай
378 2
                    $prefix = S::name(S::slice($name, 0, -1));
379
                    return [
380 2
                        static::IMENIT => $prefix . 'й',
381 2
                        static::RODIT => $prefix . 'я',
382 2
                        static::DAT => $prefix . 'ю',
383 2
                        static::VINIT => $prefix . 'й',
384 2
                        static::TVORIT => $prefix . 'ем',
385 2
                        static::PREDLOJ => $prefix . 'е',
386
                    ];
387
            }
388
389 10
            if (static::isConsonant(S::slice($name,  -1)) && !in_array($name, static::$ovAbnormalExceptions, true)) {
390 9
                $runaway_vowels_list = static::getRunAwayVowelsList();
391
392
                // if run-away vowel in name
393 9
                if (isset($runaway_vowels_list[$name])) {
394 3
                    $runaway_vowel_offset = $runaway_vowels_list[$name];
395 3
                    $prefix = S::name(S::slice($name, 0, $runaway_vowel_offset) . S::slice($name, $runaway_vowel_offset + 1));
396
                } else {
397 6
                    $prefix = S::name($name);
398
                }
399
400
                // Париж, Валаам, Киев
401
                return [
402 9
                    static::IMENIT => S::name($name),
403 9
                    static::RODIT => $prefix . 'а',
404 9
                    static::DAT => $prefix . 'у',
405 9
                    static::VINIT => S::name($name),
406 9
                    static::TVORIT => $prefix . (static::isVelarConsonant(S::slice($name, -2, -1)) ? 'ем' : 'ом'),
407 9
                    static::PREDLOJ => $prefix . 'е',
408
                ];
409
            }
410
411
            // ов, ово, ёв, ёво, ев, ево, ...
412 2
            $suffixes = ['ов', 'ёв', 'ев', 'ин', 'ын'];
413 2
            if ((in_array(S::slice($name, -1), ['е', 'о'], true) && in_array(S::slice($name, -3, -1), $suffixes, true)) || in_array(S::slice($name, -2), $suffixes, true)) {
414
                // ово, ёво, ...
415 1
                if (in_array(S::slice($name, -3, -1), $suffixes, true)) {
416
                    $prefix = S::name(S::slice($name, 0, -1));
417
                }
418
                // ов, её, ...
419 1
                elseif (in_array(S::slice($name, -2), $suffixes, true)) {
420 1
                    $prefix = S::name($name);
421
                }
422
                return [
423 1
                    static::IMENIT => S::name($name),
424 1
                    static::RODIT => $prefix.'а',
425 1
                    static::DAT => $prefix.'у',
426 1
                    static::VINIT => S::name($name),
427 1
                    static::TVORIT => $prefix.'ым',
428 1
                    static::PREDLOJ => $prefix.'е',
429
                ];
430
            }
431
        }
432
433
        // if no rules matches or name is immutable
434 3
        $name = in_array($name, static::$abbreviations, true) ? S::upper($name) : S::name($name);
435 3
        return array_fill_keys([static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ], $name);
436
    }
437
438
    /**
439
     * Получение одной формы (падежа) названия.
440
     * @param string $name  Название
441
     * @param integer $case Падеж. Одна из констант \morphos\Russian\Cases или \morphos\Cases.
442
     * @see \morphos\Russian\Cases
443
     * @return string
444
     * @throws \Exception
445
     */
446
    public static function getCase($name, $case)
447
    {
448
        $case = static::canonizeCase($case);
449
        $forms = static::getCases($name);
450
        return $forms[$case];
451
    }
452
}
453