| Total Complexity | 41 | 
| Total Lines | 353 | 
| Duplicated Lines | 0 % | 
| Changes | 0 | ||
Complex classes like Word 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.
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 Word, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 12 | class Word | ||
| 13 | { | ||
| 14 | const DEFAULT_CHARSET = 'UTF-8'; | ||
| 15 | |||
| 16 | const INCLINATION_MAP = [2, 0, 1, 1, 1, 2]; | ||
| 17 | |||
| 18 | const NEGATIVE_SIGN = 'минус'; | ||
| 19 | |||
| 20 | const DICTIONARY_MONTH_LIST = [ | ||
| 21 | '01' => 'января', | ||
| 22 | '02' => 'февраля', | ||
| 23 | '03' => 'марта', | ||
| 24 | '04' => 'апреля', | ||
| 25 | '05' => 'мая', | ||
| 26 | '06' => 'июня', | ||
| 27 | '07' => 'июля', | ||
| 28 | '08' => 'августа', | ||
| 29 | '09' => 'сентября', | ||
| 30 | '10' => 'октября', | ||
| 31 | '11' => 'ноября', | ||
| 32 | '12' => 'декабря', | ||
| 33 | ]; | ||
| 34 | |||
| 35 | const DICTIONARY_RUS_TO_EN = [ | ||
| 36 | 'а' => 'a', | ||
| 37 | 'б' => 'b', | ||
| 38 | 'в' => 'v', | ||
| 39 | 'г' => 'g', | ||
| 40 | 'д' => 'd', | ||
| 41 | 'е' => 'e', | ||
| 42 | 'ё' => 'e', | ||
| 43 | 'ж' => 'zh', | ||
| 44 | 'з' => 'z', | ||
| 45 | 'и' => 'i', | ||
| 46 | 'й' => 'y', | ||
| 47 | 'к' => 'k', | ||
| 48 | 'л' => 'l', | ||
| 49 | 'м' => 'm', | ||
| 50 | 'н' => 'n', | ||
| 51 | 'о' => 'o', | ||
| 52 | 'п' => 'p', | ||
| 53 | 'р' => 'r', | ||
| 54 | 'с' => 's', | ||
| 55 | 'т' => 't', | ||
| 56 | 'у' => 'u', | ||
| 57 | 'ф' => 'f', | ||
| 58 | 'х' => 'h', | ||
| 59 | 'ц' => 'ts', | ||
| 60 | 'ч' => 'ch', | ||
| 61 | 'ш' => 'sh', | ||
| 62 | 'щ' => 'sht', | ||
| 63 | 'ь' => '', | ||
| 64 | 'ы' => 'y', | ||
| 65 | 'ъ' => '', | ||
| 66 | 'э' => 'e', | ||
| 67 | 'ю' => 'yu', | ||
| 68 | 'я' => 'ya', | ||
| 69 | 'А' => 'A', | ||
| 70 | 'Б' => 'B', | ||
| 71 | 'В' => 'V', | ||
| 72 | 'Г' => 'G', | ||
| 73 | 'Д' => 'D', | ||
| 74 | 'Е' => 'E', | ||
| 75 | 'Ж' => 'Zh', | ||
| 76 | 'З' => 'Z', | ||
| 77 | 'И' => 'I', | ||
| 78 | 'Й' => 'Y', | ||
| 79 | 'К' => 'K', | ||
| 80 | 'Л' => 'L', | ||
| 81 | 'М' => 'M', | ||
| 82 | 'Н' => 'N', | ||
| 83 | 'О' => 'O', | ||
| 84 | 'П' => 'P', | ||
| 85 | 'Р' => 'R', | ||
| 86 | 'С' => 'S', | ||
| 87 | 'Т' => 'T', | ||
| 88 | 'У' => 'U', | ||
| 89 | 'Ф' => 'F', | ||
| 90 | 'Х' => 'H', | ||
| 91 | 'Ц' => 'Ts', | ||
| 92 | 'Ч' => 'Ch', | ||
| 93 | 'Ш' => 'Sh', | ||
| 94 | 'Щ' => 'Sht', | ||
| 95 | 'Ь' => '', | ||
| 96 | 'Ы' => 'Y', | ||
| 97 | 'Ъ' => '', | ||
| 98 | 'Э' => 'E', | ||
| 99 | 'Ю' => 'Yu', | ||
| 100 | 'Я' => 'Ya' | ||
| 101 | ]; | ||
| 102 | |||
| 103 | const DICTIONARY_NUMBER_TO_WORD = [ | ||
| 104 | -2 => 'две', | ||
| 105 | -1 => 'одна', | ||
| 106 | 0 => 'ноль', | ||
| 107 | 1 => 'один', | ||
| 108 | 2 => 'два', | ||
| 109 | 3 => 'три', | ||
| 110 | 4 => 'четыре', | ||
| 111 | 5 => 'пять', | ||
| 112 | 6 => 'шесть', | ||
| 113 | 7 => 'семь', | ||
| 114 | 8 => 'восемь', | ||
| 115 | 9 => 'девять', | ||
| 116 | 10 => 'десять', | ||
| 117 | 11 => 'одиннадцать', | ||
| 118 | 12 => 'двенадцать', | ||
| 119 | 13 => 'тринадцать', | ||
| 120 | 14 => 'четырнадцать', | ||
| 121 | 15 => 'пятнадцать', | ||
| 122 | 16 => 'шестнадцать', | ||
| 123 | 17 => 'семнадцать', | ||
| 124 | 18 => 'восемнадцать', | ||
| 125 | 19 => 'девятнадцать', | ||
| 126 | 20 => 'двадцать', | ||
| 127 | 30 => 'тридцать', | ||
| 128 | 40 => 'сорок', | ||
| 129 | 50 => 'пятьдесят', | ||
| 130 | 60 => 'шестьдесят', | ||
| 131 | 70 => 'семьдесят', | ||
| 132 | 80 => 'восемьдесят', | ||
| 133 | 90 => 'девяносто', | ||
| 134 | 100 => 'сто', | ||
| 135 | 200 => 'двести', | ||
| 136 | 300 => 'триста', | ||
| 137 | 400 => 'четыреста', | ||
| 138 | 500 => 'пятьсот', | ||
| 139 | 600 => 'шестьсот', | ||
| 140 | 700 => 'семьсот', | ||
| 141 | 800 => 'восемьсот', | ||
| 142 | 900 => 'девятьсот' | ||
| 143 | ]; | ||
| 144 | |||
| 145 | const DICTIONARY_PART_GROUP = [ | ||
| 146 | ['тысяча', 'тысячи', 'тысяч'], | ||
| 147 | ['миллион', 'миллиона', 'миллионов'], | ||
| 148 | ['миллиард', 'миллиарда', 'миллиардов'], | ||
| 149 | ['триллион', 'триллиона', 'триллионов'], | ||
| 150 | ['квадриллион', 'квадриллиона', 'квадриллионов'], | ||
| 151 | ['квинтиллион', 'квинтиллиона', 'квинтиллионов'], | ||
| 152 | ['секстиллион', 'секстиллиона', 'секстиллионов'], | ||
| 153 | // next | ||
| 154 | ]; | ||
| 155 | |||
| 156 | const DICTIONARY_CURRENCY = [ | ||
| 157 | ['рубль', 'рубля', 'рублей'], | ||
| 158 | ['копейка', 'копейки', 'копеек'] | ||
| 159 | ]; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Convert string charset to UTF-8 | ||
| 163 | * | ||
| 164 | * @param string $string | ||
| 165 | * @param string $charset | ||
| 166 | * | ||
| 167 | * @return string | ||
| 168 | */ | ||
| 169 | public static function stringToUtf(string $string, string $charset = 'CP1251'): string | ||
| 170 |     { | ||
| 171 | return iconv($charset, self::DEFAULT_CHARSET, $string); | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * Convert date month to word (Russia dictionary) | ||
| 176 | * | ||
| 177 | * @example 2017-05-01 => 1 мая 2017 | ||
| 178 | * | ||
| 179 | * @param string $date | ||
| 180 | * @param string $delimiter | ||
| 181 | * | ||
| 182 | * @return string | ||
| 183 | */ | ||
| 184 | public static function convertDateMonthToWord(string $date, string $delimiter = ' '): string | ||
| 185 |     { | ||
| 186 |         list($year, $month, $day) = explode('-', date('Y-m-d', strtotime($date))); | ||
| 187 | |||
| 188 | return $day . $delimiter . self::DICTIONARY_MONTH_LIST[$month] . $delimiter . $year; | ||
| 189 | } | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Inclination by number (Russia map) | ||
| 193 | * | ||
| 194 | * @param $number | ||
| 195 | * @param array $worlds | ||
| 196 | * @param string $prefix | ||
| 197 | * @param array $map | ||
| 198 | * | ||
| 199 | * @return string | ||
| 200 | */ | ||
| 201 | public static function getInclinationByNumber($number, array $worlds, string $prefix = '', array $map = self::INCLINATION_MAP): string | ||
| 207 | ); | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Text transliterate by dictionary (Russia dictionary) | ||
| 212 | * | ||
| 213 | * @param $text | ||
| 214 | * @param array $dictionary | ||
| 215 | * | ||
| 216 | * @return string | ||
| 217 | */ | ||
| 218 | public static function transliterate($text, array $dictionary = self::DICTIONARY_RUS_TO_EN): string | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | * Converter unsigned integer number to word (Russia dictionary) | ||
| 225 | * | ||
| 226 | * @param $number | ||
| 227 | * @param int|null $groupIndex | ||
| 228 | * | ||
| 229 | * @return string | ||
| 230 | */ | ||
| 231 | public static function convertUnsignedIntNumberToWord($number, ?int $groupIndex = null): string | ||
| 232 |     { | ||
| 233 | $result = []; | ||
| 234 | $number = is_double($number) ? number_format($number, 0, '', '') : $number; | ||
| 235 | |||
| 236 | // zero result | ||
| 237 | if (empty((int) $number)) return self::DICTIONARY_NUMBER_TO_WORD[0]; | ||
| 238 | |||
| 239 | // add zero (1234 => 001234, 1 => 001) | ||
| 240 | $number = strval($number); | ||
| 241 | $number = str_pad($number, (int) (ceil(strlen($number) / 3) * 3), '0', STR_PAD_LEFT); | ||
| 242 | // split 3 | ||
| 243 | $splits = array_reverse(str_split($number, 3)); | ||
| 244 | |||
| 245 |         foreach ($splits as $key => $group) { | ||
| 246 | |||
| 247 | // convert number to word | ||
| 248 |             if ($group > 0 || ($key == 0 && $group == 0)) { | ||
| 249 | $digits = []; | ||
| 250 | |||
| 251 | // 100 | ||
| 252 | if ($group > 99) $digits[] = floor($group / 100) * 100; | ||
| 253 | |||
| 254 | // 99 | ||
| 255 |                 if ($tens = $group % 100) { | ||
| 256 | $ones = $group % 10; | ||
| 257 | |||
| 258 | $sign_key = ($key == 1 || (is_int($groupIndex) && $groupIndex == $key)) && | ||
| 259 | ($tens != 11 && $tens != 12 && $ones > 0 && $ones < 3) | ||
| 260 | ? -1 : 1; | ||
| 261 | |||
| 262 |                     if ($tens < 20) { | ||
| 263 | $digits[] = $sign_key * $tens; | ||
| 264 |                     } else { | ||
| 265 | |||
| 266 | $digits[] = floor($tens / 10) * 10; | ||
| 267 | if (!empty($ones)) $digits[] = $sign_key * $ones; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | // last numbers | ||
| 272 | $last = abs(end($digits)); | ||
| 273 | |||
| 274 | // number to word | ||
| 275 |                 foreach ($digits as $j => $digit) { | ||
| 276 | $digits[$j] = self::DICTIONARY_NUMBER_TO_WORD[$digit]; | ||
| 277 | } | ||
| 278 | |||
| 279 | // add part group | ||
| 280 |                 if (!empty($key) && array_key_exists($key-1, self::DICTIONARY_PART_GROUP)) { | ||
| 281 | $digits[] = self::getInclinationByNumber($last, self::DICTIONARY_PART_GROUP[$key-1]); | ||
| 282 | } | ||
| 283 | |||
| 284 | // add convert part number | ||
| 285 |                 if (!empty($digits)) array_unshift($result, implode(' ', $digits)); | ||
| 286 | |||
| 287 | // clear vars | ||
| 288 | unset($j, $digit, $digits, $last); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | // clear vars | ||
| 292 | unset($group, $key, $splits, $number); | ||
| 293 | |||
| 294 | // join array | ||
| 295 |         return implode(' ', $result); | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * Converter amount to word (Russia dictionary) | ||
| 300 | * | ||
| 301 | * @param $amount | ||
| 302 | * @param bool $isFractalNullView | ||
| 303 | * @param bool $isFullView | ||
| 304 | * | ||
| 305 | * @return string | ||
| 306 | */ | ||
| 307 | public static function convertAmountToWord($amount, bool $isFractalNullView = true, $isFullView = false): string | ||
| 337 | } | ||
| 338 | |||
| 339 | /** | ||
| 340 | * Convert word to CamelCase | ||
| 341 | * | ||
| 342 | * @example | ||
| 343 | * $isCamelCase = false | example-word => ExampleWord | ||
| 344 | * $isCamelCase = true | ExampleWord => example-word | ||
| 345 | * | ||
| 346 | * @param string $word | ||
| 347 | * @param bool $isCamelCase | ||
| 348 | * @param string $delimiter | ||
| 349 | * | ||
| 350 | * @return string | ||
| 351 | */ | ||
| 352 | public static function camelCase(string $word, bool $isCamelCase = false, string $delimiter = '-'): string | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } |