PluginProbe
WP-Stateless – Google Cloud Storage / 3.2.1
WP-Stateless – Google Cloud Storage v3.2.1
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / vendor / symfony / string / AbstractString.php

AbstractString.php in WP-Stateless – Google Cloud Storage 3.2.1, at vendor/symfony/string/AbstractString.php

732 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\String;
13
14 use Symfony\Component\String\Exception\ExceptionInterface;
15 use Symfony\Component\String\Exception\InvalidArgumentException;
16 use Symfony\Component\String\Exception\RuntimeException;
17
18 /**
19 * Represents a string of abstract characters.
20 *
21 * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters).
22 * This class is the abstract type to use as a type-hint when the logic you want to
23 * implement doesn't care about the exact variant it deals with.
24 *
25 * @author Nicolas Grekas <p@tchwork.com>
26 * @author Hugo Hamon <hugohamon@neuf.fr>
27 *
28 * @throws ExceptionInterface
29 */
30 abstract class AbstractString implements \Stringable, \JsonSerializable
31 {
32 public const PREG_PATTERN_ORDER = PREG_PATTERN_ORDER;
33 public const PREG_SET_ORDER = PREG_SET_ORDER;
34 public const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE;
35 public const PREG_UNMATCHED_AS_NULL = PREG_UNMATCHED_AS_NULL;
36
37 public const PREG_SPLIT = 0;
38 public const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY;
39 public const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE;
40 public const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE;
41
42 protected $string = '';
43 protected $ignoreCase = false;
44
45 abstract public function __construct(string $string = '');
46
47 /**
48 * Unwraps instances of AbstractString back to strings.
49 *
50 * @return string[]|array
51 */
52 public static function unwrap(array $values): array
53 {
54 foreach ($values as $k => $v) {
55 if ($v instanceof self) {
56 $values[$k] = $v->__toString();
57 } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) {
58 $values[$k] = $v;
59 }
60 }
61
62 return $values;
63 }
64
65 /**
66 * Wraps (and normalizes) strings in instances of AbstractString.
67 *
68 * @return static[]|array
69 */
70 public static function wrap(array $values): array
71 {
72 $i = 0;
73 $keys = null;
74
75 foreach ($values as $k => $v) {
76 if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) {
77 $keys = $keys ?? array_keys($values);
78 $keys[$i] = $j;
79 }
80
81 if (\is_string($v)) {
82 $values[$k] = new static($v);
83 } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) {
84 $values[$k] = $v;
85 }
86
87 ++$i;
88 }
89
90 return null !== $keys ? array_combine($keys, $values) : $values;
91 }
92
93 /**
94 * @param string|string[] $needle
95 *
96 * @return static
97 */
98 public function after($needle, bool $includeNeedle = false, int $offset = 0): self
99 {
100 $str = clone $this;
101 $str->string = '';
102 $i = PHP_INT_MAX;
103
104 foreach ((array) $needle as $n) {
105 $n = (string) $n;
106 $j = $this->indexOf($n, $offset);
107
108 if (null !== $j && $j < $i) {
109 $i = $j;
110 $str->string = $n;
111 }
112 }
113
114 if (PHP_INT_MAX === $i) {
115 return $str;
116 }
117
118 if (!$includeNeedle) {
119 $i += $str->length();
120 }
121
122 return $this->slice($i);
123 }
124
125 /**
126 * @param string|string[] $needle
127 *
128 * @return static
129 */
130 public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self
131 {
132 $str = clone $this;
133 $str->string = '';
134 $i = null;
135
136 foreach ((array) $needle as $n) {
137 $n = (string) $n;
138 $j = $this->indexOfLast($n, $offset);
139
140 if (null !== $j && $j >= $i) {
141 $i = $offset = $j;
142 $str->string = $n;
143 }
144 }
145
146 if (null === $i) {
147 return $str;
148 }
149
150 if (!$includeNeedle) {
151 $i += $str->length();
152 }
153
154 return $this->slice($i);
155 }
156
157 /**
158 * @return static
159 */
160 abstract public function append(string ...$suffix): self;
161
162 /**
163 * @param string|string[] $needle
164 *
165 * @return static
166 */
167 public function before($needle, bool $includeNeedle = false, int $offset = 0): self
168 {
169 $str = clone $this;
170 $str->string = '';
171 $i = PHP_INT_MAX;
172
173 foreach ((array) $needle as $n) {
174 $n = (string) $n;
175 $j = $this->indexOf($n, $offset);
176
177 if (null !== $j && $j < $i) {
178 $i = $j;
179 $str->string = $n;
180 }
181 }
182
183 if (PHP_INT_MAX === $i) {
184 return $str;
185 }
186
187 if ($includeNeedle) {
188 $i += $str->length();
189 }
190
191 return $this->slice(0, $i);
192 }
193
194 /**
195 * @param string|string[] $needle
196 *
197 * @return static
198 */
199 public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self
200 {
201 $str = clone $this;
202 $str->string = '';
203 $i = null;
204
205 foreach ((array) $needle as $n) {
206 $n = (string) $n;
207 $j = $this->indexOfLast($n, $offset);
208
209 if (null !== $j && $j >= $i) {
210 $i = $offset = $j;
211 $str->string = $n;
212 }
213 }
214
215 if (null === $i) {
216 return $str;
217 }
218
219 if ($includeNeedle) {
220 $i += $str->length();
221 }
222
223 return $this->slice(0, $i);
224 }
225
226 /**
227 * @return int[]
228 */
229 public function bytesAt(int $offset): array
230 {
231 $str = $this->slice($offset, 1);
232
233 return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
234 }
235
236 /**
237 * @return static
238 */
239 abstract public function camel(): self;
240
241 /**
242 * @return static[]
243 */
244 abstract public function chunk(int $length = 1): array;
245
246 /**
247 * @return static
248 */
249 public function collapseWhitespace(): self
250 {
251 $str = clone $this;
252 $str->string = trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $str->string));
253
254 return $str;
255 }
256
257 /**
258 * @param string|string[] $needle
259 */
260 public function containsAny($needle): bool
261 {
262 return null !== $this->indexOf($needle);
263 }
264
265 /**
266 * @param string|string[] $suffix
267 */
268 public function endsWith($suffix): bool
269 {
270 if (!\is_array($suffix) && !$suffix instanceof \Traversable) {
271 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
272 }
273
274 foreach ($suffix as $s) {
275 if ($this->endsWith((string) $s)) {
276 return true;
277 }
278 }
279
280 return false;
281 }
282
283 /**
284 * @return static
285 */
286 public function ensureEnd(string $suffix): self
287 {
288 if (!$this->endsWith($suffix)) {
289 return $this->append($suffix);
290 }
291
292 $suffix = preg_quote($suffix);
293 $regex = '{('.$suffix.')(?:'.$suffix.')++$}D';
294
295 return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1');
296 }
297
298 /**
299 * @return static
300 */
301 public function ensureStart(string $prefix): self
302 {
303 $prefix = new static($prefix);
304
305 if (!$this->startsWith($prefix)) {
306 return $this->prepend($prefix);
307 }
308
309 $str = clone $this;
310 $i = $prefixLen = $prefix->length();
311
312 while ($this->indexOf($prefix, $i) === $i) {
313 $str = $str->slice($prefixLen);
314 $i += $prefixLen;
315 }
316
317 return $str;
318 }
319
320 /**
321 * @param string|string[] $string
322 */
323 public function equalsTo($string): bool
324 {
325 if (!\is_array($string) && !$string instanceof \Traversable) {
326 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
327 }
328
329 foreach ($string as $s) {
330 if ($this->equalsTo((string) $s)) {
331 return true;
332 }
333 }
334
335 return false;
336 }
337
338 /**
339 * @return static
340 */
341 abstract public function folded(): self;
342
343 /**
344 * @return static
345 */
346 public function ignoreCase(): self
347 {
348 $str = clone $this;
349 $str->ignoreCase = true;
350
351 return $str;
352 }
353
354 /**
355 * @param string|string[] $needle
356 */
357 public function indexOf($needle, int $offset = 0): ?int
358 {
359 if (!\is_array($needle) && !$needle instanceof \Traversable) {
360 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
361 }
362
363 $i = PHP_INT_MAX;
364
365 foreach ($needle as $n) {
366 $j = $this->indexOf((string) $n, $offset);
367
368 if (null !== $j && $j < $i) {
369 $i = $j;
370 }
371 }
372
373 return PHP_INT_MAX === $i ? null : $i;
374 }
375
376 /**
377 * @param string|string[] $needle
378 */
379 public function indexOfLast($needle, int $offset = 0): ?int
380 {
381 if (!\is_array($needle) && !$needle instanceof \Traversable) {
382 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
383 }
384
385 $i = null;
386
387 foreach ($needle as $n) {
388 $j = $this->indexOfLast((string) $n, $offset);
389
390 if (null !== $j && $j >= $i) {
391 $i = $offset = $j;
392 }
393 }
394
395 return $i;
396 }
397
398 public function isEmpty(): bool
399 {
400 return '' === $this->string;
401 }
402
403 /**
404 * @return static
405 */
406 abstract public function join(array $strings, string $lastGlue = null): self;
407
408 public function jsonSerialize(): string
409 {
410 return $this->string;
411 }
412
413 abstract public function length(): int;
414
415 /**
416 * @return static
417 */
418 abstract public function lower(): self;
419
420 /**
421 * Matches the string using a regular expression.
422 *
423 * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression.
424 *
425 * @return array All matches in a multi-dimensional array ordered according to flags
426 */
427 abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array;
428
429 /**
430 * @return static
431 */
432 abstract public function padBoth(int $length, string $padStr = ' '): self;
433
434 /**
435 * @return static
436 */
437 abstract public function padEnd(int $length, string $padStr = ' '): self;
438
439 /**
440 * @return static
441 */
442 abstract public function padStart(int $length, string $padStr = ' '): self;
443
444 /**
445 * @return static
446 */
447 abstract public function prepend(string ...$prefix): self;
448
449 /**
450 * @return static
451 */
452 public function repeat(int $multiplier): self
453 {
454 if (0 > $multiplier) {
455 throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
456 }
457
458 $str = clone $this;
459 $str->string = str_repeat($str->string, $multiplier);
460
461 return $str;
462 }
463
464 /**
465 * @return static
466 */
467 abstract public function replace(string $from, string $to): self;
468
469 /**
470 * @param string|callable $to
471 *
472 * @return static
473 */
474 abstract public function replaceMatches(string $fromRegexp, $to): self;
475
476 /**
477 * @return static
478 */
479 abstract public function reverse(): self;
480
481 /**
482 * @return static
483 */
484 abstract public function slice(int $start = 0, int $length = null): self;
485
486 /**
487 * @return static
488 */
489 abstract public function snake(): self;
490
491 /**
492 * @return static
493 */
494 abstract public function splice(string $replacement, int $start = 0, int $length = null): self;
495
496 /**
497 * @return static[]
498 */
499 public function split(string $delimiter, int $limit = null, int $flags = null): array
500 {
501 if (null === $flags) {
502 throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
503 }
504
505 if ($this->ignoreCase) {
506 $delimiter .= 'i';
507 }
508
509 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
510
511 try {
512 if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) {
513 $lastError = preg_last_error();
514
515 foreach (get_defined_constants(true)['pcre'] as $k => $v) {
516 if ($lastError === $v && '_ERROR' === substr($k, -6)) {
517 throw new RuntimeException('Splitting failed with '.$k.'.');
518 }
519 }
520
521 throw new RuntimeException('Splitting failed with unknown error code.');
522 }
523 } finally {
524 restore_error_handler();
525 }
526
527 $str = clone $this;
528
529 if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
530 foreach ($chunks as &$chunk) {
531 $str->string = $chunk[0];
532 $chunk[0] = clone $str;
533 }
534 } else {
535 foreach ($chunks as &$chunk) {
536 $str->string = $chunk;
537 $chunk = clone $str;
538 }
539 }
540
541 return $chunks;
542 }
543
544 /**
545 * @param string|string[] $prefix
546 */
547 public function startsWith($prefix): bool
548 {
549 if (!\is_array($prefix) && !$prefix instanceof \Traversable) {
550 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
551 }
552
553 foreach ($prefix as $prefix) {
554 if ($this->startsWith((string) $prefix)) {
555 return true;
556 }
557 }
558
559 return false;
560 }
561
562 /**
563 * @return static
564 */
565 abstract public function title(bool $allWords = false): self;
566
567 public function toByteString(string $toEncoding = null): ByteString
568 {
569 $b = new ByteString();
570
571 $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding;
572
573 if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') {
574 $b->string = $this->string;
575
576 return $b;
577 }
578
579 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
580
581 try {
582 try {
583 $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
584 } catch (InvalidArgumentException $e) {
585 if (!\function_exists('iconv')) {
586 throw $e;
587 }
588
589 $b->string = iconv('UTF-8', $toEncoding, $this->string);
590 }
591 } finally {
592 restore_error_handler();
593 }
594
595 return $b;
596 }
597
598 public function toCodePointString(): CodePointString
599 {
600 return new CodePointString($this->string);
601 }
602
603 public function toString(): string
604 {
605 return $this->string;
606 }
607
608 public function toUnicodeString(): UnicodeString
609 {
610 return new UnicodeString($this->string);
611 }
612
613 /**
614 * @return static
615 */
616 abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
617
618 /**
619 * @return static
620 */
621 abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
622
623 /**
624 * @return static
625 */
626 abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
627
628 /**
629 * @return static
630 */
631 public function truncate(int $length, string $ellipsis = '', bool $cut = true): self
632 {
633 $stringLength = $this->length();
634
635 if ($stringLength <= $length) {
636 return clone $this;
637 }
638
639 $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;
640
641 if ($length < $ellipsisLength) {
642 $ellipsisLength = 0;
643 }
644
645 if (!$cut) {
646 if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
647 return clone $this;
648 }
649
650 $length += $ellipsisLength;
651 }
652
653 $str = $this->slice(0, $length - $ellipsisLength);
654
655 return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
656 }
657
658 /**
659 * @return static
660 */
661 abstract public function upper(): self;
662
663 /**
664 * Returns the printable length on a terminal.
665 */
666 abstract public function width(bool $ignoreAnsiDecoration = true): int;
667
668 /**
669 * @return static
670 */
671 public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self
672 {
673 $lines = '' !== $break ? $this->split($break) : [clone $this];
674 $chars = [];
675 $mask = '';
676
677 if (1 === \count($lines) && '' === $lines[0]->string) {
678 return $lines[0];
679 }
680
681 foreach ($lines as $i => $line) {
682 if ($i) {
683 $chars[] = $break;
684 $mask .= '#';
685 }
686
687 foreach ($line->chunk() as $char) {
688 $chars[] = $char->string;
689 $mask .= ' ' === $char->string ? ' ' : '?';
690 }
691 }
692
693 $string = '';
694 $j = 0;
695 $b = $i = -1;
696 $mask = wordwrap($mask, $width, '#', $cut);
697
698 while (false !== $b = strpos($mask, '#', $b + 1)) {
699 for (++$i; $i < $b; ++$i) {
700 $string .= $chars[$j];
701 unset($chars[$j++]);
702 }
703
704 if ($break === $chars[$j] || ' ' === $chars[$j]) {
705 unset($chars[$j++]);
706 }
707
708 $string .= $break;
709 }
710
711 $str = clone $this;
712 $str->string = $string.implode('', $chars);
713
714 return $str;
715 }
716
717 public function __sleep(): array
718 {
719 return ['string'];
720 }
721
722 public function __clone()
723 {
724 $this->ignoreCase = false;
725 }
726
727 public function __toString(): string
728 {
729 return $this->string;
730 }
731 }
732