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 string[] */ |
||
14 | protected static $abbreviations = [ |
||
15 | 'сша', |
||
16 | 'оаэ', |
||
17 | 'ссср', |
||
18 | 'юар', |
||
19 | ]; |
||
20 | |||
21 | /** @var string[] */ |
||
22 | protected static $delimiters = [ |
||
23 | ' ', |
||
24 | '-на-', |
||
25 | '-эль-', |
||
26 | '-де-', |
||
27 | '-сюр-', |
||
28 | '-ан-', |
||
29 | '-ла-', |
||
30 | '-', |
||
31 | ]; |
||
32 | |||
33 | /** @var string[] */ |
||
34 | protected static $ovAbnormalExceptions = [ |
||
35 | 'осташков', |
||
36 | ]; |
||
37 | |||
38 | /** @var string[] */ |
||
39 | protected static $immutableNames = [ |
||
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 | /** @var string[] */ |
||
72 | protected static $runawayVowelsExceptions = [ |
||
73 | 'торжо*к', |
||
74 | 'волоче*к', |
||
75 | 'орё*л', |
||
76 | 'египе*т', |
||
77 | 'лунине*ц', |
||
78 | 'городо*к', |
||
79 | 'новогрудо*к', |
||
80 | 'острове*ц', |
||
81 | 'черепове*ц', |
||
82 | ]; |
||
83 | |||
84 | /** |
||
85 | * @var string[] |
||
86 | * @phpstan-var array<string, string> |
||
87 | */ |
||
88 | protected static $misspellings = [ |
||
89 | 'орел' => 'орёл', |
||
90 | 'рублево' => 'рублёво', |
||
91 | ]; |
||
92 | |||
93 | /** |
||
94 | * @return int[]|false[] |
||
95 | */ |
||
96 | 12 | View Code Duplication | protected static function getRunAwayVowelsList() |
97 | { |
||
98 | 12 | $runawayVowelsNormalized = []; |
|
99 | 12 | foreach (static::$runawayVowelsExceptions as $word) { |
|
100 | 12 | $runawayVowelsNormalized[str_replace('*', '', $word)] = S::indexOf($word, '*') - 1; |
|
101 | } |
||
102 | 12 | return $runawayVowelsNormalized; |
|
103 | } |
||
104 | |||
105 | /** |
||
106 | * Проверяет, склоняемо ли название |
||
107 | * @param string $name Название |
||
108 | * @return bool |
||
109 | */ |
||
110 | 2 | public static function isMutable($name) |
|
111 | { |
||
112 | 2 | $name = S::lower($name); |
|
113 | |||
114 | // // ends with 'ы' or 'и': plural form |
||
115 | // if (in_array(S::slice($name, -1), array('и', 'ы'))) |
||
116 | // return false; |
||
117 | |||
118 | 2 | if (in_array($name, static::$abbreviations, true) || in_array($name, static::$immutableNames, true)) { |
|
119 | 2 | return false; |
|
120 | } |
||
121 | |||
122 | if (strpos($name, ' ') !== false) { |
||
123 | // explode() is not applicable because Geographical unit may have few words |
||
124 | $first_part = S::slice($name, 0, S::findFirstPosition($name, ' ')); |
||
125 | $last_part = S::slice($name, |
||
126 | S::findLastPosition($name, ' ') + 1); |
||
127 | |||
128 | // город N, село N, хутор N, район N, поселок N, округ N, республика N |
||
129 | // N область, N край, N район, N волость |
||
130 | if (in_array($first_part, ['город', 'село', 'хутор', 'район', 'поселок', 'округ', 'республика'], true) |
||
131 | || in_array($last_part, ['край', 'область', 'район', 'волость'], true)) { |
||
132 | return true; |
||
133 | } |
||
134 | |||
135 | // пгт N |
||
136 | if ($first_part === 'пгт') |
||
137 | return false; |
||
138 | } |
||
139 | |||
140 | // ends with 'е' or 'о', but not with 'ово/ёво/ево/ино/ыно' |
||
141 | if (in_array(S::slice($name, -1), ['е', 'о'], true) |
||
142 | && !in_array(S::slice($name, -3, -1), ['ов', 'ёв', 'ев', 'ин', 'ын'], true)) { |
||
143 | return false; |
||
144 | } |
||
145 | return true; |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Получение всех форм названия |
||
150 | * @param string $name |
||
151 | * @return string[] |
||
152 | * @phpstan-return array<string, string> |
||
153 | * @throws \Exception |
||
154 | */ |
||
155 | 45 | public static function getCases($name) |
|
156 | { |
||
157 | 45 | $name = S::lower($name); |
|
158 | |||
159 | // Проверка на неизменяемость и сложное название |
||
160 | 45 | if (in_array($name, static::$immutableNames, true)) { |
|
161 | 2 | return array_fill_keys( |
|
162 | 2 | [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE] |
|
163 | 2 | , S::name($name)); |
|
164 | } |
||
165 | |||
166 | 45 | if (strpos($name, ' ') !== false) { |
|
167 | 9 | $first_part = S::slice($name, 0, S::findFirstPosition($name, ' ')); |
|
168 | // город N, село N, хутор N, пгт N |
||
169 | 9 | if (in_array($first_part, ['город', 'село', 'хутор', 'пгт', 'район', 'поселок', 'округ', 'республика'], true)) { |
|
170 | 3 | if ($first_part === 'пгт') |
|
171 | return array_fill_keys( |
||
172 | [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE], |
||
173 | 'пгт '.S::name(S::slice($name, 4))); |
||
174 | |||
175 | 3 | if ($first_part === 'республика') { |
|
176 | 1 | $prefix = array_map(['\\morphos\\S', 'name'], NounDeclension::getCases($first_part)); |
|
177 | } else { |
||
178 | 2 | $prefix = NounDeclension::getCases($first_part); |
|
179 | } |
||
180 | 3 | $prefix[Cases::LOCATIVE] = $prefix[Cases::PREDLOJ]; |
|
181 | |||
182 | 3 | return static::composeCasesFromWords([$prefix, |
|
183 | 3 | array_fill_keys( |
|
184 | 3 | array_merge(static::getAllCases(), [\morphos\Russian\Cases::LOCATIVE]), |
|
185 | 3 | S::name(S::slice($name, S::length($first_part) + 1))) |
|
186 | ]); |
||
187 | } |
||
188 | |||
189 | 6 | $last_part = S::slice($name, |
|
190 | 6 | S::findLastPosition($name, ' ') + 1); |
|
191 | // N область, N край |
||
192 | 6 | if (in_array($last_part, ['край', 'область', 'район', 'волость'], true)) { |
|
193 | 2 | $last_part_cases = NounDeclension::getCases($last_part); |
|
194 | 2 | $last_part_cases[Cases::LOCATIVE] = $last_part_cases[Cases::PREDLOJ]; |
|
195 | 2 | return static::composeCasesFromWords( |
|
196 | [ |
||
197 | 2 | static::getCases(S::slice($name, 0, S::findLastPosition($name, ' '))), |
|
198 | 2 | $last_part_cases, |
|
199 | ]); |
||
200 | } |
||
201 | } |
||
202 | |||
203 | // Сложное название с разделителем |
||
204 | 42 | foreach (static::$delimiters as $delimiter) { |
|
205 | 42 | if (strpos($name, $delimiter) !== false) { |
|
206 | 7 | $parts = explode($delimiter, $name); |
|
207 | 7 | $result = []; |
|
208 | 7 | foreach ($parts as $i => $part) { |
|
209 | 7 | $result[$i] = static::getCases($part); |
|
210 | } |
||
211 | 42 | return static::composeCasesFromWords($result, $delimiter); |
|
212 | } |
||
213 | } |
||
214 | |||
215 | // Исправление ошибок |
||
216 | 42 | if (array_key_exists($name, static::$misspellings)) { |
|
217 | $name = static::$misspellings[$name]; |
||
218 | } |
||
219 | |||
220 | // Само склонение |
||
221 | 42 | if (!in_array($name, static::$abbreviations, true)) { |
|
222 | 40 | switch (S::slice($name, -2)) { |
|
223 | // Нижний, Русский |
||
224 | 40 | case 'ий': |
|
225 | 3 | $prefix = S::name(S::slice($name, 0, -2)); |
|
226 | return [ |
||
227 | 3 | static::IMENIT => $prefix.'ий', |
|
228 | 3 | static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ого' : 'его'), |
|
229 | 3 | static::DAT => $prefix.(static::isVelarConsonant(S::slice($name, -3, -2)) ? 'ому' : 'ему'), |
|
230 | 3 | static::VINIT => $prefix.'ий', |
|
231 | 3 | static::TVORIT => $prefix.'им', |
|
232 | 3 | static::PREDLOJ => $prefix.(static::chooseEndingBySonority($prefix, 'ем', 'ом')), |
|
233 | 3 | static::LOCATIVE => $prefix.(static::chooseEndingBySonority($prefix, 'ем', 'ом')), |
|
234 | ]; |
||
235 | |||
236 | // Ростовская |
||
237 | 39 | case 'ая': |
|
238 | 1 | $prefix = S::name(S::slice($name, 0, -2)); |
|
239 | return [ |
||
240 | 1 | static::IMENIT => $prefix.'ая', |
|
241 | 1 | static::RODIT => $prefix.'ой', |
|
242 | 1 | static::DAT => $prefix.'ой', |
|
243 | 1 | static::VINIT => $prefix.'ую', |
|
244 | 1 | static::TVORIT => $prefix.'ой', |
|
245 | 1 | static::PREDLOJ => $prefix.'ой', |
|
246 | 1 | static::LOCATIVE => $prefix.'ой', |
|
247 | ]; |
||
248 | |||
249 | // Нижняя, Верхняя, Средняя |
||
250 | 38 | case 'яя': |
|
251 | $prefix = S::name(S::slice($name, 0, -2)); |
||
252 | return [ |
||
253 | static::IMENIT => $prefix.'яя', |
||
254 | static::RODIT => $prefix.'ей', |
||
255 | static::DAT => $prefix.'ей', |
||
256 | static::VINIT => $prefix.'юю', |
||
257 | static::TVORIT => $prefix.'ей', |
||
258 | static::PREDLOJ => $prefix.'ей', |
||
259 | static::LOCATIVE => $prefix.'ей', |
||
260 | ]; |
||
261 | |||
262 | // Россошь |
||
263 | 38 | case 'шь': |
|
264 | // Пермь, Кемь |
||
265 | 37 | case 'мь': |
|
266 | // Рязань, Назрань |
||
267 | 36 | case 'нь': |
|
268 | // Сысерть |
||
269 | 35 | case 'ть': |
|
270 | // Керчь |
||
271 | 35 | case 'чь': |
|
272 | 4 | $prefix = S::name(S::slice($name, 0, -1)); |
|
273 | return [ |
||
274 | 4 | static::IMENIT => $prefix.'ь', |
|
275 | 4 | static::RODIT => $prefix.'и', |
|
276 | 4 | static::DAT => $prefix.'и', |
|
277 | 4 | static::VINIT => $prefix.'ь', |
|
278 | 4 | static::TVORIT => $prefix.'ью', |
|
279 | 4 | static::PREDLOJ => $prefix.'и', |
|
280 | 4 | static::LOCATIVE => $prefix.'и', |
|
281 | ]; |
||
282 | |||
283 | // Грозный, Благодарный |
||
284 | 34 | case 'ый': |
|
285 | 2 | $prefix = S::name(S::slice($name, 0, -2)); |
|
286 | return [ |
||
287 | 2 | static::IMENIT => $prefix.'ый', |
|
288 | 2 | static::RODIT => $prefix.'ого', |
|
289 | 2 | static::DAT => $prefix.'ому', |
|
290 | 2 | static::VINIT => $prefix.'ый', |
|
291 | 2 | static::TVORIT => $prefix.'ым', |
|
292 | 2 | static::PREDLOJ => $prefix.'ом', |
|
293 | 2 | static::LOCATIVE => $prefix.'ом', |
|
294 | ]; |
||
295 | |||
296 | // Ставрополь, Ярославль, Электросталь |
||
297 | 32 | case 'ль': |
|
298 | 2 | $prefix = S::name(S::slice($name, 0, -1)); |
|
299 | |||
300 | 2 | if ($name === 'электросталь') |
|
301 | return [ |
||
302 | 1 | static::IMENIT => $prefix.'ь', |
|
303 | 1 | static::RODIT => $prefix.'и', |
|
304 | 1 | static::DAT => $prefix.'и', |
|
305 | 1 | static::VINIT => $prefix.'ь', |
|
306 | 1 | static::TVORIT => $prefix.'ью', |
|
307 | 1 | static::PREDLOJ => $prefix.'и', |
|
308 | 1 | static::LOCATIVE => $prefix.'и', |
|
309 | ]; |
||
310 | |||
311 | return [ |
||
312 | 1 | static::IMENIT => $prefix.'ь', |
|
313 | 1 | static::RODIT => $prefix.'я', |
|
314 | 1 | static::DAT => $prefix.'ю', |
|
315 | 1 | static::VINIT => $prefix.'ь', |
|
316 | 1 | static::TVORIT => $prefix.'ем', |
|
317 | 1 | static::PREDLOJ => $prefix.'е', |
|
318 | 1 | static::LOCATIVE => $prefix.'е', |
|
319 | ]; |
||
320 | |||
321 | // Тверь, Анадырь |
||
322 | 30 | case 'рь': |
|
323 | 2 | $prefix = S::name(S::slice($name, 0, -1)); |
|
324 | 2 | $last_vowel = S::slice($prefix, -2, -1); |
|
325 | return [ |
||
326 | 2 | static::IMENIT => $prefix . 'ь', |
|
327 | 2 | static::RODIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'я'), |
|
328 | 2 | static::DAT => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'ю'), |
|
329 | 2 | static::VINIT => $prefix . 'ь', |
|
330 | 2 | static::TVORIT => $prefix . (static::isBinaryVowel($last_vowel) ? 'ью' : 'ем'), |
|
331 | 2 | static::PREDLOJ => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'е'), |
|
332 | 2 | static::LOCATIVE => $prefix . (static::isBinaryVowel($last_vowel) ? 'и' : 'е'), |
|
333 | ]; |
||
334 | |||
335 | // Березники, Ессентуки |
||
336 | 28 | case 'ки': |
|
337 | // Старые Дороги |
||
338 | 27 | case 'ги': |
|
339 | // Ушачи, Ивацевичи |
||
340 | 27 | case 'чи': |
|
341 | 3 | $prefix = S::name(S::slice($name, 0, -1)); |
|
342 | return [ |
||
343 | 3 | static::IMENIT => $prefix . 'и', |
|
344 | 3 | static::RODIT => ($name === 'луки' |
|
345 | 1 | ? $prefix |
|
346 | 2 | : (S::slice($name, -2) === 'чи' |
|
347 | 1 | ? $prefix . 'ей' |
|
348 | 3 | : $prefix . 'ов')), |
|
349 | 3 | static::DAT => $prefix . 'ам', |
|
350 | 3 | static::VINIT => $prefix . 'и', |
|
351 | 3 | static::TVORIT => $prefix . 'ами', |
|
352 | 3 | static::PREDLOJ => $prefix . 'ах', |
|
353 | 3 | static::LOCATIVE => $prefix . 'ах', |
|
354 | ]; |
||
355 | |||
356 | // Набережные |
||
357 | 26 | case 'ые': |
|
358 | // Великие |
||
359 | 26 | case 'ие': |
|
360 | 2 | $prefix = S::name(S::slice($name, 0, -1)); |
|
361 | return [ |
||
362 | 2 | static::IMENIT => $prefix . 'е', |
|
363 | 2 | static::RODIT => $prefix . 'х', |
|
364 | 2 | static::DAT => $prefix . 'м', |
|
365 | 2 | static::VINIT => $prefix . 'е', |
|
366 | 2 | static::TVORIT => $prefix . 'ми', |
|
367 | 2 | static::PREDLOJ => $prefix . 'х', |
|
368 | 2 | static::LOCATIVE => $prefix . 'х', |
|
369 | ]; |
||
370 | |||
371 | // Челны |
||
372 | 25 | case 'ны': |
|
373 | // Мосты |
||
374 | 24 | case 'ты': |
|
375 | // Столбцы |
||
376 | 24 | case 'цы': |
|
377 | 2 | $prefix = S::name(S::slice($name, 0, -1)); |
|
378 | return [ |
||
379 | 2 | static::IMENIT => $prefix . 'ы', |
|
380 | 2 | static::RODIT => $prefix . 'ов', |
|
381 | 2 | static::DAT => $prefix . 'ам', |
|
382 | 2 | static::VINIT => $prefix . 'ы', |
|
383 | 2 | static::TVORIT => $prefix . 'ами', |
|
384 | 2 | static::PREDLOJ => $prefix . 'ах', |
|
385 | 2 | static::LOCATIVE => $prefix . 'ах', |
|
386 | ]; |
||
387 | |||
388 | // Глубокое |
||
389 | 23 | case 'ое': |
|
390 | 1 | $prefix = S::name(S::slice($name, 0, -2)); |
|
391 | return [ |
||
392 | 1 | static::IMENIT => $prefix.'ое', |
|
393 | 1 | static::RODIT => $prefix.'ого', |
|
394 | 1 | static::DAT => $prefix.'ому', |
|
395 | 1 | static::VINIT => $prefix.'ое', |
|
396 | 1 | static::TVORIT => $prefix.'им', |
|
397 | 1 | static::PREDLOJ => $prefix.'ом', |
|
398 | 1 | static::LOCATIVE => $prefix.'ом', |
|
399 | ]; |
||
400 | |||
401 | } |
||
402 | |||
403 | 22 | switch (S::slice($name, -1)) { |
|
404 | 22 | case 'р': |
|
405 | // Бор |
||
406 | $prefix = S::name(S::slice($name, 0, -1)); |
||
407 | return [ |
||
408 | static::IMENIT => $prefix.'р', |
||
409 | static::RODIT => $prefix.'ра', |
||
410 | static::DAT => $prefix.'ру', |
||
411 | static::VINIT => $prefix.'р', |
||
412 | static::TVORIT => $prefix.'ром', |
||
413 | static::PREDLOJ => $prefix.'ре', |
||
414 | static::LOCATIVE => $prefix.'ру', |
||
415 | ]; |
||
416 | |||
417 | 22 | case 'ы': |
|
418 | // Чебоксары, Шахты |
||
419 | 1 | $prefix = S::name(S::slice($name, 0, -1)); |
|
420 | return [ |
||
421 | 1 | static::IMENIT => $prefix.'ы', |
|
422 | 1 | static::RODIT => $prefix, |
|
423 | 1 | static::DAT => $prefix.'ам', |
|
424 | 1 | static::VINIT => $prefix.'ы', |
|
425 | 1 | static::TVORIT => $prefix.'ами', |
|
426 | 1 | static::PREDLOJ => $prefix.'ах', |
|
427 | 1 | static::LOCATIVE => $prefix.'ах', |
|
428 | ]; |
||
429 | |||
430 | 21 | case 'я': |
|
431 | // Азия |
||
432 | 1 | $prefix = S::name(S::slice($name, 0, -1)); |
|
433 | return [ |
||
434 | 1 | static::IMENIT => S::name($name), |
|
435 | 1 | static::RODIT => $prefix.'и', |
|
436 | 1 | static::DAT => $prefix.'и', |
|
437 | 1 | static::VINIT => $prefix.'ю', |
|
438 | 1 | static::TVORIT => $prefix.'ей', |
|
439 | 1 | static::PREDLOJ => $prefix.'и', |
|
440 | 1 | static::LOCATIVE => $prefix.'и', |
|
441 | ]; |
||
442 | |||
443 | 20 | case 'а': |
|
444 | // Москва, Рига |
||
445 | 5 | $prefix = S::name(S::slice($name, 0, -1)); |
|
446 | return [ |
||
447 | 5 | static::IMENIT => $prefix.'а', |
|
448 | 5 | static::RODIT => $prefix.(static::isVelarConsonant(S::slice($name, -2, -1)) || static::isHissingConsonant(S::slice($name, -2, -1)) ? 'и' : 'ы'), |
|
449 | 5 | static::DAT => $prefix.'е', |
|
450 | 5 | static::VINIT => $prefix.'у', |
|
451 | 5 | static::TVORIT => $prefix.'ой', |
|
452 | 5 | static::PREDLOJ => $prefix.'е', |
|
453 | 5 | static::LOCATIVE => $prefix.'е', |
|
454 | ]; |
||
455 | |||
456 | 15 | case 'й': |
|
457 | // Ишимбай |
||
458 | 2 | $prefix = S::name(S::slice($name, 0, -1)); |
|
459 | return [ |
||
460 | 2 | static::IMENIT => $prefix . 'й', |
|
461 | 2 | static::RODIT => $prefix . 'я', |
|
462 | 2 | static::DAT => $prefix . 'ю', |
|
463 | 2 | static::VINIT => $prefix . 'й', |
|
464 | 2 | static::TVORIT => $prefix . 'ем', |
|
465 | 2 | static::PREDLOJ => $prefix . 'е', |
|
466 | 2 | static::LOCATIVE => $prefix . 'е', |
|
467 | ]; |
||
468 | } |
||
469 | |||
470 | 13 | if (static::isConsonant(S::slice($name, -1)) && !in_array($name, static::$ovAbnormalExceptions, true)) { |
|
471 | 12 | $runaway_vowels_list = static::getRunAwayVowelsList(); |
|
472 | |||
473 | // if run-away vowel in name |
||
474 | 12 | if (isset($runaway_vowels_list[$name])) { |
|
475 | 4 | $runaway_vowel_offset = $runaway_vowels_list[$name]; |
|
476 | 4 | $prefix = S::name(S::slice($name, 0, $runaway_vowel_offset) . S::slice($name, $runaway_vowel_offset + 1)); |
|
477 | } else { |
||
478 | 8 | $prefix = S::name($name); |
|
479 | } |
||
480 | |||
481 | // Париж, Валаам, Киев |
||
482 | return [ |
||
0 ignored issues
–
show
|
|||
483 | 12 | static::IMENIT => S::name($name), |
|
484 | 12 | static::RODIT => $prefix . 'а', |
|
485 | 12 | static::DAT => $prefix . 'у', |
|
486 | 12 | static::VINIT => S::name($name), |
|
487 | 12 | static::TVORIT => $prefix . (static::isVelarConsonant(S::slice($name, -2, -1)) ? 'ем' : 'ом'), |
|
488 | 12 | static::PREDLOJ => $prefix . 'е', |
|
489 | 12 | static::LOCATIVE => $prefix.($name === 'крым' ? 'у' : 'е'), |
|
490 | ]; |
||
491 | } |
||
492 | |||
493 | // ов, ово, ёв, ёво, ев, ево, ... |
||
494 | 2 | $suffixes = ['ов', 'ёв', 'ев', 'ин', 'ын']; |
|
495 | 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)) { |
|
496 | // ово, ёво, ... |
||
497 | 1 | if (in_array(S::slice($name, -3, -1), $suffixes, true)) { |
|
498 | $prefix = S::name(S::slice($name, 0, -1)); |
||
499 | } |
||
500 | // ов, её, ... |
||
501 | 1 | elseif (in_array(S::slice($name, -2), $suffixes, true)) { |
|
502 | 1 | $prefix = S::name($name); |
|
503 | } else { |
||
504 | $prefix = ''; |
||
505 | } |
||
506 | |||
507 | return [ |
||
508 | 1 | static::IMENIT => S::name($name), |
|
509 | 1 | static::RODIT => $prefix.'а', |
|
510 | 1 | static::DAT => $prefix.'у', |
|
511 | 1 | static::VINIT => S::name($name), |
|
512 | 1 | static::TVORIT => $prefix.'ым', |
|
513 | 1 | static::PREDLOJ => $prefix.'е', |
|
514 | 1 | static::LOCATIVE => $prefix.'е', |
|
515 | ]; |
||
516 | } |
||
517 | } |
||
518 | |||
519 | // if no rules matches or name is immutable |
||
520 | 3 | $name = in_array($name, static::$abbreviations, true) ? S::upper($name) : S::name($name); |
|
521 | 3 | return array_fill_keys( |
|
522 | 3 | [static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ, static::LOCATIVE], |
|
523 | 3 | $name); |
|
524 | } |
||
525 | |||
526 | /** |
||
527 | * Получение одной формы (падежа) названия. |
||
528 | * @param string $name Название |
||
529 | * @param string $case Падеж. Одна из констант {@see \morphos\Russian\Cases} или {@see \morphos\Cases}. |
||
530 | * @see \morphos\Russian\Cases |
||
531 | * @return string |
||
532 | * @throws \Exception |
||
533 | */ |
||
534 | public static function getCase($name, $case) |
||
535 | { |
||
536 | $case = static::canonizeCase($case); |
||
537 | $forms = static::getCases($name); |
||
538 | return $forms[$case]; |
||
539 | } |
||
540 | } |
||
541 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.