Passed
Push — v5 ( 700af4...22f167 )
by smiley
01:39
created

Number::decodeSegment()   B

Complexity

Conditions 11
Paths 16

Size

Total Lines 66
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 33
nc 16
nop 2
dl 0
loc 66
rs 7.3166
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
 * Class Number
4
 *
5
 * @filesource   Number.php
6
 * @created      26.11.2015
7
 * @package      chillerlan\QRCode\Data
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode\Data;
14
15
use chillerlan\QRCode\Common\{BitBuffer, Mode};
16
17
use function array_flip, ceil, ord, sprintf, str_split, substr;
18
19
/**
20
 * Numeric mode: decimal digits 0 to 9
21
 *
22
 * ISO/IEC 18004:2000 Section 8.3.2
23
 * ISO/IEC 18004:2000 Section 8.4.2
24
 */
25
final class Number extends QRDataModeAbstract{
26
27
	/**
28
	 * @var int[]
29
	 */
30
	protected const CHAR_MAP_NUMBER = [
31
		'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
32
	];
33
34
	protected static int $datamode = Mode::DATA_NUMBER;
35
36
	/**
37
	 * @inheritdoc
38
	 */
39
	public function getLengthInBits():int{
40
		return (int)ceil($this->getCharCount() * (10 / 3));
41
	}
42
43
	/**
44
	 * @inheritdoc
45
	 */
46
	public static function validateString(string $string):bool{
47
48
		foreach(str_split($string) as $chr){
49
			if(!isset(self::CHAR_MAP_NUMBER[$chr])){
50
				return false;
51
			}
52
		}
53
54
		return true;
55
	}
56
57
	/**
58
	 * @inheritdoc
59
	 */
60
	public function write(BitBuffer $bitBuffer, int $versionNumber):void{
61
		$len = $this->getCharCount();
62
63
		$bitBuffer
64
			->put($this::$datamode, 4)
65
			->put($len, Mode::getLengthBitsForVersion($this::$datamode, $versionNumber))
66
		;
67
68
		$i = 0;
69
70
		// encode numeric triplets in 10 bits
71
		while($i + 2 < $len){
72
			$bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10);
73
			$i += 3;
74
		}
75
76
		if($i < $len){
77
78
			// encode 2 remaining numbers in 7 bits
79
			if($len - $i === 2){
80
				$bitBuffer->put($this->parseInt(substr($this->data, $i, 2)), 7);
81
			}
82
			// encode one remaining number in 4 bits
83
			elseif($len - $i === 1){
84
				$bitBuffer->put($this->parseInt(substr($this->data, $i, 1)), 4);
85
			}
86
87
		}
88
89
	}
90
91
	/**
92
	 * get the code for the given numeric string
93
	 *
94
	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
95
	 */
96
	protected function parseInt(string $string):int{
97
		$num = 0;
98
99
		foreach(str_split($string) as $chr){
100
			$c = ord($chr);
101
102
			if(!isset(self::CHAR_MAP_NUMBER[$chr])){
103
				throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
104
			}
105
106
			$c   = $c - 48; // ord('0')
107
			$num = $num * 10 + $c;
108
		}
109
110
		return $num;
111
	}
112
113
	/**
114
	 * @inheritdoc
115
	 *
116
	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
117
	 */
118
	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
119
		$length  = $bitBuffer->read(Mode::getLengthBitsForVersion(self::$datamode, $versionNumber));
120
		$charmap = array_flip(self::CHAR_MAP_NUMBER);
121
122
		// @todo
123
		$toNumericChar = function(int $ord) use ($charmap):string{
124
125
			if(isset($charmap[$ord])){
126
				return $charmap[$ord];
127
			}
128
129
			throw new QRCodeDataException('invalid character value: '.$ord);
130
		};
131
132
		$result = '';
133
		// Read three digits at a time
134
		while($length >= 3){
135
			// Each 10 bits encodes three digits
136
			if($bitBuffer->available() < 10){
137
				throw new QRCodeDataException('not enough bits available');
138
			}
139
140
			$threeDigitsBits = $bitBuffer->read(10);
141
142
			if($threeDigitsBits >= 1000){
143
				throw new QRCodeDataException('error decoding numeric value');
144
			}
145
146
			$result .= $toNumericChar($threeDigitsBits / 100);
147
			$result .= $toNumericChar(($threeDigitsBits / 10) % 10);
148
			$result .= $toNumericChar($threeDigitsBits % 10);
149
150
			$length -= 3;
151
		}
152
153
		if($length === 2){
154
			// Two digits left over to read, encoded in 7 bits
155
			if($bitBuffer->available() < 7){
156
				throw new QRCodeDataException('not enough bits available');
157
			}
158
159
			$twoDigitsBits = $bitBuffer->read(7);
160
161
			if($twoDigitsBits >= 100){
162
				throw new QRCodeDataException('error decoding numeric value');
163
			}
164
165
			$result .= $toNumericChar($twoDigitsBits / 10);
166
			$result .= $toNumericChar($twoDigitsBits % 10);
167
		}
168
		elseif($length === 1){
169
			// One digit left over to read
170
			if($bitBuffer->available() < 4){
171
				throw new QRCodeDataException('not enough bits available');
172
			}
173
174
			$digitBits = $bitBuffer->read(4);
175
176
			if($digitBits >= 10){
177
				throw new QRCodeDataException('error decoding numeric value');
178
			}
179
180
			$result .= $toNumericChar($digitBits);
181
		}
182
183
		return $result;
184
	}
185
186
}
187