Completed
Push — master ( 127b74...0b2caf )
by f
01:23
created

GeographicalNamesInflection::isMutable()   D

Complexity

Conditions 9
Paths 7

Size

Total Lines 38
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 15
nc 7
nop 1
dl 0
loc 38
rs 4.909
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 = array(
14
        'сша',
15
        'оаэ',
16
        'ссср',
17
        'юар',
18
    );
19
20
    protected static $delimiters = array(
21
        ' ',
22
        '-на-',
23
        '-',
24
    );
25
26
    protected static $ovAbnormalExceptions = [
27
        'осташков',
28
    ];
29
30
    protected static $immutableParts = [
31
        'санкт',
32
    ];
33
34
    /**
35
     * Проверяет, склоняемо ли название
36
     * @param string $name Название
37
     * @return bool
38
     */
39
    public static function isMutable($name)
40
    {
41
        $name = S::lower($name);
42
43
        // // ends with 'ы' or 'и': plural form
44
        // if (in_array(S::slice($name, -1), array('и', 'ы')))
45
        //     return false;
46
47
        if (in_array($name, self::$abbreviations) || in_array($name, self::$immutableParts)) {
48
            return false;
49
        }
50
51
        // N край
52
        if (S::slice($name, -5) == ' край') {
53
            return static::isMutable(S::slice($name, 0, -5));
0 ignored issues
show
Security Bug introduced by
It seems like \morphos\S::slice($name, 0, -5) targeting morphos\S::slice() can also be of type false; however, morphos\Russian\Geograph...Inflection::isMutable() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
54
        }
55
56
        // N область
57
        if (S::slice($name, -8) == ' область') {
58
            return true;
59
        }
60
61
        // город N
62
        if (S::slice($name, 0, 6) == 'город ') {
63
            return true;
64
        }
65
66
        // село N
67
        if (S::slice($name, 0, 5) == 'село ') {
68
            return true;
69
        }
70
71
        // ends with 'е' or 'о', but not with 'ово/ёво/ево/ино/ыно'
72
        if (in_array(S::slice($name, -1), array('е', 'о')) && !in_array(S::slice($name, -3, -1), array('ов', 'ёв', 'ев', 'ин', 'ын'))) {
73
            return false;
74
        }
75
        return true;
76
    }
77
78
    /**
79
     * Получение всех форм названия
80
     * @param string $name
81
     * @return array
82
     */
83
    public static function getCases($name)
84
    {
85
        $name = S::lower($name);
86
87
        if (in_array($name, self::$immutableParts)) {
88
            return array_fill_keys([self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT, self::PREDLOJ], S::name($name));
89
        }
90
91
        // N край
92 View Code Duplication
        if (S::slice($name, -5) == ' край') {
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...
93
            return self::composeCasesFromWords([static::getCases(S::slice($name, 0, -5)), NounDeclension::getCases('край')]);
0 ignored issues
show
Security Bug introduced by
It seems like \morphos\S::slice($name, 0, -5) 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...
94
        }
95
96
        // N область
97 View Code Duplication
        if (S::slice($name, -8) == ' область') {
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...
98
            return self::composeCasesFromWords([static::getCases(S::slice($name, 0, -8)), NounDeclension::getCases('область')]);
0 ignored issues
show
Security Bug introduced by
It seems like \morphos\S::slice($name, 0, -8) 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...
99
        }
100
101
        // город N
102 View Code Duplication
        if (S::slice($name, 0, 6) == 'город ') {
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...
103
            return self::composeCasesFromWords([
104
                NounDeclension::getCases('город'),
105
                array_fill_keys(self::getAllCases(), S::name(S::slice($name, 6)))
106
            ]);
107
        }
108
109
        // село N
110 View Code Duplication
        if (S::slice($name, 0, 5) == 'село ') {
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...
111
            return self::composeCasesFromWords([
112
                NounDeclension::getCases('село'),
113
                array_fill_keys(self::getAllCases(), S::name(S::slice($name, 5)))
114
            ]);
115
        }
116
117
        // Сложное название через пробел или через '-на-'
118
        foreach (self::$delimiters as $delimiter) {
119
            if (strpos($name, $delimiter) !== false) {
120
                $parts = explode($delimiter, $name);
121
                $result = array();
122
                foreach ($parts as $i => $part) {
123
                    $result[$i] = static::getCases($part);
124
                }
125
                return self::composeCasesFromWords($result, $delimiter);
126
            }
127
        }
128
129
        if (!in_array($name, self::$abbreviations)) {
130
            if (S::slice($name, -2) == 'ий') {
131
                // Нижний, Русский
132
                $prefix = S::name(S::slice($name, 0, -2));
133
                return array(
134
                    self::IMENIT => $prefix.'ий',
135
                    self::RODIT => $prefix.(self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'),
136
                    self::DAT => $prefix.(self::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'),
137
                    self::VINIT => $prefix.'ий',
138
                    self::TVORIT => $prefix.'им',
139
                    self::PREDLOJ => $prefix.(self::chooseEndingBySonority($prefix, 'ем', 'ом')),
0 ignored issues
show
Security Bug introduced by
It seems like $prefix defined by \morphos\S::name(\morphos\S::slice($name, 0, -2)) on line 132 can also be of type false; however, morphos\Russian\RussianL...hooseEndingBySonority() 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...
140
                );
141
            } else if (S::slice($name, -2) == 'ая') {
142
                // Ростовская
143
                $prefix = S::name(S::slice($name, 0, -2));
144
                return array(
145
                    self::IMENIT => $prefix.'ая',
146
                    self::RODIT => $prefix.'ой',
147
                    self::DAT => $prefix.'ой',
148
                    self::VINIT => $prefix.'ую',
149
                    self::TVORIT => $prefix.'ой',
150
                    self::PREDLOJ => $prefix.'ой',
151
                );
152
            } else if (S::slice($name, -2) == 'ый') {
153
                // Грозный, Благодарный
154
                $prefix = S::name(S::slice($name, 0, -2));
155
                return array(
156
                    self::IMENIT => $prefix.'ый',
157
                    self::RODIT => $prefix.'ого',
158
                    self::DAT => $prefix.'ому',
159
                    self::VINIT => $prefix.'ый',
160
                    self::TVORIT => $prefix.'ым',
161
                    self::PREDLOJ => $prefix.'ом',
162
                );
163 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...
164
                // Москва, Рига
165
                $prefix = S::name(S::slice($name, 0, -1));
166
                return array(
167
                    self::IMENIT => $prefix.'а',
168
                    self::RODIT => $prefix.(self::isVelarConsonant(S::slice($name, -2, -1)) ? 'и' : 'ы'),
169
                    self::DAT => $prefix.'е',
170
                    self::VINIT => $prefix.'у',
171
                    self::TVORIT => $prefix.'ой',
172
                    self::PREDLOJ => $prefix.'е',
173
                );
174
            } elseif (S::slice($name, -1) == 'я') {
175
                // Азия
176
                $prefix = S::name(S::slice($name, 0, -1));
177
                return array(
178
                    self::IMENIT => S::name($name),
179
                    self::RODIT => $prefix.'и',
180
                    self::DAT => $prefix.'и',
181
                    self::VINIT => $prefix.'ю',
182
                    self::TVORIT => $prefix.'ей',
183
                    self::PREDLOJ => $prefix.'и',
184
                );
185
            } elseif (S::slice($name, -1) == 'й') {
186
                // Ишимбай
187
                $prefix = S::name(S::slice($name, 0, -1));
188
                return array(
189
                    self::IMENIT => $prefix.'й',
190
                    self::RODIT => $prefix.'я',
191
                    self::DAT => $prefix.'ю',
192
                    self::VINIT => $prefix.'й',
193
                    self::TVORIT => $prefix.'ем',
194
                    self::PREDLOJ => $prefix.'е',
195
                );
196
            } elseif (self::isConsonant(S::slice($name, -1)) && !in_array($name, self::$ovAbnormalExceptions)) {
197
                // Париж, Валаам, Киев
198
                $prefix = S::name($name);
199
                return array(
200
                    self::IMENIT => $prefix,
201
                    self::RODIT => $prefix.'а',
202
                    self::DAT => $prefix.'у',
203
                    self::VINIT => $prefix,
204
                    self::TVORIT => $prefix.(self::isVelarConsonant(S::slice($name, -2, -1)) ? 'ем' : 'ом'),
205
                    self::PREDLOJ => $prefix.'е',
206
                );
207
            } elseif (S::slice($name, -2) == 'ль') {
208
                // Ставрополь, Ярославль
209
                $prefix = S::name(S::slice($name, 0, -1));
210
                return array(
211
                    self::IMENIT => $prefix.'ь',
212
                    self::RODIT => $prefix.'я',
213
                    self::DAT => $prefix.'ю',
214
                    self::VINIT => $prefix.'ь',
215
                    self::TVORIT => $prefix.'ем',
216
                    self::PREDLOJ => $prefix.'е',
217
                );
218
            } elseif (S::slice($name, -2) == 'рь') {
219
                // Тверь
220
                $prefix = S::name(S::slice($name, 0, -1));
221
                return array(
222
                    self::IMENIT => $prefix.'ь',
223
                    self::RODIT => $prefix.'и',
224
                    self::DAT => $prefix.'и',
225
                    self::VINIT => $prefix.'ь',
226
                    self::TVORIT => $prefix.'ью',
227
                    self::PREDLOJ => $prefix.'и',
228
                );
229
            } elseif (S::slice($name, -2) == 'ки') {
230
                // Березники, Ессентуки
231
                $prefix = S::name(S::slice($name, 0, -1));
232
                return array(
233
                    self::IMENIT => $prefix.'и',
234
                    self::RODIT => $name == 'луки' ? $prefix : $prefix.'ов',
235
                    self::DAT => $prefix.'ам',
236
                    self::VINIT => $prefix.'и',
237
                    self::TVORIT => $prefix.'ами',
238
                    self::PREDLOJ => $prefix.'ах',
239
                );
240
            } elseif (S::slice($name, -2) == 'мь') {
241
                // Пермь, Кемь
242
                $prefix = S::name(S::slice($name, 0, -1));
243
                return array(
244
                    self::IMENIT => $prefix.'ь',
245
                    self::RODIT => $prefix.'и',
246
                    self::DAT => $prefix.'и',
247
                    self::VINIT => $prefix.'ь',
248
                    self::TVORIT => $prefix.'ью',
249
                    self::PREDLOJ => $prefix.'и',
250
                );
251
            } elseif (S::slice($name, -2) == 'нь') {
252
                // Рязань, Назрань
253
                $prefix = S::name(S::slice($name, 0, -1));
254
                return array(
255
                    self::IMENIT => $prefix.'ь',
256
                    self::RODIT => $prefix.'и',
257
                    self::DAT => $prefix.'и',
258
                    self::VINIT => $prefix.'ь',
259
                    self::TVORIT => $prefix.'ью',
260
                    self::PREDLOJ => $prefix.'и',
261
                );
262
            } else if (S::slice($name, -2) == 'ые') {
263
                // Набережные
264
                $prefix = S::name(S::slice($name, 0, -1));
265
                return array(
266
                    self::IMENIT => $prefix.'е',
267
                    self::RODIT => $prefix.'х',
268
                    self::DAT => $prefix.'м',
269
                    self::VINIT => $prefix.'е',
270
                    self::TVORIT => $prefix.'ми',
271
                    self::PREDLOJ => $prefix.'х',
272
                );
273
            } else if (S::slice($name, -2) == 'ны') {
274
                // Челны
275
                $prefix = S::name(S::slice($name, 0, -1));
276
                return array(
277
                    self::IMENIT => $prefix.'ы',
278
                    self::RODIT => $prefix.'ов',
279
                    self::DAT => $prefix.'ам',
280
                    self::VINIT => $prefix.'ы',
281
                    self::TVORIT => $prefix.'ами',
282
                    self::PREDLOJ => $prefix.'ах',
283
                );
284
            } else if ($name == 'великие') {
285
                $prefix = 'Велики';
286
                return array(
287
                    self::IMENIT => $prefix.'е',
288
                    self::RODIT => $prefix.'х',
289
                    self::DAT => $prefix.'м',
290
                    self::VINIT => $prefix.'е',
291
                    self::TVORIT => $prefix.'ми',
292
                    self::PREDLOJ => $prefix.'х',
293
                );
294
            }
295
296
            $suffixes = array('ов', 'ёв', 'ев', 'ин', 'ын');
297
            if ((in_array(S::slice($name, -1), array('е', 'о')) && in_array(S::slice($name, -3, -1), $suffixes)) || in_array(S::slice($name, -2), $suffixes)) {
298
                // ово, ёво, ...
299
                if (in_array(S::slice($name, -3, -1), $suffixes)) {
300
                    $prefix = S::name(S::slice($name, 0, -1));
301
                }
302
                // ов, её, ...
303
                elseif (in_array(S::slice($name, -2), $suffixes)) {
304
                    $prefix = S::name($name);
305
                }
306
                return array(
307
                    self::IMENIT => S::name($name),
308
                    self::RODIT => $prefix.'а',
309
                    self::DAT => $prefix.'у',
310
                    self::VINIT => S::name($name),
311
                    self::TVORIT => $prefix.'ым',
312
                    self::PREDLOJ => $prefix.'е',
313
                );
314
            }
315
        }
316
317
        // if no rules matches or name is immutable
318
        $name = in_array($name, self::$abbreviations) ? S::upper($name) : S::name($name);
319
        return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT, self::PREDLOJ), $name);
320
    }
321
322
    /**
323
     * Получение одной формы (падежа) названия.
324
     * @param string $name Название
325
     * @param integer $case Падеж. Одна из констант \morphos\Russian\Cases или \morphos\Cases.
326
     * @see \morphos\Russian\Cases
327
     * @return mixed
328
     */
329
    public static function getCase($name, $case)
330
    {
331
        $case = self::canonizeCase($case);
332
        $forms = self::getCases($name);
333
        return $forms[$case];
334
    }
335
}
336