Word::convertUnsignedIntNumberToWord()   F
last analyzed

Complexity

Conditions 22
Paths 2534

Size

Total Lines 65
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 28
nc 2534
nop 2
dl 0
loc 65
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace cse\helpers;
6
7
/**
8
 * Class Word
9
 *
10
 * @package cse\helpers
11
 */
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
202
    {
203
        $number = (int) $number;
204
        return sprintf(
205
            $prefix . $worlds[($number % 100 > 4 && $number % 100 < 20) ? 2 : $map[min($number % 10, 5)]],
206
            $number
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
219
    {
220
        return empty($text) ? '' : str_replace(array_keys($dictionary), array_values($dictionary), $text);
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
308
    {
309
        // check sign, convert abc
310
        $amount = ($sign = $amount < 0) ? substr((string) $amount, 1) : (string) $amount;
311
312
        // extend amount
313
        preg_match('/(\d+)[.,\s]?(\d*)/', (string) $amount, $extend);
314
        $number = empty($extend[1]) ? 0 : $extend[1];
315
        $fraction = empty($extend[2]) ? 0 : $extend[2];
316
317
        // get result number
318
        $result = self::convertUnsignedIntNumberToWord($number)
319
                . self::getInclinationByNumber(
320
                    $number,
321
                    self::DICTIONARY_CURRENCY[0],
322
                    ' '
323
                );
324
325
        // get result fraction
326
        if ($isFractalNullView || !empty((int) $fraction)) {
327
            if ($fraction[0] != 0 && $fraction < 10) $fraction .= '0';
328
            $result .= ' ' . ($isFullView ? self::convertUnsignedIntNumberToWord($fraction, 0) : $fraction)
329
                     . self::getInclinationByNumber(
330
                         (int) $fraction,
331
                         self::DICTIONARY_CURRENCY[1],
332
                         ' '
333
                     );
334
        }
335
336
        return ($sign ? self::NEGATIVE_SIGN . ' ' : '') . $result;
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
353
    {
354
        if ($isCamelCase) {
355
            return implode('', array_map(
356
                'ucfirst',
357
                explode($delimiter, strtolower($word))
358
            ));
359
        } else {
360
            return ltrim(strtolower(preg_replace(
361
                '/(\p{Lu}\p{Lu}*(?=$|\p{Lu})|(\p{Lu}+))/',
362
                $delimiter . '$1',
363
                $word
364
            )), $delimiter);
365
        }
366
    }
367
}