Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.14% |
34 / 35 |
|
93.75% |
15 / 16 |
CRAP | |
0.00% |
0 / 1 |
Binary | |
97.14% |
34 / 35 |
|
93.75% |
15 / 16 |
23 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRange | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
isEquals | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toHex | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toBase64 | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
fromBase64 | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
hasNull | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
toString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
toArray | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
offsetExists | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
offsetGet | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
offsetSet | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
offsetUnset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getIterator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
count | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core; |
6 | |
7 | use ArrayAccess; |
8 | use ArrayIterator; |
9 | use Countable; |
10 | use Iterator; |
11 | use IteratorAggregate; |
12 | use Stringable; |
13 | use TypeError; |
14 | use PeServer\Core\Collection\ArrayAccessHelper; |
15 | use PeServer\Core\Throws\ArgumentException; |
16 | use PeServer\Core\Throws\IndexOutOfRangeException; |
17 | use PeServer\Core\Throws\NotSupportedException; |
18 | use PeServer\Core\Throws\NullByteStringException; |
19 | use PeServer\Core\Throws\BinaryException; |
20 | use PeServer\Core\Throws\Throws; |
21 | use ValueError; |
22 | |
23 | /** |
24 | * PHP文字列がバイトデータなのか普通の文字列なのかよくわからん。 |
25 | * |
26 | * ソース上の型を明示するだけの目的で、効率とかは特になにもない。 |
27 | * あとUTF8で動くこと前提。 |
28 | * |
29 | * @phpstan-type Byte int<0,255> |
30 | * @implements ArrayAccess<non-negative-int,Byte> |
31 | * @implements IteratorAggregate<non-negative-int,Byte> |
32 | */ |
33 | readonly final class Binary implements ArrayAccess, IteratorAggregate, Countable, Stringable |
34 | { |
35 | /** |
36 | * 生成。 |
37 | * |
38 | * @param string $raw バイトデータとして扱う文字列。 |
39 | */ |
40 | public function __construct( |
41 | public readonly string $raw |
42 | ) { |
43 | //NOP |
44 | } |
45 | |
46 | #region function |
47 | |
48 | /** |
49 | * `substr` ラッパー。 |
50 | * |
51 | * @param int $index |
52 | * @param int|null $length |
53 | * @return self |
54 | */ |
55 | public function getRange(int $index, ?int $length = null): self |
56 | { |
57 | $raw = substr($this->raw, $index, $length); |
58 | return new self($raw); |
59 | } |
60 | |
61 | /** |
62 | * 指定したバイナリと等しいか。 |
63 | * |
64 | * @param self $target 対象バイナリ。 |
65 | * @return bool 等しいか。 |
66 | */ |
67 | public function isEquals(self $target): bool |
68 | { |
69 | return $this->raw === $target->raw; |
70 | } |
71 | |
72 | /** |
73 | * 16進数文字列に変換。 |
74 | * |
75 | * `bin2hex` ラッパー。 |
76 | * |
77 | * @return string `[0-9a-f]{2}*` で構成された文字列。 |
78 | * @see https://www.php.net/manual/function.bin2hex.php |
79 | */ |
80 | public function toHex(): string |
81 | { |
82 | return bin2hex($this->raw); |
83 | } |
84 | |
85 | // public function convert(int $from, int $to): string |
86 | // { |
87 | // return base_convert($this->raw, $from, $to); |
88 | // } |
89 | |
90 | /** |
91 | * base64 文字列に変換。 |
92 | * |
93 | * `base64_encode` ラッパー。 |
94 | * |
95 | * @return string |
96 | * @see https://www.php.net/manual/function.base64-encode.php |
97 | */ |
98 | public function toBase64(): string |
99 | { |
100 | return base64_encode($this->raw); |
101 | } |
102 | |
103 | /** |
104 | * base64 文字列から Binary を取得。 |
105 | * |
106 | * `base64_decode` ラッパー。 |
107 | * |
108 | * @param string $base64 |
109 | * @return Binary |
110 | * @throws ArgumentException 変換失敗。 |
111 | * @see https://www.php.net/manual/function.base64-decode.php |
112 | */ |
113 | public static function fromBase64(string $base64): Binary |
114 | { |
115 | $value = base64_decode($base64, true); |
116 | if ($value === false) { |
117 | throw new ArgumentException('$base64'); |
118 | } |
119 | |
120 | return new Binary($value); |
121 | } |
122 | |
123 | /** |
124 | * NULLバイトを持つか。 |
125 | * |
126 | * @return boolean NULLバイトを持つ。 |
127 | */ |
128 | public function hasNull(): bool |
129 | { |
130 | $nullIndex = mb_strpos($this->raw, "\0"); |
131 | return $nullIndex !== false; |
132 | } |
133 | |
134 | /** |
135 | * バイトデータを文字列に変換。 |
136 | * |
137 | * @return string |
138 | * @throws NullByteStringException NULLバイトが存在する。 |
139 | */ |
140 | public function toString(): string |
141 | { |
142 | if ($this->hasNull()) { |
143 | throw new NullByteStringException(); |
144 | } |
145 | |
146 | return $this->raw; |
147 | } |
148 | |
149 | /** |
150 | * 配列化。 |
151 | * |
152 | * `unpack` ラッパー。 |
153 | * |
154 | * @param string $format |
155 | * @param int $offset |
156 | * @phpstan-param non-negative-int $offset |
157 | * @return array<mixed> |
158 | * @see https://www.php.net/manual/function.unpack.php |
159 | */ |
160 | public function toArray(string $format, int $offset = 0): array |
161 | { |
162 | $result = Throws::wrap(ValueError::class, BinaryException::class, fn () => unpack($format, $this->raw, $offset)); |
163 | if ($result === false) { |
164 | throw new BinaryException(); |
165 | } |
166 | |
167 | /** @var array<mixed> */ |
168 | return $result; |
169 | } |
170 | |
171 | |
172 | #endregion |
173 | |
174 | #region ArrayAccess |
175 | |
176 | /** |
177 | * @param int $offset |
178 | * @phpstan-param non-negative-int $offset |
179 | * @return bool |
180 | * @see ArrayAccess::offsetExists |
181 | */ |
182 | public function offsetExists(mixed $offset): bool |
183 | { |
184 | if (!ArrayAccessHelper::offsetExistsUInt($offset)) { //@phpstan-ignore-line [DOCTYPE] non-negative-int |
185 | return false; |
186 | } |
187 | |
188 | if (strlen($this->raw) <= $offset) { |
189 | return false; |
190 | } |
191 | |
192 | return true; |
193 | } |
194 | |
195 | /** |
196 | * @param int $offset |
197 | * @phpstan-param non-negative-int $offset |
198 | * @return int |
199 | * @phpstan-return Byte |
200 | * @throws TypeError |
201 | * @throws IndexOutOfRangeException |
202 | * @see ArrayAccess::offsetGet |
203 | */ |
204 | public function offsetGet(mixed $offset): mixed |
205 | { |
206 | ArrayAccessHelper::offsetGetUInt($offset); //@phpstan-ignore-line [DOCTYPE] non-negative-int |
207 | |
208 | if (strlen($this->raw) <= $offset) { |
209 | throw new IndexOutOfRangeException((string)$offset); |
210 | } |
211 | |
212 | /** @phpstan-var Byte */ |
213 | return ord($this->raw[$offset]); |
214 | } |
215 | |
216 | /** @throws NotSupportedException */ |
217 | public function offsetSet(mixed $offset, mixed $value): void |
218 | { |
219 | throw new NotSupportedException(); |
220 | } |
221 | |
222 | /** @throws NotSupportedException */ |
223 | public function offsetUnset(mixed $offset): void |
224 | { |
225 | throw new NotSupportedException(); |
226 | } |
227 | |
228 | #endregion |
229 | |
230 | #region IteratorAggregate |
231 | |
232 | public function getIterator(): Iterator |
233 | { |
234 | return new ArrayIterator(str_split($this->raw)); |
235 | } |
236 | |
237 | #endregion |
238 | |
239 | #region Countable |
240 | |
241 | /** |
242 | * バイト長を取得。 |
243 | * |
244 | * `strlen` ラッパー。 |
245 | * |
246 | * @return int |
247 | * @phpstan-return non-negative-int |
248 | * @see https://www.php.net/manual/function.strlen.php |
249 | */ |
250 | public function count(): int |
251 | { |
252 | return strlen($this->raw); |
253 | } |
254 | |
255 | #endregion |
256 | |
257 | #region Stringable |
258 | |
259 | public function __toString(): string |
260 | { |
261 | if ($this->hasNull()) { |
262 | return $this->toHex(); |
263 | } |
264 | |
265 | return $this->raw; |
266 | } |
267 | |
268 | #endregion |
269 | } |