1
|
|
|
<?php
|
2
|
|
|
declare(strict_types = 1);
|
3
|
|
|
|
4
|
|
|
namespace Converter\Core;
|
5
|
|
|
|
6
|
|
|
use Converter\Init\Data;
|
7
|
|
|
|
8
|
|
|
/**
|
9
|
|
|
* Converts a number (up to 1e+510) to its text representation e.g. 12 -> двенадцать (Russian only).
|
10
|
|
|
* @author Sergey Kanashin <[email protected]>
|
11
|
|
|
* @copyright 2003-2017
|
12
|
|
|
*/
|
13
|
|
|
final class Number2Text
|
14
|
|
|
{
|
15
|
|
|
private $data;
|
16
|
|
|
private $iNumber;
|
17
|
|
|
private $currency;
|
18
|
|
|
private $sign;
|
19
|
|
|
|
20
|
1 |
|
public function currency(bool $show = false): bool
|
21
|
|
|
{
|
22
|
1 |
|
return $this->currency = $show;
|
23
|
|
|
}
|
24
|
|
|
|
25
|
8 |
|
public function convert(string $input): string
|
26
|
|
|
{
|
27
|
8 |
|
$this->iNumber = $this->prepNumber($input);
|
28
|
8 |
|
$fullResult = null;
|
29
|
8 |
|
$arrChunks = $this->makeChunks();
|
30
|
8 |
|
$numGroups = count($arrChunks);
|
31
|
|
|
|
32
|
8 |
|
if ($this->iNumber === '0') {
|
33
|
3 |
|
$fullResult = 'ноль ';
|
34
|
3 |
|
$this->sign = '';
|
35
|
|
|
}
|
36
|
|
|
|
37
|
8 |
|
$this->data = new Data();
|
38
|
8 |
|
for ($i = $numGroups; $i >= 1; $i--) {
|
39
|
8 |
|
$currChunk = (int)strrev($arrChunks[$i - 1]);
|
40
|
8 |
|
$this->fixArray($i);
|
41
|
8 |
|
$preResult = $this->makeWords($currChunk);
|
42
|
8 |
|
if ($currChunk !== 0 || $i === 1) {
|
43
|
8 |
|
$preResult .= $this->getRegister($i, $currChunk);
|
44
|
|
|
}
|
45
|
8 |
|
$fullResult .= $preResult;
|
46
|
|
|
}
|
47
|
8 |
|
return $this->sign . $fullResult;
|
48
|
|
|
}
|
49
|
|
|
|
50
|
|
|
/**
|
51
|
|
|
* Checks and normalizes input number, defines its sign and returns absolute value.
|
52
|
|
|
* @param string $number - signed input number
|
53
|
|
|
* @property string $sign - sign of a number
|
54
|
|
|
* @return string - unsigned number
|
55
|
|
|
*/
|
56
|
8 |
|
private function prepNumber(string $number): string
|
57
|
|
|
{
|
58
|
8 |
|
$this->sign = '';
|
59
|
8 |
|
if (substr($number, 0, 1) === "-") {
|
60
|
2 |
|
$this->sign = 'минус ';
|
61
|
2 |
|
$number = substr($number, 1);
|
62
|
|
|
}
|
63
|
8 |
|
return preg_replace("/[^\d]/", "", $number);
|
64
|
|
|
}
|
65
|
|
|
|
66
|
|
|
/**
|
67
|
|
|
* Creates an array with reversed 3-digit chunks of given number.
|
68
|
|
|
* Example: '1125468' => array['864', '521', '1']
|
69
|
|
|
* @return array
|
70
|
|
|
*/
|
71
|
8 |
|
private function makeChunks(): array
|
72
|
|
|
{
|
73
|
8 |
|
$rvrsValue = strrev($this->iNumber);
|
74
|
8 |
|
$chunks = chunk_split($rvrsValue, 3);
|
75
|
8 |
|
$arrCh = explode("\r\n", rtrim($chunks));
|
76
|
|
|
|
77
|
8 |
|
return $arrCh;
|
78
|
|
|
}
|
79
|
|
|
|
80
|
|
|
/**
|
81
|
|
|
* Change data array so that femine names of units are correct (Russian specific language construct)
|
82
|
|
|
* @param int $fem - flag that indicates chunk index
|
83
|
|
|
* @internal param \Converter\Init\Data $data - Data object with data arrays.
|
84
|
|
|
*/
|
85
|
8 |
|
private function fixArray(int $fem)
|
86
|
|
|
{
|
87
|
8 |
|
if ($fem === 2) {
|
88
|
5 |
|
$this->data->arrUnits[0] = 'одна ';
|
89
|
5 |
|
$this->data->arrUnits[1] = 'две ';
|
90
|
|
|
} else {
|
91
|
8 |
|
$this->data->arrUnits[0] = 'один ';
|
92
|
8 |
|
$this->data->arrUnits[1] = 'два ';
|
93
|
|
|
}
|
94
|
8 |
|
}
|
95
|
|
|
|
96
|
8 |
|
private function makeWords(int $cChunk): string
|
97
|
|
|
{
|
98
|
8 |
|
$resWords = '';
|
99
|
8 |
|
$cent = (int)($cChunk / 100);
|
100
|
8 |
|
$decs = $cChunk % 100;
|
101
|
8 |
|
if ($cent >= 1) {
|
102
|
6 |
|
$resWords .= $this->data->arrHundreds[$cent - 1];
|
103
|
|
|
}
|
104
|
8 |
|
if ($decs >= 1 && $decs <= 19) {
|
105
|
8 |
|
$resWords .= $this->data->arrUnits[$decs - 1];
|
106
|
8 |
|
$decs = 0;
|
107
|
|
|
}
|
108
|
8 |
|
if ($decs !== 0) {
|
109
|
6 |
|
$resWords .= $this->data->arrTens[$decs / 10 - 1];
|
110
|
|
|
}
|
111
|
8 |
|
if ($decs % 10 !== 0) {
|
112
|
6 |
|
$resWords .= $this->data->arrUnits[$decs % 10 - 1];
|
113
|
|
|
}
|
114
|
8 |
|
return $resWords;
|
115
|
|
|
}
|
116
|
|
|
|
117
|
8 |
|
private function getRegister(int $chunkPos, int $chunkData): string
|
118
|
|
|
{
|
119
|
8 |
|
$subResult = '';
|
120
|
8 |
|
$lastDigits = $chunkData % 100;
|
121
|
8 |
|
$exponent = $this->data->arrExponents[$chunkPos];
|
122
|
|
|
|
123
|
8 |
|
if (!$this->currency && $chunkPos === 1) {
|
124
|
7 |
|
return $subResult;
|
125
|
|
|
}
|
126
|
5 |
|
return $exponent . $this->getSuffix($lastDigits, $chunkPos);
|
127
|
|
|
}
|
128
|
|
|
|
129
|
5 |
|
private function getSuffix(int $lastDigits, int $group): string
|
130
|
|
|
{
|
131
|
5 |
|
$group = $group > 3 ? 3 : $group;
|
132
|
5 |
|
$last = $lastDigits % 10;
|
133
|
5 |
|
$result = $this->data->arrSuffix[2][$group];
|
134
|
|
|
|
135
|
5 |
|
if ($lastDigits >= 11 && $lastDigits <= 14) {
|
|
|
|
|
136
|
5 |
|
} elseif ($last === 1) {
|
137
|
4 |
|
$result = $this->data->arrSuffix[0][$group];
|
138
|
5 |
|
} elseif ($last >= 2 && $last <= 4) {
|
139
|
4 |
|
$result = $this->data->arrSuffix[1][$group];
|
140
|
|
|
}
|
141
|
5 |
|
return $result;
|
142
|
|
|
}
|
143
|
|
|
}
|
144
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.