Completed
Push — master ( 082bc3...d55ee4 )
by f
11:06 queued 21s
created

RussianLanguage   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 299
Duplicated Lines 3.01 %

Coupling/Cohesion

Components 4
Dependencies 1

Test Coverage

Coverage 85.71%

Importance

Changes 0
Metric Value
dl 9
loc 299
ccs 60
cts 70
cp 0.8571
rs 9.0399
c 0
b 0
f 0
wmc 42
lcom 4
cbo 1

18 Methods

Rating   Name   Duplication   Size   Complexity  
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 isPaired() 0 5 2
A checkLastConsonantSoftness() 0 11 5
A isBinaryVowel() 0 4 1
A choosePrepositionByFirstLetter() 5 8 2
A chooseVowelAfterConsonant() 0 8 4
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

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