Passed
Push — master ( 6f5c39...8add63 )
by Sergey
01:40
created

Formatter::buildPreResult()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 19
rs 9.9
c 0
b 0
f 0
cc 3
nc 4
nop 1
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: rikosage
5
 * Date: 22/01/19
6
 * Time: 22:03
7
 */
8
9
namespace rikosage\NumberWordify;
10
11
use rikosage\NumberWordify\Base\BaseRank;
12
use rikosage\NumberWordify\Base\Morpher;
13
use rikosage\NumberWordify\Rank\ElevenToTwelve;
14
use rikosage\NumberWordify\Rank\Hundred;
15
use rikosage\NumberWordify\Rank\Single;
16
use rikosage\NumberWordify\Rank\Ten;
17
use rikosage\NumberWordify\Unit\DerivativeInterface;
18
use rikosage\NumberWordify\Unit\NullUnit;
19
use rikosage\NumberWordify\Unit\UnitInterface;
20
21
/**
22
 * Основной компонент библиотеки с методами сборки числа по разрядам.
23
 *
24
 * @package rikosage\NumberWordify
25
 */
26
class Formatter
27
{
28
    const SINGLE_UNIT_ITEM = 0;
29
    const THOUSAND_UNIT_ITEM = 1;
30
    const MILLION_UNIT = 2;
31
    const BILLION_UNIT = 3;
32
    const TRILLION_UNIT = 4;
33
34
    /* @var Single */
35
    public $single;
36
37
    /* @var BaseRank */
38
    public $elevenToTwenty;
39
40
    /* @var BaseRank */
41
    public $tens;
42
43
    /* @var BaseRank */
44
    public $hundred;
45
46
    /* @var UnitInterface|DerivativeInterface */
47
    private $unit;
48
49
    /**
50
     * Formatter constructor.
51
     * @param UnitInterface|null $unit Единицы измерения
52
     */
53
    public function __construct(UnitInterface $unit = null)
54
    {
55
        $this->unit = $unit ?: new NullUnit();
56
57
        $this->hundred = new Hundred();
58
        $this->elevenToTwenty = new ElevenToTwelve();
59
        $this->tens = new Ten();
60
        $this->single = new Single();
61
    }
62
63
    /**
64
     * @param number $number Число для перевода
65
     * @return string Число словами
66
     */
67
    public function asWords($number) : string
68
    {
69
        list($real, $float) = explode('.', sprintf("%0.2f", round((float)($number), 2)));
70
71
        $string = $this->process((int)$real);
72
73
        if ((int)$float && $this->unit instanceof DerivativeInterface) {
74
            $string .= " " . $this->process((int)$float, $this->unit->getDerivative());
75
        }
76
77
        return $string;
78
    }
79
80
    /**
81
     * Выполняет разбиение числа по разрядам, и переводит каждый разряд в слова
82
     *
83
     * @param int $number
84
     * @param UnitInterface|null $derivative
85
     * @return string
86
     */
87
    protected function process(int $number, ?UnitInterface $derivative = null)
88
    {
89
        $morpher = new Morpher($derivative ?: $this->unit);
90
91
        $ranks = str_split(strrev($number), 3);
92
        $ranks = array_map('strrev', $ranks);
93
        $result = [];
94
95
        foreach ($ranks as $rank => $value) {
96
97
            if ($value === "000") {
98
                continue;
99
            }
100
101
            $this->single->setGender($morpher->getUnitGender($rank));
102
103
            $preResult = $this->buildPreResult($value);
104
105
            if (!$preResult) {
106
                continue;
107
            }
108
109
            $result[] = vsprintf("%s %s", [
110
                implode(" ", $preResult),
111
                $morpher->morph($value, $morpher->getUnitItems($rank))
112
            ]);
113
        }
114
115
        krsort($result);
116
        return implode(" ", array_map('trim', $result));
117
    }
118
119
    private function buildPreResult($value)
120
    {
121
        list($hundred, $ten, $single) = $this->getInnerRanks($value);
122
123
        $innerResult = [$this->hundred->getWord($hundred)];
124
125
        if ($ten == 1) {
126
            $innerResult[] = $this->elevenToTwenty->getWord($single);
127
        } else {
128
            $innerResult[] = $this->tens->getWord($ten);
129
            $innerResult[] = $this->single->getWord($single);
130
        }
131
132
        if (empty ($innerResult)) {
133
            return false;
134
        }
135
136
        return array_filter($innerResult, function($item){
137
            return (bool)$item;
138
        });
139
    }
140
141
    private function getInnerRanks($value)
142
    {
143
        $innerRank = str_split($value);
144
        for ($i = count($innerRank); $i < 3; $i++) {
145
            array_unshift($innerRank, 0);
146
        }
147
        return $innerRank;
148
    }
149
150
}