PluginProbe
SendWP / 1.4.4
SendWP v1.4.4
trunk 0.0.1 0.0.2 0.0.3 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1 1.2.10 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.3.1 1.4.4 1.4.5 1.4.6 1.4.8
sendwp / vendor / symfony / string / ByteString.php

ByteString.php in SendWP 1.4.4, at vendor/symfony/string/ByteString.php

486 lines 14.3 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 binary-safe string of bytes.
20 *
21 * @author Nicolas Grekas <p@tchwork.com>
22 * @author Hugo Hamon <hugohamon@neuf.fr>
23 *
24 * @throws ExceptionInterface
25 */
26 class ByteString extends AbstractString
27 {
28 private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
29
30 public function __construct(string $string = '')
31 {
32 $this->string = $string;
33 }
34
35 /*
36 * The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03)
37 *
38 * https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16
39 *
40 * Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE).
41 *
42 * Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/)
43 */
44
45 public static function fromRandom(int $length = 16, string $alphabet = null): self
46 {
47 if ($length <= 0) {
48 throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length));
49 }
50
51 $alphabet ??= self::ALPHABET_ALPHANUMERIC;
52 $alphabetSize = \strlen($alphabet);
53 $bits = (int) ceil(log($alphabetSize, 2.0));
54 if ($bits <= 0 || $bits > 56) {
55 throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.');
56 }
57
58 $ret = '';
59 while ($length > 0) {
60 $urandomLength = (int) ceil(2 * $length * $bits / 8.0);
61 $data = random_bytes($urandomLength);
62 $unpackedData = 0;
63 $unpackedBits = 0;
64 for ($i = 0; $i < $urandomLength && $length > 0; ++$i) {
65 // Unpack 8 bits
66 $unpackedData = ($unpackedData << 8) | \ord($data[$i]);
67 $unpackedBits += 8;
68
69 // While we have enough bits to select a character from the alphabet, keep
70 // consuming the random data
71 for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) {
72 $index = ($unpackedData & ((1 << $bits) - 1));
73 $unpackedData >>= $bits;
74 // Unfortunately, the alphabet size is not necessarily a power of two.
75 // Worst case, it is 2^k + 1, which means we need (k+1) bits and we
76 // have around a 50% chance of missing as k gets larger
77 if ($index < $alphabetSize) {
78 $ret .= $alphabet[$index];
79 --$length;
80 }
81 }
82 }
83 }
84
85 return new static($ret);
86 }
87
88 public function bytesAt(int $offset): array
89 {
90 $str = $this->string[$offset] ?? '';
91
92 return '' === $str ? [] : [\ord($str)];
93 }
94
95 public function append(string ...$suffix): static
96 {
97 $str = clone $this;
98 $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix);
99
100 return $str;
101 }
102
103 public function camel(): static
104 {
105 $str = clone $this;
106
107 $parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string))));
108 $parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]);
109 $str->string = implode('', $parts);
110
111 return $str;
112 }
113
114 public function chunk(int $length = 1): array
115 {
116 if (1 > $length) {
117 throw new InvalidArgumentException('The chunk length must be greater than zero.');
118 }
119
120 if ('' === $this->string) {
121 return [];
122 }
123
124 $str = clone $this;
125 $chunks = [];
126
127 foreach (str_split($this->string, $length) as $chunk) {
128 $str->string = $chunk;
129 $chunks[] = clone $str;
130 }
131
132 return $chunks;
133 }
134
135 public function endsWith(string|iterable|AbstractString $suffix): bool
136 {
137 if ($suffix instanceof AbstractString) {
138 $suffix = $suffix->string;
139 } elseif (!\is_string($suffix)) {
140 return parent::endsWith($suffix);
141 }
142
143 return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase);
144 }
145
146 public function equalsTo(string|iterable|AbstractString $string): bool
147 {
148 if ($string instanceof AbstractString) {
149 $string = $string->string;
150 } elseif (!\is_string($string)) {
151 return parent::equalsTo($string);
152 }
153
154 if ('' !== $string && $this->ignoreCase) {
155 return 0 === strcasecmp($string, $this->string);
156 }
157
158 return $string === $this->string;
159 }
160
161 public function folded(): static
162 {
163 $str = clone $this;
164 $str->string = strtolower($str->string);
165
166 return $str;
167 }
168
169 public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int
170 {
171 if ($needle instanceof AbstractString) {
172 $needle = $needle->string;
173 } elseif (!\is_string($needle)) {
174 return parent::indexOf($needle, $offset);
175 }
176
177 if ('' === $needle) {
178 return null;
179 }
180
181 $i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset);
182
183 return false === $i ? null : $i;
184 }
185
186 public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int
187 {
188 if ($needle instanceof AbstractString) {
189 $needle = $needle->string;
190 } elseif (!\is_string($needle)) {
191 return parent::indexOfLast($needle, $offset);
192 }
193
194 if ('' === $needle) {
195 return null;
196 }
197
198 $i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset);
199
200 return false === $i ? null : $i;
201 }
202
203 public function isUtf8(): bool
204 {
205 return '' === $this->string || preg_match('//u', $this->string);
206 }
207
208 public function join(array $strings, string $lastGlue = null): static
209 {
210 $str = clone $this;
211
212 $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : '';
213 $str->string = implode($this->string, $strings).$tail;
214
215 return $str;
216 }
217
218 public function length(): int
219 {
220 return \strlen($this->string);
221 }
222
223 public function lower(): static
224 {
225 $str = clone $this;
226 $str->string = strtolower($str->string);
227
228 return $str;
229 }
230
231 public function match(string $regexp, int $flags = 0, int $offset = 0): array
232 {
233 $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match';
234
235 if ($this->ignoreCase) {
236 $regexp .= 'i';
237 }
238
239 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
240
241 try {
242 if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) {
243 throw new RuntimeException('Matching failed with error: '.preg_last_error_msg());
244 }
245 } finally {
246 restore_error_handler();
247 }
248
249 return $matches;
250 }
251
252 public function padBoth(int $length, string $padStr = ' '): static
253 {
254 $str = clone $this;
255 $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH);
256
257 return $str;
258 }
259
260 public function padEnd(int $length, string $padStr = ' '): static
261 {
262 $str = clone $this;
263 $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT);
264
265 return $str;
266 }
267
268 public function padStart(int $length, string $padStr = ' '): static
269 {
270 $str = clone $this;
271 $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT);
272
273 return $str;
274 }
275
276 public function prepend(string ...$prefix): static
277 {
278 $str = clone $this;
279 $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string;
280
281 return $str;
282 }
283
284 public function replace(string $from, string $to): static
285 {
286 $str = clone $this;
287
288 if ('' !== $from) {
289 $str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string);
290 }
291
292 return $str;
293 }
294
295 public function replaceMatches(string $fromRegexp, string|callable $to): static
296 {
297 if ($this->ignoreCase) {
298 $fromRegexp .= 'i';
299 }
300
301 $replace = \is_array($to) || $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace';
302
303 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
304
305 try {
306 if (null === $string = $replace($fromRegexp, $to, $this->string)) {
307 $lastError = preg_last_error();
308
309 foreach (get_defined_constants(true)['pcre'] as $k => $v) {
310 if ($lastError === $v && str_ends_with($k, '_ERROR')) {
311 throw new RuntimeException('Matching failed with '.$k.'.');
312 }
313 }
314
315 throw new RuntimeException('Matching failed with unknown error code.');
316 }
317 } finally {
318 restore_error_handler();
319 }
320
321 $str = clone $this;
322 $str->string = $string;
323
324 return $str;
325 }
326
327 public function reverse(): static
328 {
329 $str = clone $this;
330 $str->string = strrev($str->string);
331
332 return $str;
333 }
334
335 public function slice(int $start = 0, int $length = null): static
336 {
337 $str = clone $this;
338 $str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX);
339
340 return $str;
341 }
342
343 public function snake(): static
344 {
345 $str = $this->camel();
346 $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string));
347
348 return $str;
349 }
350
351 public function splice(string $replacement, int $start = 0, int $length = null): static
352 {
353 $str = clone $this;
354 $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX);
355
356 return $str;
357 }
358
359 public function split(string $delimiter, int $limit = null, int $flags = null): array
360 {
361 if (1 > $limit ??= \PHP_INT_MAX) {
362 throw new InvalidArgumentException('Split limit must be a positive integer.');
363 }
364
365 if ('' === $delimiter) {
366 throw new InvalidArgumentException('Split delimiter is empty.');
367 }
368
369 if (null !== $flags) {
370 return parent::split($delimiter, $limit, $flags);
371 }
372
373 $str = clone $this;
374 $chunks = $this->ignoreCase
375 ? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit)
376 : explode($delimiter, $this->string, $limit);
377
378 foreach ($chunks as &$chunk) {
379 $str->string = $chunk;
380 $chunk = clone $str;
381 }
382
383 return $chunks;
384 }
385
386 public function startsWith(string|iterable|AbstractString $prefix): bool
387 {
388 if ($prefix instanceof AbstractString) {
389 $prefix = $prefix->string;
390 } elseif (!\is_string($prefix)) {
391 return parent::startsWith($prefix);
392 }
393
394 return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix)));
395 }
396
397 public function title(bool $allWords = false): static
398 {
399 $str = clone $this;
400 $str->string = $allWords ? ucwords($str->string) : ucfirst($str->string);
401
402 return $str;
403 }
404
405 public function toUnicodeString(string $fromEncoding = null): UnicodeString
406 {
407 return new UnicodeString($this->toCodePointString($fromEncoding)->string);
408 }
409
410 public function toCodePointString(string $fromEncoding = null): CodePointString
411 {
412 $u = new CodePointString();
413
414 if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) {
415 $u->string = $this->string;
416
417 return $u;
418 }
419
420 set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
421
422 try {
423 try {
424 $validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true);
425 } catch (InvalidArgumentException $e) {
426 if (!\function_exists('iconv')) {
427 throw $e;
428 }
429
430 $u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string);
431
432 return $u;
433 }
434 } finally {
435 restore_error_handler();
436 }
437
438 if (!$validEncoding) {
439 throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252'));
440 }
441
442 $u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252');
443
444 return $u;
445 }
446
447 public function trim(string $chars = " \t\n\r\0\x0B\x0C"): static
448 {
449 $str = clone $this;
450 $str->string = trim($str->string, $chars);
451
452 return $str;
453 }
454
455 public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): static
456 {
457 $str = clone $this;
458 $str->string = rtrim($str->string, $chars);
459
460 return $str;
461 }
462
463 public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): static
464 {
465 $str = clone $this;
466 $str->string = ltrim($str->string, $chars);
467
468 return $str;
469 }
470
471 public function upper(): static
472 {
473 $str = clone $this;
474 $str->string = strtoupper($str->string);
475
476 return $str;
477 }
478
479 public function width(bool $ignoreAnsiDecoration = true): int
480 {
481 $string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string);
482
483 return (new CodePointString($string))->width($ignoreAnsiDecoration);
484 }
485 }
486