Completed
Push — master ( a85940...3d0bce )
by f
01:53
created

RussianLanguage   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 270
Duplicated Lines 2.59 %

Coupling/Cohesion

Components 4
Dependencies 1

Test Coverage

Coverage 84.85%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 7
loc 270
ccs 56
cts 66
cp 0.8485
rs 8.2608
c 1
b 0
f 0
wmc 40
lcom 4
cbo 1

18 Methods

Rating   Name   Duplication   Size   Complexity  
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
B checkLastConsonantSoftness() 0 11 5
A isVowel() 0 4 1
A countSyllables() 0 4 1
A isPaired() 0 5 2
A isBinaryVowel() 0 4 1
A choosePrepositionByFirstLetter() 5 8 2
A chooseVowelAfterConsonant() 0 8 4
A verb() 0 11 4
A in() 0 7 2
A with() 0 7 4
A about() 2 11 4
A chooseEndingBySonority() 0 10 3
A isAdjectiveNoun() 0 5 2

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
     * Проверка гласной
75
     */
76 342
    public static function isVowel($char)
77
    {
78 342
        return in_array($char, self::$vowels);
79
    }
80
81
    /**
82
     * Проверка согласной
83
     */
84 188
    public static function isConsonant($char)
85
    {
86 188
        return in_array($char, self::$consonants);
87
    }
88
89
    /**
90
     * Проверка звонкости согласной
91
     */
92 2
    public static function isSonorousConsonant($char)
93
    {
94 2
        return in_array($char, self::$sonorousConsonants);
95
    }
96
97
    /**
98
     * Проверка глухости согласной
99
     */
100 6
    public static function isDeafConsonant($char)
101
    {
102 6
        return in_array($char, self::$deafConsonants);
103
    }
104
105
    /**
106
     * Щипящая ли согласная
107
     */
108 445
    public static function isHissingConsonant($consonant)
109
    {
110 445
        return in_array(S::lower($consonant), ['ж', 'ш', 'ч', 'щ'], true);
111
    }
112
113
    /**
114
     *
115
     */
116 12
    protected static function isVelarConsonant($consonant)
117
    {
118 12
        return in_array(S::lower($consonant), ['г', 'к', 'х'], true);
119
    }
120
121
    /**
122
     * Подсчет слогов
123
     */
124
    public static function countSyllables($string)
125
    {
126
        return S::countChars($string, self::$vowels);
127
    }
128
129
    /**
130
     * Проверка парности согласной
131
     */
132
    public static function isPaired($consonant)
133
    {
134
        $consonant = S::lower($consonant);
135
        return array_key_exists($consonant, self::$pairs) || (array_search($consonant, self::$pairs) !== false);
136
    }
137
138
    /**
139
     * Проверка мягкости последней согласной
140
     */
141 43
    public static function checkLastConsonantSoftness($word)
142
    {
143 43
        if (($substring = S::findLastPositionForOneOfChars(S::lower($word), self::$consonants)) !== false) {
144 43
            if (in_array(S::slice($substring, 0, 1), ['й', 'ч', 'щ'], true)) { // always soft consonants
145 14
                return true;
146 29
            } elseif (S::length($substring) > 1 && in_array(S::slice($substring, 1, 2), ['е', 'ё', 'и', 'ю', 'я', 'ь'], true)) { // consonants are soft if they are trailed with these vowels
147 3
                return true;
148
            }
149
        }
150 26
        return false;
151
    }
152
153
    /**
154
     * Проверяет, что гласная образует два звука в словах
155
     * @param $vowel
156
     * @return bool
157
     */
158 2
    public static function isBinaryVowel($vowel)
159
    {
160 2
        return in_array(S::lower($vowel), ['е', 'ё', 'ю', 'я'], true);
161
    }
162
163
    /**
164
     * Выбор предлога по первой букве
165
     */
166
    public static function choosePrepositionByFirstLetter($word, $prepositionWithVowel, $preposition)
167
    {
168 View Code Duplication
        if (in_array(S::lower(S::slice($word, 0, 1)), ['а', 'о', 'и', 'у', 'э'], true)) {
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...
169
            return $prepositionWithVowel;
170
        } else {
171
            return $preposition;
172
        }
173
    }
174
175
    /**
176
     * Выбор окончания в зависимости от мягкости
177
     */
178 116
    public static function chooseVowelAfterConsonant($last, $soft_last, $after_soft, $after_hard)
179
    {
180 116
        if ((RussianLanguage::isHissingConsonant($last) && !in_array($last, ['ж', 'ч'], true)) || /*self::isVelarConsonant($last) ||*/ $soft_last) {
181 40
            return $after_soft;
182
        } else {
183 90
            return $after_hard;
184
        }
185
    }
186
187
    /**
188
     * @param string $verb Verb to modify if gender is female
189
     * @param string $gender If not `m`, verb will be modified
190
     * @return string Correct verb
191
     */
192 8
    public static function verb($verb, $gender)
193
    {
194 8
        $verb = S::lower($verb);
195
        // возвратный глагол
196 8
        if (S::slice($verb, -2) == 'ся') {
0 ignored issues
show
Security Bug introduced by
It seems like $verb defined by \morphos\S::lower($verb) on line 194 can also be of type false; however, morphos\S::slice() 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...
197 4
            return ($gender == Gender::MALE ? $verb : mb_substr($verb, 0, -2).'ась');
198
        }
199
200
        // обычный глагол
201 4
        return ($gender == Gender::MALE ? $verb : $verb.'а');
202
    }
203
204
    /**
205
     * Add 'в' or 'во' prepositional before the word
206
     * @param string $word
207
     * @return string
208
     */
209 1
    public static function in($word)
210
    {
211 1
        $normalized = trim(S::lower($word));
212 1
        if (in_array(S::slice($normalized, 0, 1), ['в', 'ф'], true))
213 1
            return 'во '.$word;
214 1
        return 'в '.$word;
215
    }
216
217
    /**
218
     * Add 'с' or 'со' prepositional before the word
219
     * @param string $word
220
     * @return string
221
     */
222 1
    public static function with($word)
223
    {
224 1
        $normalized = trim(S::lower($word));
225 1
        if (in_array(S::slice($normalized, 0, 1), ['c', 'з', 'ш', 'ж'], true) && static::isConsonant(S::slice($normalized, 1, 2)) || S::slice($normalized, 0, 1) == 'щ')
226 1
            return 'со '.$word;
227 1
        return 'с '.$word;
228
    }
229
230
    /**
231
     * Add 'о' or 'об' or 'обо' prepositional before the word
232
     * @param string $word
233
     * @return string
234
     */
235 1
    public static function about($word)
236
    {
237 1
        $normalized = trim(S::lower($word));
238 1
        if (static::isVowel(S::slice($normalized, 0, 1)) && !in_array(S::slice($normalized, 0, 1), ['е', 'ё', 'ю', 'я'], true))
239 1
            return 'об '.$word;
240
241 1 View Code Duplication
        if (in_array(S::slice($normalized, 0, 3), ['все', 'всё', 'всю', 'что', 'мне'], true))
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...
242 1
            return 'обо '.$word;
243
244 1
        return 'о '.$word;
245
    }
246
247
    /**
248
     * Выбирает первое или второе окончание в зависимости от звонкости/глухости в конце слова.
249
     * @param string $word Слово (или префикс), на основе звонкости которого нужно выбрать окончание
250
     * @param string $ifSonorous Окончание, если слово оканчивается на звонкую согласную
251
     * @param string $ifDeaf Окончание, если слово оканчивается на глухую согласную
252
     * @return string Первое или второе окончание
253
     * @throws \Exception
254
     */
255 2
    public static function chooseEndingBySonority($word, $ifSonorous, $ifDeaf)
256
    {
257 2
        $last = S::slice($word, -1);
258 2
        if (self::isSonorousConsonant($last))
259 1
            return $ifSonorous;
260 1
        if (self::isDeafConsonant($last))
261 1
            return $ifDeaf;
262
263
        throw new \Exception('Not implemented');
264
    }
265
266
    /**
267
     * Проверяет, является ли существительно адъективным существительным
268
     * @param string $noun Существительное
269
     * @return bool
270
     */
271 142
    public static function isAdjectiveNoun($noun)
272
    {
273 142
        return in_array(S::slice($noun, -2), ['ой', 'ий', 'ый', 'ая', 'ое', 'ее'])
274 142
            && $noun != 'гений';
275
    }
276
}
277