Completed
Push — master ( 6a872b...95bb87 )
by f
01:14
created

NounPluralization   C

Complexity

Total Complexity 58

Size/Duplication

Total Lines 204
Duplicated Lines 7.35 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 15
loc 204
rs 6.3005
c 0
b 0
f 0
wmc 58
lcom 1
cbo 5

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getRunAwayVowelsList() 0 10 3
A pluralize() 0 11 4
B getNumeralForm() 0 14 8
A getCase() 0 6 1
B getCases() 3 23 4
F declinateSubstative() 12 81 35
A declinateAdjective() 0 13 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 NounPluralization 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 NounPluralization, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace morphos\Russian;
3
4
use morphos\S;
5
6
/**
7
 * Rules are from http://morpher.ru/Russian/Noun.aspx
8
 */
9
class NounPluralization extends \morphos\NounPluralization implements Cases
10
{
11
    use RussianLanguage, CasesHelper;
12
13
    const ONE = 1;
14
    const TWO_FOUR = 2;
15
    const FIVE_OTHER = 3;
16
17
    protected static $neuterExceptions = array(
18
        'поле',
19
        'море',
20
    );
21
22
    protected static $genitiveExceptions = array(
23
        'письмо' => 'писем',
24
        'пятно' => 'пятен',
25
        'кресло' => 'кресел',
26
        'коромысло' => 'коромысел',
27
        'ядро' => 'ядер',
28
        'блюдце' => 'блюдец',
29
        'полотенце' => 'полотенец',
30
    );
31
32
    protected static $immutableWords = array(
33
        'евро',
34
        'пенни',
35
    );
36
37
    protected static $runawayVowelsExceptions = array(
38
        'писе*ц',
39
        'песе*ц',
40
        'глото*к',
41
    );
42
43
    protected static $runawayVowelsNormalized = false;
44
45
    protected static function getRunAwayVowelsList()
46
    {
47
        if (self::$runawayVowelsNormalized === false) {
48
            self::$runawayVowelsNormalized = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type boolean of property $runawayVowelsNormalized.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
49
            foreach (self::$runawayVowelsExceptions as $word) {
50
                self::$runawayVowelsNormalized[str_replace('*', null, $word)] = S::indexOf($word, '*') - 1;
51
            }
52
        }
53
        return self::$runawayVowelsNormalized;
54
    }
55
56
    public static function pluralize($word, $count = 2, $animateness = false)
57
    {
58
        switch (self::getNumeralForm($count)) {
59
            case self::ONE:
60
                return $word;
61
            case self::TWO_FOUR:
62
                return NounDeclension::getCase($word, self::RODIT, $animateness);
63
            case self::FIVE_OTHER:
64
                return NounPluralization::getCase($word, self::RODIT, $animateness);
65
        }
66
    }
67
68
    public static function getNumeralForm($count)
69
    {
70
        if ($count > 100) {
71
            $count %= 100;
72
        }
73
        $ending = $count % 10;
74
        if (($count > 20 && $ending == 1) || $count == 1) {
75
            return self::ONE;
76
        } elseif (($count > 20 && in_array($ending, range(2, 4))) || in_array($count, range(2, 4))) {
77
            return self::TWO_FOUR;
78
        } else {
79
            return self::FIVE_OTHER;
80
        }
81
    }
82
83
    public static function getCase($word, $case, $animateness = false)
84
    {
85
        $case = self::canonizeCase($case);
86
        $forms = self::getCases($word, $animateness);
87
        return $forms[$case];
88
    }
89
90
    public static function getCases($word, $animateness = false)
91
    {
92
        $word = S::lower($word);
93
94
        if (in_array($word, self::$immutableWords)) {
95
            return array(
96
                self::IMENIT => $word,
97
                self::RODIT => $word,
98
                self::DAT => $word,
99
                self::VINIT => $word,
100
                self::TVORIT => $word,
101
                self::PREDLOJ => self::choosePrepositionByFirstLetter($word, 'об', 'о').' '.$word,
102
            );
103
        }
104
105
        // Адъективное склонение (Сущ, образованные от прилагательных и причастий) - прохожий, существительное
106 View Code Duplication
        if (in_array(S::slice($word, -2), array('ой', 'ий', 'ый', 'ая', 'ое', 'ее')) && $word != 'гений') {
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...
107
            return self::declinateAdjective($word, $animateness);
108
        }
109
110
        // Субстантивное склонение (существительные)
111
        return self::declinateSubstative($word, $animateness);
112
    }
113
114
    protected static function declinateSubstative($word, $animateness)
115
    {
116
        $prefix = S::slice($word, 0, -1);
117
        $last = S::slice($word, -1);
118
119
        $runaway_vowels_list = static::getRunAwayVowelsList();
120
        if (isset($runaway_vowels_list[$word])) {
121
            $vowel_offset = $runaway_vowels_list[$word];
122
            $word = S::slice($word, 0, $vowel_offset) . S::slice($word, $vowel_offset + 1);
123
        }
124
125
        if (($declension = NounDeclension::getDeclension($word)) == NounDeclension::SECOND_DECLENSION) {
126
            $soft_last = $last == 'й' || (in_array($last, ['ь', 'е', 'ё', 'ю', 'я']) && ((self::isConsonant(S::slice($word, -2, -1)) && !self::isHissingConsonant(S::slice($word, -2, -1))) || S::slice($word, -2, -1) == 'и'));
127
            $prefix = NounDeclension::getPrefixOfSecondDeclension($word, $last);
128
        } elseif ($declension == NounDeclension::FIRST_DECLENSION) {
129
            $soft_last = self::checkLastConsonantSoftness($word);
130
        } else {
131
            $soft_last = S::slice($word, -2) == 'сь';
132
        }
133
134
        $forms = array();
135
136
        if ($last == 'ч' || in_array(S::slice($word, -2), array('чь', 'сь')) || (self::isVowel($last) && in_array(S::slice($word, -2, -1), array('ч', 'к')))) { // before ч, чь, сь, ч+vowel, к+vowel
137
            $forms[Cases::IMENIT] = $prefix.'и';
138
        } elseif ($last == 'н' || $last == 'ц') {
139
            $forms[Cases::IMENIT] = $prefix.'ы';
140 View Code Duplication
        } else {
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...
141
            $forms[Cases::IMENIT] = self::chooseVowelAfterConsonant($last, $soft_last, $prefix.'я', $prefix.'а');
142
        }
143
144
        // RODIT
145
        if (isset(self::$genitiveExceptions[$word])) {
146
            $forms[Cases::RODIT] = self::$genitiveExceptions[$word];
147
        } elseif (in_array($last, array('о', 'е'))) {
148
            // exceptions
149
            if (in_array($word, self::$neuterExceptions)) {
150
                $forms[Cases::RODIT] = $prefix.'ей';
151 View Code Duplication
            } elseif (S::slice($word, -2, -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...
152
                $forms[Cases::RODIT] = $prefix.'й';
153
            } else {
154
                $forms[Cases::RODIT] = $prefix;
155
            }
156
        } elseif (S::slice($word, -2) == 'ка') { // words ending with -ка: чашка, вилка, ложка, тарелка, копейка, батарейка
157
            if (S::slice($word, -3, -2) == 'л') {
158
                $forms[Cases::RODIT] = S::slice($word, 0, -2).'ок';
159 View Code Duplication
            } elseif (S::slice($word, -3, -2) == 'й') {
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...
160
                $forms[Cases::RODIT] = S::slice($word, 0, -3).'ек';
161
            } else {
162
                $forms[Cases::RODIT] = S::slice($word, 0, -2).'ек';
163
            }
164
        } elseif (in_array($last, array('а'))) { // обида, ябеда
165
            $forms[Cases::RODIT] = $prefix;
166
        } elseif (in_array($last, array('я'))) { // молния
167
            $forms[Cases::RODIT] = $prefix.'й';
168
        } elseif (RussianLanguage::isHissingConsonant($last) || ($soft_last && $last != 'й') || in_array(S::slice($word, -2), array('чь', 'сь'))) {
169
            $forms[Cases::RODIT] = $prefix.'ей';
170 View Code Duplication
        } elseif ($last == 'й' || S::slice($word, -2) == 'яц') { // месяц
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...
171
            $forms[Cases::RODIT] = $prefix.'ев';
172
        } else { // (self::isConsonant($last) && !RussianLanguage::isHissingConsonant($last))
173
            $forms[Cases::RODIT] = $prefix.'ов';
174
        }
175
176
        // DAT
177
        $forms[Cases::DAT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ям', $prefix.'ам');
178
179
        // VINIT
180
        $forms[Cases::VINIT] = NounDeclension::getVinitCaseByAnimateness($forms, $animateness);
181
182
        // TVORIT
183
        // my personal rule
184
        if ($last == 'ь' && $declension == NounDeclension::THIRD_DECLENSION && !in_array(S::slice($word, -2), array('чь', 'сь'))) {
185
            $forms[Cases::TVORIT] = $prefix.'ми';
186
        } else {
187
            $forms[Cases::TVORIT] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ями', $prefix.'ами');
188
        }
189
190
        // PREDLOJ
191
        $forms[Cases::PREDLOJ] = self::chooseVowelAfterConsonant($last, $soft_last && S::slice($word, -2, -1) != 'ч', $prefix.'ях', $prefix.'ах');
192
        $forms[Cases::PREDLOJ] = self::choosePrepositionByFirstLetter($forms[Cases::PREDLOJ], 'об', 'о').' '.$forms[Cases::PREDLOJ];
193
        return $forms;
194
    }
195
196
    /**
197
     * Rules are from http://rusgram.narod.ru/1216-1231.html
198
     */
199
    protected static function declinateAdjective($word, $animateness)
200
    {
201
        $prefix = S::slice($word, 0, -2);
202
        $vowel = self::isHissingConsonant(S::slice($prefix, -1)) ? 'и' : 'ы';
203
        return array(
204
            Cases::IMENIT => $prefix.$vowel.'е',
205
            Cases::RODIT => $prefix.$vowel.'х',
206
            Cases::DAT => $prefix.$vowel.'м',
207
            Cases::VINIT => $prefix.$vowel.($animateness ? 'х' : 'е'),
208
            Cases::TVORIT => $prefix.$vowel.'ми',
209
            Cases::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.$vowel.'х',
210
        );
211
    }
212
}
213