Passed
Push — main ( 738294...48b6c2 )
by smiley
01:56
created

Kanji::validateString()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 10
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 19
rs 8.8333
1
<?php
2
/**
3
 * Class Kanji
4
 *
5
 * @created      25.11.2015
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2015 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\QRCode\Data;
12
13
use chillerlan\QRCode\Common\{BitBuffer, Mode};
14
15
use function chr, implode, mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
16
17
/**
18
 * Kanji mode: double-byte characters from the Shift JIS character set
19
 *
20
 * ISO/IEC 18004:2000 Section 8.3.5
21
 * ISO/IEC 18004:2000 Section 8.4.5
22
 */
23
final class Kanji extends QRDataModeAbstract{
24
25
	/**
26
	 * @inheritDoc
27
	 */
28
	protected static int $datamode = Mode::KANJI;
29
30
	/**
31
	 *
32
	 */
33
	public function __construct(string $data){
34
		parent::__construct($data);
35
36
		$this->data = mb_convert_encoding($this->data, 'SJIS', mb_detect_encoding($this->data));
37
	}
38
39
	/**
40
	 * @inheritDoc
41
	 */
42
	protected function getCharCount():int{
43
		return mb_strlen($this->data, 'SJIS');
44
	}
45
46
	/**
47
	 * @inheritDoc
48
	 */
49
	public function getLengthInBits():int{
50
		return $this->getCharCount() * 13;
51
	}
52
53
	/**
54
	 * checks if a string qualifies as Kanji
55
	 */
56
	public static function validateString(string $string):bool{
57
		$i   = 0;
58
		$len = strlen($string);
59
60
		if($len < 2){
61
			return false;
62
		}
63
64
		while($i + 1 < $len){
65
			$c = ((0xff & ord($string[$i])) << 8) | (0xff & ord($string[$i + 1]));
66
67
			if(!($c >= 0x8140 && $c <= 0x9ffc) && !($c >= 0xe040 && $c <= 0xebbf)){
68
				return false;
69
			}
70
71
			$i += 2;
72
		}
73
74
		return $i >= $len;
75
	}
76
77
	/**
78
	 * @inheritDoc
79
	 *
80
	 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
81
	 */
82
	public function write(BitBuffer $bitBuffer, int $versionNumber):void{
83
84
		$bitBuffer
85
			->put($this::$datamode, 4)
86
			->put($this->getCharCount(), Mode::getLengthBitsForVersion($this::$datamode, $versionNumber))
87
		;
88
89
		$len = strlen($this->data);
90
91
		for($i = 0; $i + 1 < $len; $i += 2){
92
			$c = ((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[$i + 1]));
93
94
			if($c >= 0x8140 && $c <= 0x9ffC){
95
				$c -= 0x8140;
96
			}
97
			elseif($c >= 0xe040 && $c <= 0xebbf){
98
				$c -= 0xc140;
99
			}
100
			else{
101
				throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
102
			}
103
104
			$bitBuffer->put(((($c >> 8) & 0xff) * 0xc0) + ($c & 0xff), 13);
105
		}
106
107
		if($i < $len){
108
			throw new QRCodeDataException(sprintf('illegal char at %d', $i + 1));
109
		}
110
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
121
		if($bitBuffer->available() < $length * 13){
122
			throw new QRCodeDataException('not enough bits available');
123
		}
124
125
		$buffer = [];
126
		$offset = 0;
127
128
		while($length > 0){
129
			// Each 13 bits encodes a 2-byte character
130
			$twoBytes          = $bitBuffer->read(13);
131
			$assembledTwoBytes = (($twoBytes / 0x0c0) << 8) | ($twoBytes % 0x0c0);
132
133
			$assembledTwoBytes += ($assembledTwoBytes < 0x01f00)
134
				? 0x08140  // In the 0x8140 to 0x9FFC range
135
				: 0x0c140; // In the 0xE040 to 0xEBBF range
136
137
			$buffer[$offset]     = chr(0xff & ($assembledTwoBytes >> 8));
138
			$buffer[$offset + 1] = chr(0xff & $assembledTwoBytes);
139
			$offset              += 2;
140
			$length--;
141
		}
142
143
		return mb_convert_encoding(implode($buffer), mb_internal_encoding(), 'SJIS');
0 ignored issues
show
Bug introduced by
It seems like mb_internal_encoding() can also be of type true; however, parameter $to_encoding of mb_convert_encoding() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
		return mb_convert_encoding(implode($buffer), /** @scrutinizer ignore-type */ mb_internal_encoding(), 'SJIS');
Loading history...
144
	}
145
146
}
147