PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / 2.8.4
Kubio AI Page Builder v2.8.4
2.8.4 2.8.3 2.8.2 2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / vendor / symfony / string / AbstractString.php
kubio / vendor / symfony / string Last commit date
Exception 1 year ago Inflector 1 year ago Resources 1 year ago Slugger 1 year ago AbstractString.php 1 year ago AbstractUnicodeString.php 1 year ago ByteString.php 1 year ago CHANGELOG.md 1 year ago CodePointString.php 1 year ago LICENSE 1 year ago LazyString.php 1 year ago README.md 1 year ago UnicodeString.php 1 year ago composer.json 1 year ago
AbstractString.php
799 lines
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 $i = \PHP_INT_MAX;
102
103 foreach ((array) $needle as $n) {
104 $n = (string) $n;
105 $j = $this->indexOf($n, $offset);
106
107 if (null !== $j && $j < $i) {
108 $i = $j;
109 $str->string = $n;
110 }
111 }
112
113 if (\PHP_INT_MAX === $i) {
114 return $str;
115 }
116
117 if (!$includeNeedle) {
118 $i += $str->length();
119 }
120
121 return $this->slice($i);
122 }
123
124 /**
125 * @param string|string[] $needle
126 *
127 * @return static
128 */
129 public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self
130 {
131 $str = clone $this;
132 $i = null;
133
134 foreach ((array) $needle as $n) {
135 $n = (string) $n;
136 $j = $this->indexOfLast($n, $offset);
137
138 if (null !== $j && $j >= $i) {
139 $i = $offset = $j;
140 $str->string = $n;
141 }
142 }
143
144 if (null === $i) {
145 return $str;
146 }
147
148 if (!$includeNeedle) {
149 $i += $str->length();
150 }
151
152 return $this->slice($i);
153 }
154
155 /**
156 * @return static
157 */
158 abstract public function append(string ...$suffix): self;
159
160 /**
161 * @param string|string[] $needle
162 *
163 * @return static
164 */
165 public function before($needle, bool $includeNeedle = false, int $offset = 0): self
166 {
167 $str = clone $this;
168 $i = \PHP_INT_MAX;
169
170 foreach ((array) $needle as $n) {
171 $n = (string) $n;
172 $j = $this->indexOf($n, $offset);
173
174 if (null !== $j && $j < $i) {
175 $i = $j;
176 $str->string = $n;
177 }
178 }
179
180 if (\PHP_INT_MAX === $i) {
181 return $str;
182 }
183
184 if ($includeNeedle) {
185 $i += $str->length();
186 }
187
188 return $this->slice(0, $i);
189 }
190
191 /**
192 * @param string|string[] $needle
193 *
194 * @return static
195 */
196 public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self
197 {
198 $str = clone $this;
199 $i = null;
200
201 foreach ((array) $needle as $n) {
202 $n = (string) $n;
203 $j = $this->indexOfLast($n, $offset);
204
205 if (null !== $j && $j >= $i) {
206 $i = $offset = $j;
207 $str->string = $n;
208 }
209 }
210
211 if (null === $i) {
212 return $str;
213 }
214
215 if ($includeNeedle) {
216 $i += $str->length();
217 }
218
219 return $this->slice(0, $i);
220 }
221
222 /**
223 * @return int[]
224 */
225 public function bytesAt(int $offset): array
226 {
227 $str = $this->slice($offset, 1);
228
229 return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
230 }
231
232 /**
233 * @return static
234 */
235 abstract public function camel(): self;
236
237 /**
238 * @return static[]
239 */
240 abstract public function chunk(int $length = 1): array;
241
242 /**
243 * @return static
244 */
245 public function collapseWhitespace(): self
246 {
247 $str = clone $this;
248 $str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C");
249
250 return $str;
251 }
252
253 /**
254 * @param string|string[] $needle
255 */
256 public function containsAny($needle): bool
257 {
258 return null !== $this->indexOf($needle);
259 }
260
261 /**
262 * @param string|string[] $suffix
263 */
264 public function endsWith($suffix): bool
265 {
266 if (!\is_array($suffix) && !$suffix instanceof \Traversable) {
267 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
268 }
269
270 foreach ($suffix as $s) {
271 if ($this->endsWith((string) $s)) {
272 return true;
273 }
274 }
275
276 return false;
277 }
278
279 /**
280 * @return static
281 */
282 public function ensureEnd(string $suffix): self
283 {
284 if (!$this->endsWith($suffix)) {
285 return $this->append($suffix);
286 }
287
288 $suffix = preg_quote($suffix);
289 $regex = '{('.$suffix.')(?:'.$suffix.')++$}D';
290
291 return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1');
292 }
293
294 /**
295 * @return static
296 */
297 public function ensureStart(string $prefix): self
298 {
299 $prefix = new static($prefix);
300
301 if (!$this->startsWith($prefix)) {
302 return $this->prepend($prefix);
303 }
304
305 $str = clone $this;
306 $i = $prefixLen = $prefix->length();
307
308 while ($this->indexOf($prefix, $i) === $i) {
309 $str = $str->slice($prefixLen);
310 $i += $prefixLen;
311 }
312
313 return $str;
314 }
315
316 /**
317 * @param string|string[] $string
318 */
319 public function equalsTo($string): bool
320 {
321 if (!\is_array($string) && !$string instanceof \Traversable) {
322 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
323 }
324
325 foreach ($string as $s) {
326 if ($this->equalsTo((string) $s)) {
327 return true;
328 }
329 }
330
331 return false;
332 }
333
334 /**
335 * @return static
336 */
337 abstract public function folded(): self;
338
339 /**
340 * @return static
341 */
342 public function ignoreCase(): self
343 {
344 $str = clone $this;
345 $str->ignoreCase = true;
346
347 return $str;
348 }
349
350 /**
351 * @param string|string[] $needle
352 */
353 public function indexOf($needle, int $offset = 0): ?int
354 {
355 if (!\is_array($needle) && !$needle instanceof \Traversable) {
356 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
357 }
358
359 $i = \PHP_INT_MAX;
360
361 foreach ($needle as $n) {
362 $j = $this->indexOf((string) $n, $offset);
363
364 if (null !== $j && $j < $i) {
365 $i = $j;
366 }
367 }
368
369 return \PHP_INT_MAX === $i ? null : $i;
370 }
371
372 /**
373 * @param string|string[] $needle
374 */
375 public function indexOfLast($needle, int $offset = 0): ?int
376 {
377 if (!\is_array($needle) && !$needle instanceof \Traversable) {
378 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
379 }
380
381 $i = null;
382
383 foreach ($needle as $n) {
384 $j = $this->indexOfLast((string) $n, $offset);
385
386 if (null !== $j && $j >= $i) {
387 $i = $offset = $j;
388 }
389 }
390
391 return $i;
392 }
393
394 public function isEmpty(): bool
395 {
396 return '' === $this->string;
397 }
398
399 /**
400 * @return static
401 */
402 abstract public function join(array $strings, ?string $lastGlue = null): self;
403
404 public function jsonSerialize(): string
405 {
406 return $this->string;
407 }
408
409 abstract public function length(): int;
410
411 /**
412 * @return static
413 */
414 abstract public function lower(): self;
415
416 /**
417 * Matches the string using a regular expression.
418 *
419 * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression.
420 *
421 * @return array All matches in a multi-dimensional array ordered according to flags
422 */
423 abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array;
424
425 /**
426 * @return static
427 */
428 abstract public function padBoth(int $length, string $padStr = ' '): self;
429
430 /**
431 * @return static
432 */
433 abstract public function padEnd(int $length, string $padStr = ' '): self;
434
435 /**
436 * @return static
437 */
438 abstract public function padStart(int $length, string $padStr = ' '): self;
439
440 /**
441 * @return static
442 */
443 abstract public function prepend(string ...$prefix): self;
444
445 /**
446 * @return static
447 */
448 public function repeat(int $multiplier): self
449 {
450 if (0 > $multiplier) {
451 throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
452 }
453
454 $str = clone $this;
455 $str->string = str_repeat($str->string, $multiplier);
456
457 return $str;
458 }
459
460 /**
461 * @return static
462 */
463 abstract public function replace(string $from, string $to): self;
464
465 /**
466 * @param string|callable $to
467 *
468 * @return static
469 */
470 abstract public function replaceMatches(string $fromRegexp, $to): self;
471
472 /**
473 * @return static
474 */
475 abstract public function reverse(): self;
476
477 /**
478 * @return static
479 */
480 abstract public function slice(int $start = 0, ?int $length = null): self;
481
482 /**
483 * @return static
484 */
485 abstract public function snake(): self;
486
487 /**
488 * @return static
489 */
490 abstract public function splice(string $replacement, int $start = 0, ?int $length = null): self;
491
492 /**
493 * @return static[]
494 */
495 public function split(string $delimiter, ?int $limit = null, ?int $flags = null): array
496 {
497 if (null === $flags) {
498 throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
499 }
500
501 if ($this->ignoreCase) {
502 $delimiter .= 'i';
503 }
504
505 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
506
507 try {
508 if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) {
509 $lastError = preg_last_error();
510
511 foreach (get_defined_constants(true)['pcre'] as $k => $v) {
512 if ($lastError === $v && '_ERROR' === substr($k, -6)) {
513 throw new RuntimeException('Splitting failed with '.$k.'.');
514 }
515 }
516
517 throw new RuntimeException('Splitting failed with unknown error code.');
518 }
519 } finally {
520 restore_error_handler();
521 }
522
523 $str = clone $this;
524
525 if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
526 foreach ($chunks as &$chunk) {
527 $str->string = $chunk[0];
528 $chunk[0] = clone $str;
529 }
530 } else {
531 foreach ($chunks as &$chunk) {
532 $str->string = $chunk;
533 $chunk = clone $str;
534 }
535 }
536
537 return $chunks;
538 }
539
540 /**
541 * @param string|string[] $prefix
542 */
543 public function startsWith($prefix): bool
544 {
545 if (!\is_array($prefix) && !$prefix instanceof \Traversable) {
546 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
547 }
548
549 foreach ($prefix as $prefix) {
550 if ($this->startsWith((string) $prefix)) {
551 return true;
552 }
553 }
554
555 return false;
556 }
557
558 /**
559 * @return static
560 */
561 abstract public function title(bool $allWords = false): self;
562
563 public function toByteString(?string $toEncoding = null): ByteString
564 {
565 $b = new ByteString();
566
567 $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding;
568
569 if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') {
570 $b->string = $this->string;
571
572 return $b;
573 }
574
575 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
576
577 try {
578 try {
579 $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
580 } catch (InvalidArgumentException|\ValueError $e) {
581 if (!\function_exists('iconv')) {
582 if ($e instanceof \ValueError) {
583 throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
584 }
585 throw $e;
586 }
587
588 $b->string = iconv('UTF-8', $toEncoding, $this->string);
589 }
590 } finally {
591 restore_error_handler();
592 }
593
594 return $b;
595 }
596
597 public function toCodePointString(): CodePointString
598 {
599 return new CodePointString($this->string);
600 }
601
602 public function toString(): string
603 {
604 return $this->string;
605 }
606
607 public function toUnicodeString(): UnicodeString
608 {
609 return new UnicodeString($this->string);
610 }
611
612 /**
613 * @return static
614 */
615 abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
616
617 /**
618 * @return static
619 */
620 abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
621
622 /**
623 * @param string|string[] $prefix
624 *
625 * @return static
626 */
627 public function trimPrefix($prefix): self
628 {
629 if (\is_array($prefix) || $prefix instanceof \Traversable) {
630 foreach ($prefix as $s) {
631 $t = $this->trimPrefix($s);
632
633 if ($t->string !== $this->string) {
634 return $t;
635 }
636 }
637
638 return clone $this;
639 }
640
641 $str = clone $this;
642
643 if ($prefix instanceof self) {
644 $prefix = $prefix->string;
645 } else {
646 $prefix = (string) $prefix;
647 }
648
649 if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) {
650 $str->string = substr($this->string, \strlen($prefix));
651 }
652
653 return $str;
654 }
655
656 /**
657 * @return static
658 */
659 abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
660
661 /**
662 * @param string|string[] $suffix
663 *
664 * @return static
665 */
666 public function trimSuffix($suffix): self
667 {
668 if (\is_array($suffix) || $suffix instanceof \Traversable) {
669 foreach ($suffix as $s) {
670 $t = $this->trimSuffix($s);
671
672 if ($t->string !== $this->string) {
673 return $t;
674 }
675 }
676
677 return clone $this;
678 }
679
680 $str = clone $this;
681
682 if ($suffix instanceof self) {
683 $suffix = $suffix->string;
684 } else {
685 $suffix = (string) $suffix;
686 }
687
688 if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) {
689 $str->string = substr($this->string, 0, -\strlen($suffix));
690 }
691
692 return $str;
693 }
694
695 /**
696 * @return static
697 */
698 public function truncate(int $length, string $ellipsis = '', bool $cut = true): self
699 {
700 $stringLength = $this->length();
701
702 if ($stringLength <= $length) {
703 return clone $this;
704 }
705
706 $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;
707
708 if ($length < $ellipsisLength) {
709 $ellipsisLength = 0;
710 }
711
712 if (!$cut) {
713 if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
714 return clone $this;
715 }
716
717 $length += $ellipsisLength;
718 }
719
720 $str = $this->slice(0, $length - $ellipsisLength);
721
722 return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
723 }
724
725 /**
726 * @return static
727 */
728 abstract public function upper(): self;
729
730 /**
731 * Returns the printable length on a terminal.
732 */
733 abstract public function width(bool $ignoreAnsiDecoration = true): int;
734
735 /**
736 * @return static
737 */
738 public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self
739 {
740 $lines = '' !== $break ? $this->split($break) : [clone $this];
741 $chars = [];
742 $mask = '';
743
744 if (1 === \count($lines) && '' === $lines[0]->string) {
745 return $lines[0];
746 }
747
748 foreach ($lines as $i => $line) {
749 if ($i) {
750 $chars[] = $break;
751 $mask .= '#';
752 }
753
754 foreach ($line->chunk() as $char) {
755 $chars[] = $char->string;
756 $mask .= ' ' === $char->string ? ' ' : '?';
757 }
758 }
759
760 $string = '';
761 $j = 0;
762 $b = $i = -1;
763 $mask = wordwrap($mask, $width, '#', $cut);
764
765 while (false !== $b = strpos($mask, '#', $b + 1)) {
766 for (++$i; $i < $b; ++$i) {
767 $string .= $chars[$j];
768 unset($chars[$j++]);
769 }
770
771 if ($break === $chars[$j] || ' ' === $chars[$j]) {
772 unset($chars[$j++]);
773 }
774
775 $string .= $break;
776 }
777
778 $str = clone $this;
779 $str->string = $string.implode('', $chars);
780
781 return $str;
782 }
783
784 public function __sleep(): array
785 {
786 return ['string'];
787 }
788
789 public function __clone()
790 {
791 $this->ignoreCase = false;
792 }
793
794 public function __toString(): string
795 {
796 return $this->string;
797 }
798 }
799