Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
76.00% |
38 / 50 |
|
53.33% |
8 / 15 |
CRAP | |
0.00% |
0 / 1 |
Encoding | |
76.00% |
38 / 50 |
|
53.33% |
8 / 15 |
33.64 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
throwIfInvalidEncodingName | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getEncodingNames | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefaultEncoding | |
33.33% |
2 / 6 |
|
0.00% |
0 / 1 |
5.67 | |||
setDefaultEncoding | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
getAscii | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUtf8 | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUtf16 | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUtf32 | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getShiftJis | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBinary | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
3.21 | |||
toString | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
3.21 | |||
getAliasNames | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
isValid | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getByteOrderMark | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core; |
6 | |
7 | use ValueError; |
8 | use PeServer\Core\Binary; |
9 | use PeServer\Core\Collection\Arr; |
10 | use PeServer\Core\Throws\ArgumentException; |
11 | use PeServer\Core\Throws\EncodingException; |
12 | use PeServer\Core\Throws\InvalidOperationException; |
13 | use PeServer\Core\Throws\Throws; |
14 | use Throwable; |
15 | |
16 | /** |
17 | * エンコーディング処理。 |
18 | * |
19 | * コンストラクタで指定されたエンコード名に対して通常文字列へあれこれする。 |
20 | */ |
21 | class Encoding |
22 | { |
23 | #region define |
24 | |
25 | /** アスキー */ |
26 | public const ENCODE_ASCII = 'ASCII'; |
27 | |
28 | /** UTF-7 */ |
29 | public const ENCODE_UTF7 = 'UTF-7'; |
30 | /** UTF-8 */ |
31 | public const ENCODE_UTF8 = 'UTF-8'; |
32 | /** UTF-16 */ |
33 | public const ENCODE_UTF16_PLAIN = 'UTF-16'; |
34 | public const ENCODE_UTF16_BE = 'UTF-16BE'; |
35 | public const ENCODE_UTF16_LE = 'UTF-16LE'; |
36 | public const ENCODE_UTF16_DEFAULT = self::ENCODE_UTF16_LE; |
37 | /** UTF-32 */ |
38 | public const ENCODE_UTF32_PLAIN = 'UTF-32'; |
39 | public const ENCODE_UTF32_BE = 'UTF-32BE'; |
40 | public const ENCODE_UTF32_LE = 'UTF-32LE'; |
41 | public const ENCODE_UTF32_DEFAULT = self::ENCODE_UTF32_LE; |
42 | |
43 | /** SJIS(SHIFT-JIS) */ |
44 | public const ENCODE_SJIS_PLAIN = 'SJIS'; |
45 | /** SJIS(CP932) */ |
46 | public const ENCODE_SJIS_WIN31J = 'CP932'; |
47 | /** SJIS(Windows) */ |
48 | public const ENCODE_SJIS_WIN = 'SJIS-win'; |
49 | /** SJIS(Shift_JIS-2004) */ |
50 | public const ENCODE_SJIS_2004 = 'SJIS-2004'; |
51 | /** SJIS(何も考えず使う用) */ |
52 | public const ENCODE_SJIS_DEFAULT = self::ENCODE_SJIS_WIN31J; |
53 | |
54 | public const ENCODE_JIS_PLAIN = 'JIS'; |
55 | public const ENCODE_JIS_DEFAULT = self::ENCODE_JIS_PLAIN; |
56 | |
57 | public const ENCODE_EUC_JP_PLAIN = 'EUC-JP'; |
58 | public const ENCODE_EUC_JP_WIN = 'eucJP-win'; |
59 | public const ENCODE_EUC_JP_DEFAULT = self::ENCODE_EUC_JP_WIN; |
60 | |
61 | #endregion |
62 | |
63 | #region variable |
64 | |
65 | /** |
66 | * キャッシュされたエンコーディング名一覧。 |
67 | * |
68 | * @var string[]|null |
69 | */ |
70 | protected static ?array $cacheNames = null; |
71 | |
72 | /** |
73 | * デフォルトエンコーディング。 |
74 | * |
75 | * `setDefaultEncoding` で設定され、 |
76 | * `getDefaultEncoding` で使用される。 |
77 | * |
78 | * ただし `getDefaultEncoding` で本プロパティ未設定の場合は上書きされる。 |
79 | * |
80 | * @var self|null |
81 | */ |
82 | private static ?self $defaultEncoding = null; |
83 | |
84 | /** |
85 | * エンコード名。 |
86 | */ |
87 | public readonly string $name; |
88 | |
89 | #endregion |
90 | |
91 | /** |
92 | * 生成 |
93 | * |
94 | * @param string $name エンコード名。 |
95 | * @phpstan-param non-empty-string|Encoding::ENCODE_* $name |
96 | */ |
97 | public function __construct(string $name) |
98 | { |
99 | self::throwIfInvalidEncodingName($name); |
100 | $this->name = $name; |
101 | } |
102 | |
103 | #region function |
104 | |
105 | /** |
106 | * エンコーディング名が正しいか。 |
107 | * |
108 | * @param string $name |
109 | * @throws ArgumentException 正しくない。 |
110 | */ |
111 | private static function throwIfInvalidEncodingName(string $name): void |
112 | { |
113 | $names = self::getEncodingNames(); |
114 | if (!Arr::containsValue($names, $name)) { |
115 | throw new ArgumentException('$name'); |
116 | } |
117 | } |
118 | |
119 | /** |
120 | * エンコーディング名一覧を取得。 |
121 | * |
122 | * キャッシュされる。 |
123 | * |
124 | * `mb_list_encodings` ラッパー。 |
125 | * |
126 | * @return string[] |
127 | * @see https://www.php.net/manual/function.mb-list-encodings.php |
128 | */ |
129 | public static function getEncodingNames(): array |
130 | { |
131 | return self::$cacheNames ??= mb_list_encodings(); |
132 | } |
133 | |
134 | /** |
135 | * `mb_internal_encoding` ラッパー |
136 | * |
137 | * @return Encoding |
138 | * @see https://www.php.net/manual/function.mb-internal-encoding.php |
139 | */ |
140 | public static function getDefaultEncoding(): Encoding |
141 | { |
142 | if (self::$defaultEncoding === null) { |
143 | $name = mb_internal_encoding(); |
144 | if (Text::isNullOrEmpty($name)) { |
145 | throw new InvalidOperationException(); |
146 | } |
147 | |
148 | return self::$defaultEncoding = new Encoding($name); |
149 | } |
150 | |
151 | return self::$defaultEncoding; |
152 | } |
153 | |
154 | /** |
155 | * `mb_internal_encoding` ラッパー |
156 | * |
157 | * @param Encoding $encoding |
158 | * @throws ArgumentException |
159 | * @see https://www.php.net/manual/function.mb-internal-encoding.php |
160 | */ |
161 | public static function setDefaultEncoding(Encoding $encoding): void |
162 | { |
163 | $result = mb_internal_encoding($encoding->name); |
164 | if (!$result) { |
165 | throw new ArgumentException('$encoding'); |
166 | } |
167 | |
168 | self::$defaultEncoding = $encoding; |
169 | } |
170 | |
171 | /** |
172 | * ASCIIエンコーディング。 |
173 | * |
174 | * @return Encoding |
175 | */ |
176 | public static function getAscii(): Encoding |
177 | { |
178 | return new Encoding(self::ENCODE_ASCII); |
179 | } |
180 | |
181 | /** |
182 | * UTF8エンコーディング。 |
183 | * |
184 | * @return Encoding |
185 | */ |
186 | public static function getUtf8(): Encoding |
187 | { |
188 | return new Encoding(self::ENCODE_UTF8); |
189 | } |
190 | |
191 | /** |
192 | * UTF16エンコーディング。 |
193 | * |
194 | * @return Encoding |
195 | */ |
196 | public static function getUtf16(): Encoding |
197 | { |
198 | return new Encoding(self::ENCODE_UTF16_DEFAULT); |
199 | } |
200 | |
201 | /** |
202 | * UTF32エンコーディング。 |
203 | * |
204 | * @return Encoding |
205 | */ |
206 | public static function getUtf32(): Encoding |
207 | { |
208 | return new Encoding(self::ENCODE_UTF32_DEFAULT); |
209 | } |
210 | |
211 | /** |
212 | * SJISエンコーディング。 |
213 | * |
214 | * @return Encoding |
215 | */ |
216 | public static function getShiftJis(): Encoding |
217 | { |
218 | return new Encoding(self::ENCODE_SJIS_DEFAULT); |
219 | } |
220 | |
221 | /** |
222 | * 文字列(デフォルトエンコーディング)を現在のエンコーディングへ変換。 |
223 | * |
224 | * @param string $input |
225 | * @return Binary |
226 | * @throws EncodingException |
227 | * @see https://www.php.net/manual/function.mb-convert-encoding.php |
228 | */ |
229 | public function getBinary(string $input): Binary |
230 | { |
231 | $default = self::getDefaultEncoding(); |
232 | if ($default->name === $this->name) { |
233 | return new Binary($input); |
234 | } |
235 | |
236 | try { |
237 | $output = mb_convert_encoding($input, $this->name, $default->name); |
238 | return new Binary($output); |
239 | } catch (Throwable $ex) { |
240 | Throws::reThrow(EncodingException::class, $ex); |
241 | } |
242 | } |
243 | |
244 | /** |
245 | * 現在のエンコーディングデータを文字列(デフォルトエンコーディング)へ変換。 |
246 | * |
247 | * @param Binary $input |
248 | * @return string |
249 | * @throws EncodingException |
250 | * @see https://www.php.net/manual/function.mb-convert-encoding.php |
251 | */ |
252 | public function toString(Binary $input): string |
253 | { |
254 | $default = self::getDefaultEncoding(); |
255 | if ($default->name === $this->name) { |
256 | return $input->raw; |
257 | } |
258 | |
259 | try { |
260 | $output = mb_convert_encoding($input->raw, $default->name, $this->name); |
261 | return $output; |
262 | } catch (Throwable $ex) { |
263 | Throws::reThrow(EncodingException::class, $ex); |
264 | } |
265 | } |
266 | |
267 | /** |
268 | * 現在のエンコーディング・タイプのエイリアスを取得。 |
269 | * |
270 | * @return string[] |
271 | * @see https://www.php.net/manual/function.mb-encoding-aliases.php |
272 | */ |
273 | public static function getAliasNames(string $encoding): array |
274 | { |
275 | try { |
276 | $names = mb_encoding_aliases($encoding); |
277 | return $names; |
278 | } catch (Throwable $ex) { |
279 | Throws::reThrow(EncodingException::class, $ex); |
280 | } |
281 | } |
282 | |
283 | /** |
284 | * 現在のエンコーディングで有効か。 |
285 | * |
286 | * @param Binary $input |
287 | * @return bool 有効か。 |
288 | */ |
289 | public function isValid(Binary $input): bool |
290 | { |
291 | return mb_check_encoding($input->raw, $this->name); |
292 | } |
293 | |
294 | /** |
295 | * 現在のエンコーディングからBOMを取得する。 |
296 | * |
297 | * @return Binary エンコーディング対するBOM。対応しない場合空のバイナリ。 |
298 | */ |
299 | public function getByteOrderMark(): Binary |
300 | { |
301 | //wikipedia の bom 見たけどわからん。 |
302 | //TODO: 定義しているエンコーディングともあわんし、どうしたもんか |
303 | $bomMap = [ |
304 | 'UTF-8' => "\xEF\xBB\xBF", |
305 | 'UTF-16BE' => "\xFE\xFF", |
306 | 'UTF-16LE' => "\xFF\xFE", |
307 | 'UTF-32BE' => "\x00\x00\xFE\xFF", |
308 | 'UTF-32LE' => "\xFF\xFE\x00\x00", |
309 | ]; |
310 | |
311 | if (isset($bomMap[$this->name])) { |
312 | return new Binary($bomMap[$this->name]); |
313 | } |
314 | |
315 | return new Binary(''); |
316 | } |
317 | |
318 | #endregion |
319 | } |