RussianLanguage   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 353
Duplicated Lines 6.52 %

Coupling/Cohesion

Components 4
Dependencies 1

Test Coverage

Coverage 81.39%

Importance

Changes 0
Metric Value
dl 23
loc 353
ccs 70
cts 86
cp 0.8139
rs 8.48
c 0
b 0
f 0
wmc 49
lcom 4
cbo 1

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getVowels() 0 4 1
A isVowel() 0 4 1
A isConsonant() 0 4 1
A isSonorousConsonant() 0 4 1
A isDeafConsonant() 0 4 1
A isHissingConsonant() 0 4 1
A isVelarConsonant() 0 4 1
A countSyllables() 0 4 1
A isPairedConsonant() 0 5 2
A checkLastConsonantSoftness() 7 11 5
A checkBaseLastConsonantSoftness() 7 15 5
A isBinaryVowel() 0 4 1
A choosePrepositionByFirstLetter() 5 8 2
A verb() 0 16 6
A in() 2 7 2
A with() 0 7 4
A about() 2 11 4
A chooseEndingBySonority() 0 10 3
A isAdjectiveNoun() 0 5 2
A getVinitCaseByAnimateness() 0 8 2
A chooseVowelAfterConsonant() 0 8 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RussianLanguage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RussianLanguage, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace morphos\Russian;
3
4
use morphos\Gender;
5
use morphos\S;
6
7
trait RussianLanguage
8
{
9
    /**
10
     * @var array Все гласные
11
     */
12
    public static $vowels = [
13
        'а',
14
        'е',
15
        'ё',
16
        'и',
17
        'о',
18
        'у',
19
        'ы',
20
        'э',
21
        'ю',
22
        'я',
23
    ];
24
25
    /**
26
     * @var array Все согласные
27
     */
28
    public static $consonants = [
29
        'б',
30
        'в',
31
        'г',
32
        'д',
33
        'ж',
34
        'з',
35
        'й',
36
        'к',
37
        'л',
38
        'м',
39
        'н',
40
        'п',
41
        'р',
42
        'с',
43
        'т',
44
        'ф',
45
        'х',
46
        'ц',
47
        'ч',
48
        'ш',
49
        'щ',
50
    ];
51
52
    /**
53
     * @var array Пары согласных
54
     */
55
    public static $pairs = [
56
        'б' => 'п',
57
        'в' => 'ф',
58
        'г' => 'к',
59
        'д' => 'т',
60
        'ж' => 'ш',
61
        'з' => 'с',
62
    ];
63
64
    /**
65
     * @var array Звонкие согласные
66
     */
67
    public static $sonorousConsonants = ['б', 'в', 'г', 'д', 'з', 'ж', 'л', 'м', 'н', 'р'];
68
    /**
69
     * @var array Глухие согласные
70
     */
71
    public static $deafConsonants = ['п', 'ф', 'к', 'т', 'с', 'ш', 'х', 'ч', 'щ'];
72
73
    /**
74
     * @var array Союзы
75
     */
76
    public static $unions = ['и', 'или'];
77
78
    /**
79
     * @return array
80
     */
81
    public static function getVowels()
82
    {
83
        return self::$vowels;
84
    }
85
86
    /**
87
     * Проверка гласной
88
     * @param $char
89
     * @return bool
90
     */
91 351
    public static function isVowel($char)
92
    {
93 351
        return in_array($char, static::$vowels, true);
94
    }
95
96
    /**
97
     * Проверка согласной
98
     * @param $char
99
     * @return bool
100
     */
101 222
    public static function isConsonant($char)
102
    {
103 222
        return in_array($char, static::$consonants, true);
104
    }
105
106
    /**
107
     * Проверка звонкости согласной
108
     */
109 3
    public static function isSonorousConsonant($char)
110
    {
111 3
        return in_array($char, static::$sonorousConsonants, true);
112
    }
113
114
    /**
115
     * Проверка глухости согласной
116
     * @param $char
117
     * @return bool
118
     */
119 6
    public static function isDeafConsonant($char)
120
    {
121 6
        return in_array($char, static::$deafConsonants, true);
122
    }
123
124
    /**
125
     * Щипящая ли согласная
126
     * @param $consonant
127
     * @return bool
128
     */
129 426
    public static function isHissingConsonant($consonant)
130
    {
131 426
        return in_array(S::lower($consonant), ['ж', 'ш', 'ч', 'щ'], true);
132
    }
133
134
    /**
135
     * Проверка на велярность согласной
136
     * @param string[1] $consonant
0 ignored issues
show
Documentation introduced by wapmorgan
The doc-type string[1] could not be parsed: Expected "]" at position 2, but found "1". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
137
     * @return bool
138
     */
139 18
    protected static function isVelarConsonant($consonant)
140
    {
141 18
        return in_array(S::lower($consonant), ['г', 'к', 'х'], true);
142
    }
143
144
    /**
145
     * Подсчет слогов
146
     * @param $string
147
     * @return bool|int
148
     */
149
    public static function countSyllables($string)
150
    {
151
        return S::countChars($string, static::$vowels);
152
    }
153
154
    /**
155
     * Проверка парности согласной
156
     *
157
     * @param $consonant
158
     * @return bool
159
     */
160
    public static function isPairedConsonant($consonant)
161
    {
162
        $consonant = S::lower($consonant);
163
        return array_key_exists($consonant, static::$pairs) || (array_search($consonant, static::$pairs) !== false);
164
    }
165
166
    /**
167
     * Проверка мягкости последней согласной
168
     * @param $word
169
     * @return bool
170
     */
171 48
    public static function checkLastConsonantSoftness($word)
172
    {
173 48 View Code Duplication
        if (($substring = S::findLastPositionForOneOfChars(S::lower($word), static::$consonants)) !== false) {
0 ignored issues
show
Duplication introduced by wapmorgan
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...
174 48
            if (in_array(S::slice($substring, 0, 1), ['й', 'ч', 'щ', 'ш'], true)) { // always soft consonants
175 14
                return true;
176 34
            } elseif (S::length($substring) > 1 && in_array(S::slice($substring, 1, 2), ['е', 'ё', 'и', 'ю', 'я', 'ь'], true)) { // consonants are soft if they are trailed with these vowels
177 3
                return true;
178
            }
179
        }
180 31
        return false;
181
    }
182
183
    /**
184
     * Проверка мягкости последней согласной, за исключением Н
185
     * @param $word
186
     * @return bool
187
     */
188 13
    public static function checkBaseLastConsonantSoftness($word)
189
    {
190 13
        $consonants = static::$consonants;
191 13
        unset($consonants[array_search('н', $consonants)]);
192 13
        unset($consonants[array_search('й', $consonants)]);
193
194 13 View Code Duplication
        if (($substring = S::findLastPositionForOneOfChars(S::lower($word), $consonants)) !== false) {
0 ignored issues
show
Duplication introduced by wapmorgan
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...
195
            if (in_array(S::slice($substring, 0, 1), ['й', 'ч', 'щ', 'ш'], true)) { // always soft consonants
196
                return true;
197
            } elseif (S::length($substring) > 1 && in_array(S::slice($substring, 1, 2), ['е', 'ё', 'и', 'ю', 'я', 'ь'], true)) { // consonants are soft if they are trailed with these vowels
198
                return true;
199
            }
200
        }
201 13
        return false;
202
    }
203
204
    /**
205
     * Проверяет, что гласная образует два звука в словах
206
     * @param $vowel
207
     * @return bool
208
     */
209 2
    public static function isBinaryVowel($vowel)
210
    {
211 2
        return in_array(S::lower($vowel), ['е', 'ё', 'ю', 'я'], true);
212
    }
213
214
    /**
215
     * Выбор предлога по первой букве
216
     *
217
     * @param string $word Слово
218
     * @param string $prepositionWithVowel Предлог, если слово начинается с гласной
219
     * @param string $preposition Предлог, если слово не начинается с гласной
220
     *
221
     * @return string
222
     */
223
    public static function choosePrepositionByFirstLetter($word, $prepositionWithVowel, $preposition)
224
    {
225 View Code Duplication
        if (in_array(S::lower(S::slice($word, 0, 1)), ['а', 'о', 'и', 'у', 'э'], true)) {
0 ignored issues
show
Duplication introduced by wapmorgan
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...
226
            return $prepositionWithVowel;
227
        } else {
228
            return $preposition;
229
        }
230
    }
231
232
    /**
233
     * Выбор окончания в зависимости от мягкости
234
     *
235
     * @param $last
236
     * @param $softLast
237
     * @param $afterSoft
238
     * @param $afterHard
239
     *
240
     * @return mixed
241
     */
242 139
    public static function chooseVowelAfterConsonant($last, $softLast, $afterSoft, $afterHard)
243
    {
244 139
        if ($last === 'щ' || /*static::isVelarConsonant($last) ||*/ $softLast) {
245 48
            return $afterSoft;
246
        } else {
247 107
            return $afterHard;
248
        }
249
    }
250
251
    /**
252
     * @param string $verb Verb to modify if gender is female
253
     * @param string $gender If not `m`, verb will be modified
254
     * @return string Correct verb
255
     */
256 10
    public static function verb($verb, $gender)
257
    {
258 10
        $verb = S::lower($verb);
259
        // возвратный глагол
260 10
        if (S::slice($verb, -2) == 'ся') {
0 ignored issues
show
Bug introduced by wapmorgan
It seems like $verb defined by \morphos\S::lower($verb) on line 258 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...
261
262 5
            return ($gender == Gender::MALE
263 2
                ? $verb
264 5
                : S::slice($verb, 0, -2).(S::slice($verb, -3, -2) === 'л' ? null : 'л').'ась');
0 ignored issues
show
Bug introduced by wapmorgan
It seems like $verb defined by \morphos\S::lower($verb) on line 258 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...
265
        }
266
267
        // обычный глагол
268 5
        return ($gender == Gender::MALE
269 2
            ? $verb
270 5
            : $verb.(S::slice($verb, -1) === 'л' ? null : 'л').'а');
0 ignored issues
show
Bug introduced by wapmorgan
It seems like $verb defined by \morphos\S::lower($verb) on line 258 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...
271
    }
272
273
    /**
274
     * Add 'в' or 'во' prepositional before the word
275
     * @param string $word
276
     * @return string
277
     */
278 1
    public static function in($word)
279
    {
280 1
        $normalized = trim(S::lower($word));
281 1 View Code Duplication
        if (in_array(S::slice($normalized, 0, 1), ['в', 'ф'], true))
0 ignored issues
show
Duplication introduced by wapmorgan
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...
282 1
            return 'во '.$word;
283 1
        return 'в '.$word;
284
    }
285
286
    /**
287
     * Add 'с' or 'со' prepositional before the word
288
     * @param string $word
289
     * @return string
290
     */
291 1
    public static function with($word)
292
    {
293 1
        $normalized = trim(S::lower($word));
294 1
        if (in_array(S::slice($normalized, 0, 1), ['c', 'з', 'ш', 'ж'], true) && static::isConsonant(S::slice($normalized, 1, 2)) || S::slice($normalized, 0, 1) == 'щ')
295 1
            return 'со '.$word;
296 1
        return 'с '.$word;
297
    }
298
299
    /**
300
     * Add 'о' or 'об' or 'обо' prepositional before the word
301
     * @param string $word
302
     * @return string
303
     */
304 1
    public static function about($word)
305
    {
306 1
        $normalized = trim(S::lower($word));
307 1
        if (static::isVowel(S::slice($normalized, 0, 1)) && !in_array(S::slice($normalized, 0, 1), ['е', 'ё', 'ю', 'я'], true))
308 1
            return 'об '.$word;
309
310 1 View Code Duplication
        if (in_array(S::slice($normalized, 0, 3), ['все', 'всё', 'всю', 'что', 'мне'], true))
0 ignored issues
show
Duplication introduced by wapmorgan
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...
311 1
            return 'обо '.$word;
312
313 1
        return 'о '.$word;
314
    }
315
316
    /**
317
     * Выбирает первое или второе окончание в зависимости от звонкости/глухости в конце слова.
318
     * @param string $word Слово (или префикс), на основе звонкости которого нужно выбрать окончание
319
     * @param string $ifSonorous Окончание, если слово оканчивается на звонкую согласную
320
     * @param string $ifDeaf Окончание, если слово оканчивается на глухую согласную
321
     * @return string Первое или второе окончание
322
     * @throws \Exception
323
     */
324 3
    public static function chooseEndingBySonority($word, $ifSonorous, $ifDeaf)
325
    {
326 3
        $last = S::slice($word, -1);
327 3
        if (static::isSonorousConsonant($last))
328 2
            return $ifSonorous;
329 1
        if (static::isDeafConsonant($last))
330 1
            return $ifDeaf;
331
332
        throw new \Exception('Not implemented');
333
    }
334
335
    /**
336
     * Проверяет, является ли существительно адъективным существительным
337
     * @param string $noun Существительное
338
     * @return bool
339
     */
340 166
    public static function isAdjectiveNoun($noun)
341
    {
342 166
        return in_array(S::slice($noun, -2), ['ой', 'ий', 'ый', 'ая', 'ое', 'ее'])
343 166
            && !in_array($noun, ['гений', 'комментарий']);
344
    }
345
346
    /**
347
     * @param array $forms
348
     * @param $animate
349
     * @return mixed
350
     */
351 132
    public static function getVinitCaseByAnimateness(array $forms, $animate)
352
    {
353 132
        if ($animate) {
354 11
            return $forms[Cases::RODIT];
355
        } else {
356 121
            return $forms[Cases::IMENIT];
357
        }
358
    }
359
}
360