Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.96% |
140 / 149 |
|
74.19% |
23 / 31 |
CRAP | |
0.00% |
0 / 1 |
Text | |
93.96% |
140 / 149 |
|
74.19% |
23 / 31 |
80.38 | |
0.00% |
0 / 1 |
isNullOrEmpty | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
isNullOrWhiteSpace | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
requireNotNullOrEmpty | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
requireNotNullOrWhiteSpace | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getLength | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getByteCount | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
fromCodePointCore | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
fromCodePoint | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
replaceMap | |
88.89% |
16 / 18 |
|
0.00% |
0 / 1 |
3.01 | |||
format | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
formatNumber | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPosition | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getLastPosition | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
startsWith | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
6.04 | |||
endsWith | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
6.04 | |||
contains | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
6.05 | |||
substring | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
toLower | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toUpper | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
split | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
splitLines | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
join | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
createTrimPattern | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
trim | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
trimStart | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
trimEnd | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
dump | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
replace | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
5.20 | |||
repeat | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
toCharacters | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
toString | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core; |
6 | |
7 | use Throwable; |
8 | use PeServer\Core\Throws\ArgumentException; |
9 | use PeServer\Core\Throws\Throws; |
10 | use PeServer\Core\Throws\RegexException; |
11 | use PeServer\Core\Throws\StringException; |
12 | |
13 | /** |
14 | * 文字列操作。 |
15 | */ |
16 | abstract class Text |
17 | { |
18 | #region define |
19 | |
20 | /** トリム対象文字一覧。 */ |
21 | public const TRIM_CHARACTERS = " \n\r\t\v\0 "; |
22 | /** PHP の使ってる対象文字一覧 */ |
23 | public const TRIM_PHP_CHARACTERS = " \t\n\r\0\x0B"; |
24 | private const TRIM_TARGET_START = -1; |
25 | private const TRIM_TARGET_END = 1; |
26 | private const TRIM_TARGET_BOTH = 0; |
27 | |
28 | /** 空文字列。 */ |
29 | public const EMPTY = ''; |
30 | |
31 | #endregion |
32 | |
33 | #region function |
34 | |
35 | /** |
36 | * 文字列が `null` か空か |
37 | * |
38 | * @param string|null $s 対象文字列。 |
39 | * @return bool 真: `null`か空。 |
40 | * @phpstan-assert-if-false non-empty-string $s |
41 | */ |
42 | public static function isNullOrEmpty(?string $s): bool |
43 | { |
44 | if ($s === null) { |
45 | return true; |
46 | } |
47 | |
48 | if ($s === '0') { |
49 | return false; |
50 | } |
51 | |
52 | return empty($s); |
53 | } |
54 | |
55 | /** |
56 | * 文字列がnullかホワイトスペースのみで構築されているか |
57 | * |
58 | * `TRIM_CHARACTERS` がホワイトスペースとして扱われる。 |
59 | * |
60 | * @param string|null $s 対象文字列。 |
61 | * @return bool 真: nullかホワイトスペースのみ。 |
62 | * @phpstan-assert-if-false non-empty-string $s |
63 | */ |
64 | public static function isNullOrWhiteSpace(?string $s): bool |
65 | { |
66 | if (self::isNullOrEmpty($s)) { |
67 | return true; |
68 | } |
69 | |
70 | return strlen(self::trim($s)) === 0; |
71 | } |
72 | |
73 | /** |
74 | * 文字列が `null` か空の場合に代替文字列を返す。 |
75 | * |
76 | * @param string|null $s |
77 | * @param string $fallback |
78 | * @return string |
79 | */ |
80 | public static function requireNotNullOrEmpty(?string $s, string $fallback): string |
81 | { |
82 | if (self::isNullOrEmpty($s)) { |
83 | return $fallback; |
84 | } |
85 | |
86 | return $s; |
87 | } |
88 | |
89 | /** |
90 | * 文字列がnullかホワイトスペースのみで構築されている場合に代替文字列を返す。 |
91 | * |
92 | * @param string|null $s |
93 | * @param string $fallback |
94 | * @return string |
95 | */ |
96 | public static function requireNotNullOrWhiteSpace(?string $s, string $fallback): string |
97 | { |
98 | if (self::isNullOrWhiteSpace($s)) { |
99 | return $fallback; |
100 | } |
101 | |
102 | return $s; |
103 | } |
104 | |
105 | /** |
106 | * 文字列長を取得。 |
107 | * |
108 | * `mb_strlen` ラッパー。 |
109 | * |
110 | * @param string $value 対象文字列。 |
111 | * @return int 文字数。 |
112 | * @phpstan-return non-negative-int |
113 | * @see https://www.php.net/manual/function.mb-strlen.php |
114 | */ |
115 | public static function getLength(string $value): int |
116 | { |
117 | return mb_strlen($value); |
118 | } |
119 | |
120 | /* |
121 | public static function getCharacterLength(string $value): int |
122 | { |
123 | $length = self::getLength($value); |
124 | if($length < 2) { |
125 | return $length; |
126 | } |
127 | return \grapheme_strlen($value); |
128 | } |
129 | */ |
130 | |
131 | /** |
132 | * 文字列バイト数を取得。 |
133 | * |
134 | * `strlen` ラッパー。 |
135 | * |
136 | * @param string $value 対象文字列。 |
137 | * @return int バイト数。 |
138 | * @phpstan-return non-negative-int |
139 | * @see https://www.php.net/manual/function.strlen.php |
140 | */ |
141 | public static function getByteCount(string $value): int |
142 | { |
143 | return strlen($value); |
144 | } |
145 | |
146 | private static function fromCodePointCore(int $value): string |
147 | { |
148 | $single = mb_chr($value); |
149 | if ($single === false) { //@phpstan-ignore-line [PHP_VERSION] |
150 | throw new ArgumentException(); |
151 | } |
152 | |
153 | return $single; |
154 | } |
155 | |
156 | /** |
157 | * Unicode のコードポイントに対応する文字を返す。 |
158 | * |
159 | * `mb_chr` ラッパー。 |
160 | * |
161 | * @param int|int[] $value |
162 | * @phpstan-param non-negative-int|non-negative-int[] $value |
163 | * @return string |
164 | * @see https://www.php.net/manual/function.mb-chr.php |
165 | * @throws ArgumentException |
166 | */ |
167 | public static function fromCodePoint(int|array $value): string |
168 | { |
169 | if (is_int($value)) { |
170 | return self::fromCodePointCore($value); |
171 | } |
172 | |
173 | $result = ''; |
174 | foreach ($value as $cp) { |
175 | if (!is_int($cp)) { //@phpstan-ignore-line [DOCTYPE] |
176 | throw new ArgumentException(); |
177 | } |
178 | |
179 | $result .= self::fromCodePointCore($cp); |
180 | } |
181 | |
182 | return $result; |
183 | } |
184 | |
185 | /** |
186 | * プレースホルダー文字列置き換え処理 |
187 | * |
188 | * @param string $source 元文字列 |
189 | * @phpstan-param literal-string $source |
190 | * @param array<string,string> $map 置き換え対象辞書 |
191 | * @param non-empty-string $head プレースホルダー先頭 |
192 | * @param non-empty-string $tail プレースホルダー終端 |
193 | * @return string 置き換え後文字列 |
194 | * @throws StringException なんかもうあかんかった |
195 | */ |
196 | public static function replaceMap(string $source, array $map, string $head = '{', string $tail = '}'): string |
197 | { |
198 | Throws::throwIfNullOrEmpty($head, Text::EMPTY, StringException::class); |
199 | Throws::throwIfNullOrEmpty($tail, Text::EMPTY, StringException::class); |
200 | |
201 | $regex = new Regex(); |
202 | $escHead = $regex->escape($head); |
203 | $escTail = $regex->escape($tail); |
204 | $pattern = "/$escHead(.+?)$escTail/"; |
205 | |
206 | try { |
207 | $result = $regex->replaceCallback( |
208 | $source, |
209 | $pattern, |
210 | function ($matches) use ($map) { |
211 | if (isset($map[$matches[1]])) { |
212 | return $map[$matches[1]]; |
213 | } |
214 | return Text::EMPTY; |
215 | } |
216 | ); |
217 | |
218 | return $result; |
219 | } catch (Throwable $ex) { |
220 | Throws::reThrow(StringException::class, $ex); |
221 | } |
222 | } |
223 | |
224 | /** |
225 | * `sprintf` ラッパー。 |
226 | * |
227 | * 単純な文字列置き換えであれば `Text::replaceMap` を使用する。 |
228 | * |
229 | * @param string $format |
230 | * @param mixed ...$values |
231 | * @phpstan-param FormatAlias ...$values |
232 | * @return string |
233 | * @see https://www.php.net/manual/function.sprintf.php |
234 | * @see Text::replaceMap() |
235 | */ |
236 | public static function format(string $format, string|int|float ...$values): string |
237 | { |
238 | return sprintf($format, ...$values); |
239 | } |
240 | |
241 | /** |
242 | * 数字を千の位毎にグループ化してフォーマット |
243 | * |
244 | * @param int|float $number フォーマットする数値 |
245 | * @param int $decimals 小数点以下の桁数。 0 を指定すると、 戻り値の $decimalSeparator は省略されます |
246 | * @param string|null $decimalSeparator 小数点を表す区切り文字 |
247 | * @param string|null $thousandsSeparator 千の位毎の区切り文字 |
248 | * @return string 置き換え後文字列 |
249 | * @see https://www.php.net/manual/function.number-format.php |
250 | */ |
251 | public static function formatNumber(int|float $number, int $decimals = 0, ?string $decimalSeparator = '.', ?string $thousandsSeparator = ','): string |
252 | { |
253 | return number_format($number, $decimals, $decimalSeparator, $thousandsSeparator); |
254 | } |
255 | |
256 | /** |
257 | * 文字列位置を取得。 |
258 | * |
259 | * @param string $haystack 対象文字列。 |
260 | * @param string $needle 検索文字列。 |
261 | * @param int $offset 開始文字数目。 |
262 | * @phpstan-param non-negative-int $offset |
263 | * @return int 見つかった文字位置。見つかんない場合は `-1` |
264 | * @phpstan-return non-negative-int|-1 |
265 | * @throws ArgumentException |
266 | */ |
267 | public static function getPosition(string $haystack, string $needle, int $offset = 0): int |
268 | { |
269 | if ($offset < 0) { //@phpstan-ignore-line non-negative-int |
270 | throw new ArgumentException('$offset'); |
271 | } |
272 | |
273 | $result = mb_strpos($haystack, $needle, $offset); |
274 | if ($result === false) { |
275 | return -1; |
276 | } |
277 | |
278 | return $result; |
279 | } |
280 | |
281 | /** |
282 | * 文字列位置を取得。 |
283 | * |
284 | * @param string $haystack 対象文字列。 |
285 | * @param string $needle 検索文字列。 |
286 | * @param int $offset 終端文字数目。 |
287 | * @phpstan-param non-negative-int $offset |
288 | * @return int 見つかった文字位置。見つかんない場合は `-1` |
289 | * @phpstan-return non-negative-int|-1 |
290 | * @throws ArgumentException |
291 | */ |
292 | public static function getLastPosition(string $haystack, string $needle, int $offset = 0): int |
293 | { |
294 | if ($offset < 0) { //@phpstan-ignore-line [DOCTYPE] |
295 | throw new ArgumentException('$offset'); |
296 | } |
297 | |
298 | $result = mb_strrpos($haystack, $needle, $offset); |
299 | if ($result === false) { |
300 | return -1; |
301 | } |
302 | |
303 | return $result; |
304 | } |
305 | |
306 | /** |
307 | * 先頭文字列一致判定。 |
308 | * |
309 | * @param string $haystack 対象文字列。 |
310 | * @param string $needle 検索文字列。 |
311 | * @param boolean $ignoreCase 大文字小文字を無視するか。 |
312 | * @return boolean |
313 | */ |
314 | public static function startsWith(string $haystack, string $needle, bool $ignoreCase): bool |
315 | { |
316 | if (!$ignoreCase && function_exists('str_starts_with')) { |
317 | return str_starts_with($haystack, $needle); |
318 | } |
319 | |
320 | if (self::isNullOrEmpty($needle)) { |
321 | return true; |
322 | } |
323 | if (strlen($haystack) < strlen($needle)) { |
324 | return false; |
325 | } |
326 | |
327 | $word = mb_substr($haystack, 0, mb_strlen($needle)); |
328 | |
329 | if ($ignoreCase) { |
330 | return !strcasecmp($needle, $word); |
331 | } |
332 | return $needle === $word; |
333 | } |
334 | |
335 | /** |
336 | * 終端文字列一致判定。 |
337 | * |
338 | * @param string $haystack 対象文字列。 |
339 | * @param string $needle 検索文字列。 |
340 | * @param boolean $ignoreCase 大文字小文字を無視するか。 |
341 | * @return boolean |
342 | */ |
343 | public static function endsWith(string $haystack, string $needle, bool $ignoreCase): bool |
344 | { |
345 | if (!$ignoreCase && function_exists('str_ends_with')) { |
346 | return str_ends_with($haystack, $needle); |
347 | } |
348 | |
349 | if (self::isNullOrEmpty($needle)) { |
350 | return true; |
351 | } |
352 | if (strlen($haystack) < strlen($needle)) { |
353 | return false; |
354 | } |
355 | |
356 | $word = mb_substr($haystack, -mb_strlen($needle)); |
357 | |
358 | if ($ignoreCase) { |
359 | return !strcasecmp($needle, $word); |
360 | } |
361 | return $needle === $word; |
362 | } |
363 | |
364 | /** |
365 | * 文字列を含んでいるか判定。 |
366 | * |
367 | * @param string $haystack 対象文字列。 |
368 | * @param string $needle 検索文字列。 |
369 | * @param boolean $ignoreCase 大文字小文字を無視するか。 |
370 | * @return boolean |
371 | */ |
372 | public static function contains(string $haystack, string $needle, bool $ignoreCase): bool |
373 | { |
374 | if (!$ignoreCase && function_exists('str_contains')) { |
375 | return str_contains($haystack, $needle); |
376 | } |
377 | |
378 | if (self::isNullOrEmpty($needle)) { |
379 | return true; |
380 | } |
381 | if (strlen($haystack) < strlen($needle)) { |
382 | return false; |
383 | } |
384 | |
385 | if ($ignoreCase) { |
386 | return stripos($haystack, $needle) !== false; |
387 | } |
388 | |
389 | return strpos($haystack, $needle) !== false; |
390 | } |
391 | |
392 | /** |
393 | * 文字列部分切り出し。 |
394 | * |
395 | * @param string $value 対象文字列。 |
396 | * @param int $offset 開始文字数目。負数の場合は後ろから。 |
397 | * @param int $length 抜き出す長さ。負数の場合は最後まで($offset) |
398 | * @return string 切り抜き後文字列。 |
399 | */ |
400 | public static function substring(string $value, int $offset, int $length = -1): string |
401 | { |
402 | return mb_substr($value, $offset, 0 <= $length ? $length : null); |
403 | } |
404 | |
405 | /** |
406 | * 大文字を小文字に変換。 |
407 | * |
408 | * @param string $value |
409 | * @return string |
410 | */ |
411 | public static function toLower(string $value): string |
412 | { |
413 | return mb_strtolower($value); |
414 | } |
415 | |
416 | /** |
417 | * 小文字を大文字に変換。 |
418 | * |
419 | * @param string $value |
420 | * @return string |
421 | */ |
422 | public static function toUpper(string $value): string |
423 | { |
424 | return mb_strtoupper($value); |
425 | } |
426 | |
427 | /** |
428 | * 文字列分割。 |
429 | * |
430 | * `explode` ラッパー。 |
431 | * |
432 | * @param string $value 対象文字列。 |
433 | * @param non-empty-string $separator 分割対象文字列。 |
434 | * @param int $limit 分割数。 |
435 | * @return string[] 分割された文字列。 |
436 | * @phpstan-return list<string> 分割された文字列。 |
437 | * @throws ArgumentException 分割失敗(PHP8未満) |
438 | * @throws \ValueError 分割失敗(PHP8以上) |
439 | * @see https://www.php.net/manual/function.explode.php |
440 | */ |
441 | public static function split(string $value, string $separator, int $limit = PHP_INT_MAX): array |
442 | { |
443 | if (Text::isNullOrEmpty($separator)) { //@phpstan-ignore-line [DOCTYPE] |
444 | throw new ArgumentException(); |
445 | } |
446 | |
447 | $result = explode($separator, $value, $limit); |
448 | |
449 | return $result; |
450 | } |
451 | |
452 | /** |
453 | * 文字列を改行で分割。 |
454 | * |
455 | * @param string $value |
456 | * @return string[] |
457 | * @phpstan-return list<string> |
458 | */ |
459 | public static function splitLines(string $value): array |
460 | { |
461 | $lfValue = self::replace($value, ["\r\n", "\r"], "\n"); |
462 | return self::split($lfValue, "\n"); |
463 | } |
464 | |
465 | /** |
466 | * 文字列結合。 |
467 | * |
468 | * `implode` ラッパー。 |
469 | * |
470 | * @param string $separator |
471 | * @param string[] $values |
472 | * @return string |
473 | * @see https://www.php.net/manual/function.implode.php |
474 | */ |
475 | public static function join(string $separator, array $values): string |
476 | { |
477 | return implode($separator, $values); |
478 | } |
479 | |
480 | /** |
481 | * トリム用パターンの生成。 |
482 | * |
483 | * @param Regex $regex |
484 | * @param string $characters |
485 | * @param int $trimTarget |
486 | * @phpstan-param self::TRIM_TARGET_* $trimTarget |
487 | * @return string |
488 | */ |
489 | private static function createTrimPattern(Regex $regex, string $characters, int $trimTarget): string |
490 | { |
491 | $escapedCharactersPattern = $regex->escape($characters); |
492 | $rangePattern = self::replace($escapedCharactersPattern, '\.\.', '-'); |
493 | |
494 | return match ($trimTarget) { |
495 | self::TRIM_TARGET_START => "/\A[$rangePattern]++|/", |
496 | self::TRIM_TARGET_END => "/[$rangePattern]++\z/", |
497 | self::TRIM_TARGET_BOTH => "/\A[$rangePattern]++|[$rangePattern]++\z/", |
498 | }; |
499 | } |
500 | |
501 | /** |
502 | * トリム処理。 |
503 | * |
504 | * `trim` ラッパー。 |
505 | * |
506 | * @param string $value 対象文字列。 |
507 | * @param string $characters トリム対象文字。 |
508 | * @return string トリム後文字列。 |
509 | * @see https://www.php.net/manual/function.trim.php |
510 | */ |
511 | public static function trim(string $value, string $characters = self::TRIM_CHARACTERS): string |
512 | { |
513 | // 1byte文字構成であればそのまま |
514 | if (strlen($characters) == self::getLength($characters)) { |
515 | return \trim($value, $characters); |
516 | } |
517 | |
518 | $regex = new Regex(); |
519 | $pattern = self::createTrimPattern($regex, $characters, self::TRIM_TARGET_BOTH); |
520 | $result = $regex->replace($value, $pattern, ''); |
521 | |
522 | return $result; |
523 | } |
524 | |
525 | /** |
526 | * 先頭トリム。 |
527 | * |
528 | * `ltrim` ラッパー。 |
529 | * |
530 | * @param string $value 対象文字列。 |
531 | * @param string $characters トリム対象文字。 |
532 | * @return string トリム後文字列。 |
533 | * @see https://www.php.net/manual/function.ltrim.php |
534 | */ |
535 | public static function trimStart(string $value, string $characters = self::TRIM_CHARACTERS): string |
536 | { |
537 | // 1byte文字構成であればそのまま |
538 | if (strlen($characters) == self::getLength($characters)) { |
539 | return ltrim($value, $characters); |
540 | } |
541 | |
542 | $regex = new Regex(); |
543 | $pattern = self::createTrimPattern($regex, $characters, self::TRIM_TARGET_START); |
544 | $result = $regex->replace($value, $pattern, ''); |
545 | |
546 | return $result; |
547 | } |
548 | |
549 | /** |
550 | * 終端トリム。 |
551 | * |
552 | * `rtrim` ラッパー。 |
553 | * |
554 | * @param string $value 対象文字列。 |
555 | * @param string $characters トリム対象文字。 |
556 | * @return string トリム後文字列。 |
557 | * @see https://www.php.net/manual/function.rtrim.php |
558 | */ |
559 | public static function trimEnd(string $value, string $characters = self::TRIM_CHARACTERS): string |
560 | { |
561 | // 1byte文字構成であればそのまま |
562 | if (strlen($characters) == self::getLength($characters)) { |
563 | return rtrim($value, $characters); |
564 | } |
565 | |
566 | $regex = new Regex(); |
567 | $pattern = self::createTrimPattern($regex, $characters, self::TRIM_TARGET_END); |
568 | $result = $regex->replace($value, $pattern, ''); |
569 | |
570 | return $result; |
571 | } |
572 | |
573 | |
574 | /** |
575 | * データ出力。 |
576 | * |
577 | * var_export/print_r で迷ったり $return = true 忘れのためのラッパー。 |
578 | * 色々あったけど `var_dump` に落ち着いた感。 |
579 | * |
580 | * @param mixed $value |
581 | * @return string |
582 | */ |
583 | public static function dump($value): string |
584 | { |
585 | //return print_r($value, true); |
586 | |
587 | $val = OutputBuffer::get(fn () => var_dump($value)); |
588 | if ($val->hasNull()) { |
589 | return $val->toBase64(); |
590 | } |
591 | |
592 | return $val->toString(); |
593 | } |
594 | |
595 | /** |
596 | * 文字列置き換え |
597 | * |
598 | * `str_replace` ラッパー。 |
599 | * |
600 | * @param string $source 入力文字列。 |
601 | * @param string|string[] $oldValue 元文字列(か、元文字列配列) |
602 | * @param string|string[] $newValue 置き換え文字列(か、元文字列配列)。 |
603 | * @return string 置き換え後文字列。 |
604 | * @see https://www.php.net/manual/function.str-replace.php |
605 | */ |
606 | public static function replace(string $source, string|array $oldValue, string|array $newValue): string |
607 | { |
608 | if (is_string($oldValue) && is_array($newValue)) { |
609 | throw new ArgumentException('$newValue'); |
610 | } |
611 | |
612 | if (is_string($oldValue) && $oldValue === $newValue) { |
613 | return $source; |
614 | } |
615 | |
616 | return str_replace($oldValue, $newValue, $source); |
617 | } |
618 | |
619 | /** |
620 | * 文字列を反復。 |
621 | * |
622 | * str_repeat ラッパー。 |
623 | * |
624 | * @param string $value |
625 | * @param int $count |
626 | * @phpstan-param non-negative-int $count |
627 | * @return string |
628 | * @throws ArgumentException 負数。 |
629 | * @see https://www.php.net/manual/function.str-repeat.php |
630 | */ |
631 | public static function repeat(string $value, int $count): string |
632 | { |
633 | //@phpstan-ignore-next-line [DOCTYPE] |
634 | if ($count < 0) { |
635 | throw new ArgumentException(); |
636 | } |
637 | |
638 | return str_repeat($value, $count); |
639 | } |
640 | |
641 | /** |
642 | * 文字列を文字の配列に変換。 |
643 | * |
644 | * @param non-empty-string $value |
645 | * @return string[] |
646 | */ |
647 | public static function toCharacters(string $value): array |
648 | { |
649 | if (Text::isNullOrEmpty($value)) { //@phpstan-ignore-line [DOCTYPE] |
650 | throw new ArgumentException('$value = ' . $value); |
651 | } |
652 | |
653 | $length = Text::getLength($value); |
654 | $charactersArray = []; |
655 | for ($i = 0; $i < $length; $i++) { |
656 | $c = self::substring($value, $i, 1); |
657 | $charactersArray[] = $c; |
658 | } |
659 | |
660 | return $charactersArray; |
661 | } |
662 | |
663 | /** |
664 | * 何かしらを文字列変換 |
665 | * |
666 | * @param mixed $raw |
667 | * @param string $newline 配列の場合の改行(未指定時に `PHP_EOL`) |
668 | * @return string |
669 | */ |
670 | public static function toString(mixed $raw, string $newline = PHP_EOL): string |
671 | { |
672 | if (is_string($raw)) { |
673 | return $raw; |
674 | } |
675 | |
676 | if (is_array($raw)) { |
677 | return self::join($newline, $raw); |
678 | } |
679 | |
680 | return strval($raw); |
681 | } |
682 | |
683 | #endregion |
684 | } |