Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 150 | Ganze Datei anzeigen | Leerzeichen ignorieren | Details | Blame | Letzte Änderung | Log anzeigen | RSS feed

Revision 150 Revision 399
Zeile 19... Zeile 19...
19
 */
19
 */
20
class Strings
20
class Strings
21
{
21
{
22
	use Nette\StaticClass;
22
	use Nette\StaticClass;
Zeile 23... Zeile 23...
23
 
23
 
-
 
24
	public const TrimCharacters = " \t\n\r\0\x0B\u{A0}";
-
 
25
 
-
 
26
	/** @deprecated use Strings::TrimCharacters */
Zeile 24... Zeile 27...
24
	public const TRIM_CHARACTERS = " \t\n\r\0\x0B\u{A0}";
27
	public const TRIM_CHARACTERS = self::TrimCharacters;
25
 
28
 
26
 
29
 
27
	/**
30
	/**
28
	 * Checks if the string is valid in UTF-8 encoding.
31
	 * @deprecated use Nette\Utils\Validator::isUnicode()
29
	 */
32
	 */
30
	public static function checkEncoding(string $s): bool
33
	public static function checkEncoding(string $s): bool
Zeile 58... Zeile 61...
58
		return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code));
61
		return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code));
59
	}
62
	}
Zeile 60... Zeile 63...
60
 
63
 
-
 
64
 
-
 
65
	/**
61
 
66
	 * Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF).
-
 
67
	 */
-
 
68
	public static function ord(string $c): int
-
 
69
	{
-
 
70
		if (!extension_loaded('iconv')) {
-
 
71
			throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.');
-
 
72
		}
-
 
73
 
-
 
74
		$tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c);
-
 
75
		if (!$tmp) {
-
 
76
			throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".');
-
 
77
		}
-
 
78
 
-
 
79
		return unpack('N', $tmp)[1];
-
 
80
	}
-
 
81
 
-
 
82
 
62
	/**
83
	/**
63
	 * Starts the $haystack string with the prefix $needle?
84
	 * @deprecated use str_starts_with()
64
	 */
85
	 */
65
	public static function startsWith(string $haystack, string $needle): bool
86
	public static function startsWith(string $haystack, string $needle): bool
66
	{
87
	{
Zeile 67... Zeile 88...
67
		return strncmp($haystack, $needle, strlen($needle)) === 0;
88
		return str_starts_with($haystack, $needle);
68
	}
89
	}
69
 
90
 
70
 
91
 
71
	/**
92
	/**
72
	 * Ends the $haystack string with the suffix $needle?
93
	 * @deprecated use str_ends_with()
73
	 */
94
	 */
Zeile 74... Zeile 95...
74
	public static function endsWith(string $haystack, string $needle): bool
95
	public static function endsWith(string $haystack, string $needle): bool
75
	{
96
	{
76
		return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
97
		return str_ends_with($haystack, $needle);
77
	}
98
	}
78
 
99
 
79
 
100
 
80
	/**
101
	/**
Zeile 81... Zeile 102...
81
	 * Does $haystack contain $needle?
102
	 * @deprecated use str_contains()
82
	 */
103
	 */
Zeile 115... Zeile 136...
115
		// convert to compressed normal form (NFC)
136
		// convert to compressed normal form (NFC)
116
		if (class_exists('Normalizer', false) && ($n = \Normalizer::normalize($s, \Normalizer::FORM_C)) !== false) {
137
		if (class_exists('Normalizer', false) && ($n = \Normalizer::normalize($s, \Normalizer::FORM_C)) !== false) {
117
			$s = $n;
138
			$s = $n;
118
		}
139
		}
Zeile 119... Zeile 140...
119
 
140
 
Zeile 120... Zeile 141...
120
		$s = self::normalizeNewLines($s);
141
		$s = self::unixNewLines($s);
121
 
142
 
Zeile 122... Zeile 143...
122
		// remove control characters; leave \t + \n
143
		// remove control characters; leave \t + \n
Zeile 130... Zeile 151...
130
 
151
 
131
		return $s;
152
		return $s;
Zeile -... Zeile 153...
-
 
153
	}
-
 
154
 
-
 
155
 
-
 
156
	/** @deprecated use Strings::unixNewLines() */
-
 
157
	public static function normalizeNewLines(string $s): string
-
 
158
	{
-
 
159
		return self::unixNewLines($s);
132
	}
160
	}
133
 
161
 
-
 
162
 
134
 
163
	/**
135
	/**
164
	 * Converts line endings to \n used on Unix-like systems.
-
 
165
	 * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
-
 
166
	 */
-
 
167
	public static function unixNewLines(string $s): string
-
 
168
	{
-
 
169
		return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s);
-
 
170
	}
-
 
171
 
-
 
172
 
-
 
173
	/**
-
 
174
	 * Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere.
136
	 * Standardize line endings to unix-like.
175
	 * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
137
	 */
176
	 */
138
	public static function normalizeNewLines(string $s): string
177
	public static function platformNewLines(string $s): string
Zeile 139... Zeile 178...
139
	{
178
	{
140
		return str_replace(["\r\n", "\r"], "\n", $s);
179
		return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s);
Zeile 185... Zeile 224...
185
				// glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
224
				// glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
186
				$s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
225
				$s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
187
				$s = strtr(
226
				$s = strtr(
188
					$s,
227
					$s,
189
					"\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
228
					"\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
190
					'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.'
229
					'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.',
191
				);
230
				);
192
				$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
231
				$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
193
			} else {
232
			} else {
194
				$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
233
				$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
195
			}
234
			}
Zeile 365... Zeile 404...
365
 
404
 
366
 
405
 
367
	/**
406
	/**
368
	 * Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string.
407
	 * Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string.
369
	 */
408
	 */
370
	public static function trim(string $s, string $charlist = self::TRIM_CHARACTERS): string
409
	public static function trim(string $s, string $charlist = self::TrimCharacters): string
371
	{
410
	{
372
		$charlist = preg_quote($charlist, '#');
411
		$charlist = preg_quote($charlist, '#');
Zeile 481... Zeile 520...
481
		return Helpers::falseToNull($pos);
520
		return Helpers::falseToNull($pos);
482
	}
521
	}
Zeile 483... Zeile 522...
483
 
522
 
484
 
523
 
485
	/**
-
 
486
	 * Splits a string into array by the regular expression. Parenthesized expression in the delimiter are captured.
524
	/**
487
	 * Parameter $flags can be any combination of PREG_SPLIT_NO_EMPTY and PREG_OFFSET_CAPTURE flags.
525
	 * Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well.
488
	 */
526
	 */
489
	public static function split(
527
	public static function split(
490
		string $subject,
528
		string $subject,
-
 
529
		#[Language('RegExp')]
-
 
530
		string $pattern,
491
		#[Language('RegExp')]
531
		bool|int $captureOffset = false,
-
 
532
		bool $skipEmpty = false,
492
		string $pattern,
533
		int $limit = -1,
493
		int $flags = 0
534
		bool $utf8 = false,
-
 
535
	): array
-
 
536
	{
-
 
537
		$flags = is_int($captureOffset)  // back compatibility
-
 
538
			? $captureOffset
-
 
539
			: ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0);
494
	): array
540
 
-
 
541
		$pattern .= $utf8 ? 'u' : '';
-
 
542
		$m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]);
-
 
543
		return $utf8 && $captureOffset
-
 
544
			? self::bytesToChars($subject, [$m])[0]
495
	{
545
			: $m;
Zeile 496... Zeile 546...
496
		return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]);
546
 
497
	}
547
	}
498
 
548
 
499
 
549
 
500
	/**
550
	/**
501
	 * Checks if given string matches a regular expression pattern and returns an array with first found match and each subpattern.
551
	 * Searches the string for the part matching the regular expression and returns
502
	 * Parameter $flags can be any combination of PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL flags.
552
	 * an array with the found expression and individual subexpressions, or `null`.
503
	 */
553
	 */
504
	public static function match(
554
	public static function match(
505
		string $subject,
555
		string $subject,
-
 
556
		#[Language('RegExp')]
-
 
557
		string $pattern,
506
		#[Language('RegExp')]
558
		bool|int $captureOffset = false,
507
		string $pattern,
559
		int $offset = 0,
-
 
560
		bool $unmatchedAsNull = false,
-
 
561
		bool $utf8 = false,
-
 
562
	): ?array
-
 
563
	{
-
 
564
		$flags = is_int($captureOffset) // back compatibility
-
 
565
			? $captureOffset
-
 
566
			: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
-
 
567
 
-
 
568
		if ($utf8) {
508
		int $flags = 0,
569
			$offset = strlen(self::substring($subject, 0, $offset));
509
		int $offset = 0
570
			$pattern .= 'u';
-
 
571
		}
-
 
572
 
-
 
573
		if ($offset > strlen($subject)) {
-
 
574
			return null;
-
 
575
		} elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) {
-
 
576
			return null;
510
	): ?array
577
		} elseif ($utf8 && $captureOffset) {
511
	{
-
 
512
		if ($offset > strlen($subject)) {
-
 
513
			return null;
-
 
514
		}
-
 
515
 
578
			return self::bytesToChars($subject, [$m])[0];
Zeile 516... Zeile 579...
516
		return self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])
579
		} else {
517
			? $m
580
			return $m;
518
			: null;
581
		}
519
	}
582
	}
520
 
583
 
521
 
584
 
522
	/**
585
	/**
523
	 * Finds all occurrences matching regular expression pattern and returns a two-dimensional array. Result is array of matches (ie uses by default PREG_SET_ORDER).
586
	 * Searches the string for all occurrences matching the regular expression and
524
	 * Parameter $flags can be any combination of PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL and PREG_PATTERN_ORDER flags.
587
	 * returns an array of arrays containing the found expression and each subexpression.
525
	 */
588
	 */
-
 
589
	public static function matchAll(
-
 
590
		string $subject,
-
 
591
		#[Language('RegExp')]
526
	public static function matchAll(
592
		string $pattern,
527
		string $subject,
593
		bool|int $captureOffset = false,
-
 
594
		int $offset = 0,
-
 
595
		bool $unmatchedAsNull = false,
-
 
596
		bool $patternOrder = false,
-
 
597
		bool $utf8 = false,
-
 
598
	): array
-
 
599
	{
-
 
600
		$flags = is_int($captureOffset) // back compatibility
-
 
601
			? $captureOffset
-
 
602
			: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0);
528
		#[Language('RegExp')]
603
 
529
		string $pattern,
604
		if ($utf8) {
530
		int $flags = 0,
605
			$offset = strlen(self::substring($subject, 0, $offset));
Zeile 531... Zeile 606...
531
		int $offset = 0
606
			$pattern .= 'u';
532
	): array
607
		}
533
	{
608
 
534
		if ($offset > strlen($subject)) {
609
		if ($offset > strlen($subject)) {
535
			return [];
610
			return [];
-
 
611
		}
-
 
612
 
536
		}
613
		self::pcre('preg_match_all', [
-
 
614
			$pattern, $subject, &$m,
537
 
615
			($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
Zeile 538... Zeile 616...
538
		self::pcre('preg_match_all', [
616
			$offset,
539
			$pattern, $subject, &$m,
617
		]);
540
			($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
-
 
541
			$offset,
-
 
542
		]);
618
		return $utf8 && $captureOffset
543
		return $m;
619
			? self::bytesToChars($subject, $m)
544
	}
620
			: $m;
545
 
621
 
546
 
622
	}
547
	/**
623
 
548
	 * Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`.
624
 
-
 
625
	/**
-
 
626
	 * Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`.
-
 
627
	 */
549
	 * @param  string|array  $pattern
628
	public static function replace(
550
	 * @param  string|callable  $replacement
629
		string $subject,
551
	 */
630
		#[Language('RegExp')]
552
	public static function replace(
631
		string|array $pattern,
553
		string $subject,
632
		string|callable $replacement = '',
554
		#[Language('RegExp')]
633
		int $limit = -1,
Zeile -... Zeile 634...
-
 
634
		bool $captureOffset = false,
-
 
635
		bool $unmatchedAsNull = false,
-
 
636
		bool $utf8 = false,
-
 
637
	): string
-
 
638
	{
-
 
639
		if (is_object($replacement) || is_array($replacement)) {
-
 
640
			if (!is_callable($replacement, false, $textual)) {
-
 
641
				throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
555
		$pattern,
642
			}
Zeile 556... Zeile 643...
556
		$replacement = '',
643
 
557
		int $limit = -1
644
			$flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
558
	): string
645
			if ($utf8) {
559
	{
646
				$pattern .= 'u';
Zeile -... Zeile 647...
-
 
647
				if ($captureOffset) {
-
 
648
					$replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]);
-
 
649
				}
-
 
650
			}
560
		if (is_object($replacement) || is_array($replacement)) {
651
 
561
			if (!is_callable($replacement, false, $textual)) {
652
			return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]);
Zeile -... Zeile 653...
-
 
653
 
-
 
654
		} elseif (is_array($pattern) && is_string(key($pattern))) {
-
 
655
			$replacement = array_values($pattern);
-
 
656
			$pattern = array_keys($pattern);
-
 
657
		}
-
 
658
 
-
 
659
		if ($utf8) {
-
 
660
			$pattern = array_map(fn($item) => $item . 'u', (array) $pattern);
-
 
661
		}
-
 
662
 
-
 
663
		return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]);
-
 
664
	}
-
 
665
 
-
 
666
 
-
 
667
	private static function bytesToChars(string $s, array $groups): array
-
 
668
	{
-
 
669
		$lastBytes = $lastChars = 0;
-
 
670
		foreach ($groups as &$matches) {
-
 
671
			foreach ($matches as &$match) {
-
 
672
				if ($match[1] > $lastBytes) {
562
				throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
673
					$lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes));
563
			}
674
				} elseif ($match[1] < $lastBytes) {
564
 
675
					$lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1]));
565
			return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit]);
676
				}
566
 
677
 
Zeile 582... Zeile 693...
582
		});
693
		});
Zeile 583... Zeile 694...
583
 
694
 
584
		if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars
695
		if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars
585
			&& ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true))
696
			&& ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true))
586
		) {
697
		) {
587
			throw new RegexpException((RegexpException::MESSAGES[$code] ?? 'Unknown error')
698
			throw new RegexpException(preg_last_error_msg()
588
				. ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code);
699
				. ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code);
Zeile 589... Zeile 700...
589
		}
700
		}
590
 
701