PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.8.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.8.2
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / prefixed / symfony / string / AbstractString.php
matomo / app / vendor / prefixed / symfony / string Last commit date
Exception 2 years 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 CodePointString.php 1 year ago LICENSE 2 years ago LazyString.php 2 years ago README.md 2 years ago UnicodeString.php 1 year ago
AbstractString.php
649 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 namespace Matomo\Dependencies\Symfony\Component\String;
12
13 use Matomo\Dependencies\Symfony\Component\String\Exception\ExceptionInterface;
14 use Matomo\Dependencies\Symfony\Component\String\Exception\InvalidArgumentException;
15 use Matomo\Dependencies\Symfony\Component\String\Exception\RuntimeException;
16 /**
17 * Represents a string of abstract characters.
18 *
19 * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters).
20 * This class is the abstract type to use as a type-hint when the logic you want to
21 * implement doesn't care about the exact variant it deals with.
22 *
23 * @author Nicolas Grekas <p@tchwork.com>
24 * @author Hugo Hamon <hugohamon@neuf.fr>
25 *
26 * @throws ExceptionInterface
27 */
28 abstract class AbstractString implements \Stringable, \JsonSerializable
29 {
30 public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER;
31 public const PREG_SET_ORDER = \PREG_SET_ORDER;
32 public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE;
33 public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL;
34 public const PREG_SPLIT = 0;
35 public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY;
36 public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE;
37 public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE;
38 protected $string = '';
39 protected $ignoreCase = \false;
40 public abstract function __construct(string $string = '');
41 /**
42 * Unwraps instances of AbstractString back to strings.
43 *
44 * @return string[]|array
45 */
46 public static function unwrap(array $values) : array
47 {
48 foreach ($values as $k => $v) {
49 if ($v instanceof self) {
50 $values[$k] = $v->__toString();
51 } elseif (\is_array($v) && $values[$k] !== ($v = static::unwrap($v))) {
52 $values[$k] = $v;
53 }
54 }
55 return $values;
56 }
57 /**
58 * Wraps (and normalizes) strings in instances of AbstractString.
59 *
60 * @return static[]|array
61 */
62 public static function wrap(array $values) : array
63 {
64 $i = 0;
65 $keys = null;
66 foreach ($values as $k => $v) {
67 if (\is_string($k) && '' !== $k && $k !== ($j = (string) new static($k))) {
68 $keys = $keys ?? array_keys($values);
69 $keys[$i] = $j;
70 }
71 if (\is_string($v)) {
72 $values[$k] = new static($v);
73 } elseif (\is_array($v) && $values[$k] !== ($v = static::wrap($v))) {
74 $values[$k] = $v;
75 }
76 ++$i;
77 }
78 return null !== $keys ? array_combine($keys, $values) : $values;
79 }
80 /**
81 * @param string|string[] $needle
82 *
83 * @return static
84 */
85 public function after($needle, bool $includeNeedle = \false, int $offset = 0) : self
86 {
87 $str = clone $this;
88 $i = \PHP_INT_MAX;
89 foreach ((array) $needle as $n) {
90 $n = (string) $n;
91 $j = $this->indexOf($n, $offset);
92 if (null !== $j && $j < $i) {
93 $i = $j;
94 $str->string = $n;
95 }
96 }
97 if (\PHP_INT_MAX === $i) {
98 return $str;
99 }
100 if (!$includeNeedle) {
101 $i += $str->length();
102 }
103 return $this->slice($i);
104 }
105 /**
106 * @param string|string[] $needle
107 *
108 * @return static
109 */
110 public function afterLast($needle, bool $includeNeedle = \false, int $offset = 0) : self
111 {
112 $str = clone $this;
113 $i = null;
114 foreach ((array) $needle as $n) {
115 $n = (string) $n;
116 $j = $this->indexOfLast($n, $offset);
117 if (null !== $j && $j >= $i) {
118 $i = $offset = $j;
119 $str->string = $n;
120 }
121 }
122 if (null === $i) {
123 return $str;
124 }
125 if (!$includeNeedle) {
126 $i += $str->length();
127 }
128 return $this->slice($i);
129 }
130 /**
131 * @return static
132 */
133 public abstract function append(string ...$suffix) : self;
134 /**
135 * @param string|string[] $needle
136 *
137 * @return static
138 */
139 public function before($needle, bool $includeNeedle = \false, int $offset = 0) : self
140 {
141 $str = clone $this;
142 $i = \PHP_INT_MAX;
143 foreach ((array) $needle as $n) {
144 $n = (string) $n;
145 $j = $this->indexOf($n, $offset);
146 if (null !== $j && $j < $i) {
147 $i = $j;
148 $str->string = $n;
149 }
150 }
151 if (\PHP_INT_MAX === $i) {
152 return $str;
153 }
154 if ($includeNeedle) {
155 $i += $str->length();
156 }
157 return $this->slice(0, $i);
158 }
159 /**
160 * @param string|string[] $needle
161 *
162 * @return static
163 */
164 public function beforeLast($needle, bool $includeNeedle = \false, int $offset = 0) : self
165 {
166 $str = clone $this;
167 $i = null;
168 foreach ((array) $needle as $n) {
169 $n = (string) $n;
170 $j = $this->indexOfLast($n, $offset);
171 if (null !== $j && $j >= $i) {
172 $i = $offset = $j;
173 $str->string = $n;
174 }
175 }
176 if (null === $i) {
177 return $str;
178 }
179 if ($includeNeedle) {
180 $i += $str->length();
181 }
182 return $this->slice(0, $i);
183 }
184 /**
185 * @return int[]
186 */
187 public function bytesAt(int $offset) : array
188 {
189 $str = $this->slice($offset, 1);
190 return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
191 }
192 /**
193 * @return static
194 */
195 public abstract function camel() : self;
196 /**
197 * @return static[]
198 */
199 public abstract function chunk(int $length = 1) : array;
200 /**
201 * @return static
202 */
203 public function collapseWhitespace() : self
204 {
205 $str = clone $this;
206 $str->string = trim(preg_replace("/(?:[ \n\r\t\f]{2,}+|[\n\r\t\f])/", ' ', $str->string), " \n\r\t\f");
207 return $str;
208 }
209 /**
210 * @param string|string[] $needle
211 */
212 public function containsAny($needle) : bool
213 {
214 return null !== $this->indexOf($needle);
215 }
216 /**
217 * @param string|string[] $suffix
218 */
219 public function endsWith($suffix) : bool
220 {
221 if (!\is_array($suffix) && !$suffix instanceof \Traversable) {
222 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
223 }
224 foreach ($suffix as $s) {
225 if ($this->endsWith((string) $s)) {
226 return \true;
227 }
228 }
229 return \false;
230 }
231 /**
232 * @return static
233 */
234 public function ensureEnd(string $suffix) : self
235 {
236 if (!$this->endsWith($suffix)) {
237 return $this->append($suffix);
238 }
239 $suffix = preg_quote($suffix);
240 $regex = '{(' . $suffix . ')(?:' . $suffix . ')++$}D';
241 return $this->replaceMatches($regex . ($this->ignoreCase ? 'i' : ''), '$1');
242 }
243 /**
244 * @return static
245 */
246 public function ensureStart(string $prefix) : self
247 {
248 $prefix = new static($prefix);
249 if (!$this->startsWith($prefix)) {
250 return $this->prepend($prefix);
251 }
252 $str = clone $this;
253 $i = $prefixLen = $prefix->length();
254 while ($this->indexOf($prefix, $i) === $i) {
255 $str = $str->slice($prefixLen);
256 $i += $prefixLen;
257 }
258 return $str;
259 }
260 /**
261 * @param string|string[] $string
262 */
263 public function equalsTo($string) : bool
264 {
265 if (!\is_array($string) && !$string instanceof \Traversable) {
266 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
267 }
268 foreach ($string as $s) {
269 if ($this->equalsTo((string) $s)) {
270 return \true;
271 }
272 }
273 return \false;
274 }
275 /**
276 * @return static
277 */
278 public abstract function folded() : self;
279 /**
280 * @return static
281 */
282 public function ignoreCase() : self
283 {
284 $str = clone $this;
285 $str->ignoreCase = \true;
286 return $str;
287 }
288 /**
289 * @param string|string[] $needle
290 */
291 public function indexOf($needle, int $offset = 0) : ?int
292 {
293 if (!\is_array($needle) && !$needle instanceof \Traversable) {
294 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
295 }
296 $i = \PHP_INT_MAX;
297 foreach ($needle as $n) {
298 $j = $this->indexOf((string) $n, $offset);
299 if (null !== $j && $j < $i) {
300 $i = $j;
301 }
302 }
303 return \PHP_INT_MAX === $i ? null : $i;
304 }
305 /**
306 * @param string|string[] $needle
307 */
308 public function indexOfLast($needle, int $offset = 0) : ?int
309 {
310 if (!\is_array($needle) && !$needle instanceof \Traversable) {
311 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
312 }
313 $i = null;
314 foreach ($needle as $n) {
315 $j = $this->indexOfLast((string) $n, $offset);
316 if (null !== $j && $j >= $i) {
317 $i = $offset = $j;
318 }
319 }
320 return $i;
321 }
322 public function isEmpty() : bool
323 {
324 return '' === $this->string;
325 }
326 /**
327 * @return static
328 */
329 public abstract function join(array $strings, ?string $lastGlue = null) : self;
330 public function jsonSerialize() : string
331 {
332 return $this->string;
333 }
334 public abstract function length() : int;
335 /**
336 * @return static
337 */
338 public abstract function lower() : self;
339 /**
340 * Matches the string using a regular expression.
341 *
342 * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression.
343 *
344 * @return array All matches in a multi-dimensional array ordered according to flags
345 */
346 public abstract function match(string $regexp, int $flags = 0, int $offset = 0) : array;
347 /**
348 * @return static
349 */
350 public abstract function padBoth(int $length, string $padStr = ' ') : self;
351 /**
352 * @return static
353 */
354 public abstract function padEnd(int $length, string $padStr = ' ') : self;
355 /**
356 * @return static
357 */
358 public abstract function padStart(int $length, string $padStr = ' ') : self;
359 /**
360 * @return static
361 */
362 public abstract function prepend(string ...$prefix) : self;
363 /**
364 * @return static
365 */
366 public function repeat(int $multiplier) : self
367 {
368 if (0 > $multiplier) {
369 throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
370 }
371 $str = clone $this;
372 $str->string = str_repeat($str->string, $multiplier);
373 return $str;
374 }
375 /**
376 * @return static
377 */
378 public abstract function replace(string $from, string $to) : self;
379 /**
380 * @param string|callable $to
381 *
382 * @return static
383 */
384 public abstract function replaceMatches(string $fromRegexp, $to) : self;
385 /**
386 * @return static
387 */
388 public abstract function reverse() : self;
389 /**
390 * @return static
391 */
392 public abstract function slice(int $start = 0, ?int $length = null) : self;
393 /**
394 * @return static
395 */
396 public abstract function snake() : self;
397 /**
398 * @return static
399 */
400 public abstract function splice(string $replacement, int $start = 0, ?int $length = null) : self;
401 /**
402 * @return static[]
403 */
404 public function split(string $delimiter, ?int $limit = null, ?int $flags = null) : array
405 {
406 if (null === $flags) {
407 throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
408 }
409 if ($this->ignoreCase) {
410 $delimiter .= 'i';
411 }
412 set_error_handler(static function ($t, $m) {
413 throw new InvalidArgumentException($m);
414 });
415 try {
416 if (\false === ($chunks = preg_split($delimiter, $this->string, $limit, $flags))) {
417 $lastError = preg_last_error();
418 foreach (get_defined_constants(\true)['pcre'] as $k => $v) {
419 if ($lastError === $v && '_ERROR' === substr($k, -6)) {
420 throw new RuntimeException('Splitting failed with ' . $k . '.');
421 }
422 }
423 throw new RuntimeException('Splitting failed with unknown error code.');
424 }
425 } finally {
426 restore_error_handler();
427 }
428 $str = clone $this;
429 if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
430 foreach ($chunks as &$chunk) {
431 $str->string = $chunk[0];
432 $chunk[0] = clone $str;
433 }
434 } else {
435 foreach ($chunks as &$chunk) {
436 $str->string = $chunk;
437 $chunk = clone $str;
438 }
439 }
440 return $chunks;
441 }
442 /**
443 * @param string|string[] $prefix
444 */
445 public function startsWith($prefix) : bool
446 {
447 if (!\is_array($prefix) && !$prefix instanceof \Traversable) {
448 throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
449 }
450 foreach ($prefix as $prefix) {
451 if ($this->startsWith((string) $prefix)) {
452 return \true;
453 }
454 }
455 return \false;
456 }
457 /**
458 * @return static
459 */
460 public abstract function title(bool $allWords = \false) : self;
461 public function toByteString(?string $toEncoding = null) : ByteString
462 {
463 $b = new ByteString();
464 $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], \true) ? 'UTF-8' : $toEncoding;
465 if (null === $toEncoding || $toEncoding === ($fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252')) {
466 $b->string = $this->string;
467 return $b;
468 }
469 set_error_handler(static function ($t, $m) {
470 throw new InvalidArgumentException($m);
471 });
472 try {
473 try {
474 $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
475 } catch (InvalidArgumentException|\ValueError $e) {
476 if (!\function_exists('iconv')) {
477 if ($e instanceof \ValueError) {
478 throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
479 }
480 throw $e;
481 }
482 $b->string = iconv('UTF-8', $toEncoding, $this->string);
483 }
484 } finally {
485 restore_error_handler();
486 }
487 return $b;
488 }
489 public function toCodePointString() : CodePointString
490 {
491 return new CodePointString($this->string);
492 }
493 public function toString() : string
494 {
495 return $this->string;
496 }
497 public function toUnicodeString() : UnicodeString
498 {
499 return new UnicodeString($this->string);
500 }
501 /**
502 * @return static
503 */
504 public abstract function trim(string $chars = " \t\n\r\x00\v\f\xC2\xA0") : self;
505 /**
506 * @return static
507 */
508 public abstract function trimEnd(string $chars = " \t\n\r\x00\v\f\xC2\xA0") : self;
509 /**
510 * @param string|string[] $prefix
511 *
512 * @return static
513 */
514 public function trimPrefix($prefix) : self
515 {
516 if (\is_array($prefix) || $prefix instanceof \Traversable) {
517 foreach ($prefix as $s) {
518 $t = $this->trimPrefix($s);
519 if ($t->string !== $this->string) {
520 return $t;
521 }
522 }
523 return clone $this;
524 }
525 $str = clone $this;
526 if ($prefix instanceof self) {
527 $prefix = $prefix->string;
528 } else {
529 $prefix = (string) $prefix;
530 }
531 if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) {
532 $str->string = substr($this->string, \strlen($prefix));
533 }
534 return $str;
535 }
536 /**
537 * @return static
538 */
539 public abstract function trimStart(string $chars = " \t\n\r\x00\v\f\xC2\xA0") : self;
540 /**
541 * @param string|string[] $suffix
542 *
543 * @return static
544 */
545 public function trimSuffix($suffix) : self
546 {
547 if (\is_array($suffix) || $suffix instanceof \Traversable) {
548 foreach ($suffix as $s) {
549 $t = $this->trimSuffix($s);
550 if ($t->string !== $this->string) {
551 return $t;
552 }
553 }
554 return clone $this;
555 }
556 $str = clone $this;
557 if ($suffix instanceof self) {
558 $suffix = $suffix->string;
559 } else {
560 $suffix = (string) $suffix;
561 }
562 if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) {
563 $str->string = substr($this->string, 0, -\strlen($suffix));
564 }
565 return $str;
566 }
567 /**
568 * @return static
569 */
570 public function truncate(int $length, string $ellipsis = '', bool $cut = \true) : self
571 {
572 $stringLength = $this->length();
573 if ($stringLength <= $length) {
574 return clone $this;
575 }
576 $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;
577 if ($length < $ellipsisLength) {
578 $ellipsisLength = 0;
579 }
580 if (!$cut) {
581 if (null === ($length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1))) {
582 return clone $this;
583 }
584 $length += $ellipsisLength;
585 }
586 $str = $this->slice(0, $length - $ellipsisLength);
587 return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
588 }
589 /**
590 * @return static
591 */
592 public abstract function upper() : self;
593 /**
594 * Returns the printable length on a terminal.
595 */
596 public abstract function width(bool $ignoreAnsiDecoration = \true) : int;
597 /**
598 * @return static
599 */
600 public function wordwrap(int $width = 75, string $break = "\n", bool $cut = \false) : self
601 {
602 $lines = '' !== $break ? $this->split($break) : [clone $this];
603 $chars = [];
604 $mask = '';
605 if (1 === \count($lines) && '' === $lines[0]->string) {
606 return $lines[0];
607 }
608 foreach ($lines as $i => $line) {
609 if ($i) {
610 $chars[] = $break;
611 $mask .= '#';
612 }
613 foreach ($line->chunk() as $char) {
614 $chars[] = $char->string;
615 $mask .= ' ' === $char->string ? ' ' : '?';
616 }
617 }
618 $string = '';
619 $j = 0;
620 $b = $i = -1;
621 $mask = wordwrap($mask, $width, '#', $cut);
622 while (\false !== ($b = strpos($mask, '#', $b + 1))) {
623 for (++$i; $i < $b; ++$i) {
624 $string .= $chars[$j];
625 unset($chars[$j++]);
626 }
627 if ($break === $chars[$j] || ' ' === $chars[$j]) {
628 unset($chars[$j++]);
629 }
630 $string .= $break;
631 }
632 $str = clone $this;
633 $str->string = $string . implode('', $chars);
634 return $str;
635 }
636 public function __sleep() : array
637 {
638 return ['string'];
639 }
640 public function __clone()
641 {
642 $this->ignoreCase = \false;
643 }
644 public function __toString() : string
645 {
646 return $this->string;
647 }
648 }
649