Completed
Push — master ( 30d3de...6bbfea )
by Xu
29:18 queued 23:02
created

StringHelper::substr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link http://www.tintsoft.com/
4
 * @copyright Copyright (c) 2012 TintSoft Technology Co. Ltd.
5
 * @license http://www.tintsoft.com/license/
6
 */
7
8
namespace yuncms\helpers;
9
10
use Yii;
11
use Stringy\Stringy as BaseStringy;
12
13
/**
14
 * Class StringHelper
15
 *
16
 * @author Tongle Xu <[email protected]>
17
 * @since 3.0
18
 */
19
class StringHelper extends \yii\helpers\StringHelper
20
{
21
    /**
22
     * @var
23
     */
24
    private static $_asciiCharMap;
25
26
    /**
27
     * 查找 指定字符串出现的位置
28
     * @param string $string
29
     * @param string $needle
30
     * @param int $offset
31
     * @return bool|false|int
32
     */
33
    public static function byteStrPos($string, $needle, $offset = 0)
34
    {
35
        return mb_strpos($string, $needle, $offset, '8bit');
36
    }
37
38
    /**
39
     * 提取两个字符串之间的值,不包括分隔符
40
     *
41
     * @param string $string 待提取的只付出
42
     * @param string $start 开始字符串
43
     * @param string|null $end 结束字符串,省略将返回所有的。
44
     * @return bool|string substring between $start and $end or false if either string is not found
45
     */
46
    public static function byteStrBetween($string, $start, $end = null)
47
    {
48
        if (($startPos = static::byteStrPos($string, $start)) !== false) {
0 ignored issues
show
introduced by
The condition $startPos = static::byte...ring, $start) !== false can never be false.
Loading history...
49
            if ($end) {
50
                if (($end_pos = static::byteStrPos($string, $end, $startPos + static::byteLength($start))) !== false) {
0 ignored issues
show
introduced by
The condition $end_pos = static::byteS...ngth($start)) !== false can never be false.
Loading history...
51
                    return static::byteSubstr($string, $startPos + static::byteLength($start), $end_pos - ($startPos + static::byteLength($start)));
52
                }
53
            } else {
54
                return static::byteSubstr($string, $startPos);
55
            }
56
        }
57
        return false;
58
    }
59
60
    /**
61
     * Returns a camelCase version of the given string. Trims surrounding spaces, capitalizes letters following digits,
62
     * spaces, dashes and underscores, and removes spaces, dashes, as well as underscores.
63
     *
64
     * @param string $str The string to convert to camelCase.
65
     * @return string The string in camelCase.
66
     */
67
    public static function camelCase(string $str): string
68
    {
69
        return (string)BaseStringy::create($str)->camelize();
70
    }
71
72
    /**
73
     * Returns an array consisting of the characters in the string.
74
     *
75
     * @param string $str
76
     * @return string[] An array of string chars
77
     */
78
    public static function charsAsArray(string $str): array
79
    {
80
        return BaseStringy::create($str)->chars();
81
    }
82
83
    /**
84
     * Trims the string and replaces consecutive whitespace characters with a single space. This includes tabs and
85
     * newline characters, as well as multibyte whitespace such as the thin space and ideographic space.
86
     *
87
     * @param string $str The string to the whitespace from.
88
     * @return string The trimmed string with condensed whitespace
89
     */
90
    public static function collapseWhitespace(string $str): string
91
    {
92
        return (string)BaseStringy::create($str)->collapseWhitespace();
93
    }
94
95
    /**
96
     * Returns true if the string contains $needle, false otherwise. By default, the comparison is case-sensitive, but
97
     * can be made insensitive by setting $caseSensitive to false.
98
     *
99
     * @param string $haystack The string being checked.
100
     * @param string $needle The substring to look for.
101
     * @param bool $caseSensitive Whether or not to force case-sensitivity.
102
     * @return bool Whether or not $haystack contains $needle.
103
     */
104
    public static function contains(string $haystack, string $needle, bool $caseSensitive = true): bool
105
    {
106
        return BaseStringy::create($haystack)->contains($needle, $caseSensitive);
107
    }
108
109
    /**
110
     * Returns true if the string contains any $needles, false otherwise. By default, the comparison is case-sensitive,
111
     * but can be made insensitive by setting $caseSensitive to false.
112
     *
113
     * @param string $haystack The string being checked.
114
     * @param array $needles The substrings to look for.
115
     * @param bool $caseSensitive Whether or not to force case-sensitivity.
116
     * @return bool Whether or not $haystack contains any $needles.
117
     */
118
    public static function containsAny(string $haystack, array $needles, bool $caseSensitive = true): bool
119
    {
120
        return BaseStringy::create($haystack)->containsAny($needles, $caseSensitive);
121
    }
122
123
    /**
124
     * Returns true if the string contains all $needles, false otherwise. By default, the comparison is case-sensitive,
125
     * but can be made insensitive by setting $caseSensitive to false.
126
     *
127
     * @param string $haystack The string being checked.
128
     * @param array $needles The substrings to look for.
129
     * @param bool $caseSensitive Whether or not to force case-sensitivity.
130
     * @return bool Whether or not $haystack contains all $needles.
131
     */
132
    public static function containsAll(string $haystack, array $needles, bool $caseSensitive = true): bool
133
    {
134
        return BaseStringy::create($haystack)->containsAll($needles, $caseSensitive);
135
    }
136
137
    /**
138
     * Returns the number of occurrences of $substring in the given string. By default, the comparison is case-sensitive,
139
     * but can be made insensitive by setting $caseSensitive to false.
140
     *
141
     * @param string $str The string to search through.
142
     * @param string $substring The substring to search for.
143
     * @param bool $caseSensitive Whether or not to enforce case-sensitivity
144
     * @return int The number of $substring occurrences.
145
     */
146
    public static function countSubstrings(string $str, string $substring, bool $caseSensitive = true): int
147
    {
148
        return BaseStringy::create($str)->countSubstr($substring, $caseSensitive);
149
    }
150
151
    /**
152
     * Returns a lowercase and trimmed string separated by the given delimiter. Delimiters are inserted before
153
     * uppercase characters (with the exception of the first character of the string), and in place of spaces,
154
     * dashes, and underscores. Alpha delimiters are not converted to lowercase.
155
     *
156
     * @param string $str The string to delimit.
157
     * @param string $delimiter Sequence used to separate parts of the string
158
     * @return string The delimited string.
159
     */
160
    public static function delimit(string $str, string $delimiter): string
161
    {
162
        return (string)BaseStringy::create($str)->delimit($delimiter);
163
    }
164
165
    /**
166
     * Returns true if the string ends with $substring, false otherwise. By default, the comparison is case-sensitive,
167
     * but can be made insensitive by setting $caseSensitive to false.
168
     *
169
     * @param string $str The string to check the end of.
170
     * @param string $substring The substring to look for.
171
     * @param bool $caseSensitive Whether or not to force case-sensitivity.
172
     * @return bool Whether or not $str ends with $substring.
173
     */
174
    public static function endsWith($str, $substring, $caseSensitive = true): bool
175
    {
176
        return BaseStringy::create($str)->endsWith($substring, $caseSensitive);
177
    }
178
179
    /**
180
     * Ensures that the string begins with $substring. If it doesn't, it's prepended.
181
     *
182
     * @param string $str The string to modify.
183
     * @param string $substring The substring to add if not present.
184
     * @return string The string prefixed by the $substring.
185
     */
186
    public static function ensureLeft(string $str, string $substring): string
187
    {
188
        return (string)BaseStringy::create($str)->ensureLeft($substring);
189
    }
190
191
    /**
192
     * Ensures that the string ends with $substring. If it doesn't, it's appended.
193
     *
194
     * @param string $str The string to modify.
195
     * @param string $substring The substring to add if not present.
196
     * @return string The string suffixed by the $substring.
197
     */
198
    public static function ensureRight(string $str, string $substring): string
199
    {
200
        return (string)BaseStringy::create($str)->ensureRight($substring);
201
    }
202
203
    /**
204
     * Returns the first $n characters of the string.
205
     *
206
     * @param string $str The string from which to get the substring.
207
     * @param int $number The Number of chars to retrieve from the start.
208
     * @return string The first $number characters.
209
     */
210
    public static function first(string $str, int $number): string
211
    {
212
        return (string)BaseStringy::create($str)->first($number);
213
    }
214
215
    /**
216
     * Returns the character at a specific point in a potentially multibyte string.
217
     *
218
     * @param string $str The string to check.
219
     * @param int $i The 0-offset position in the string to check.
220
     * @return string
221
     */
222
    public static function charAt(string $str, int $i): string
223
    {
224
        return (string)BaseStringy::create($str)->at($i);
225
    }
226
227
    /**
228
     * Returns whether the given string has any lowercase characters in it.
229
     *
230
     * @param string $str The string to check.
231
     * @return bool
232
     */
233
    public static function hasLowerCase(string $str): bool
234
    {
235
        return BaseStringy::create($str)->hasLowerCase();
236
    }
237
238
    /**
239
     * Returns whether the given string has any uppercase characters in it.
240
     *
241
     * @param string $str The string to check.
242
     * @return bool
243
     */
244
    public static function hasUpperCase(string $str): bool
245
    {
246
        return BaseStringy::create($str)->hasUpperCase();
247
    }
248
249
    /**
250
     * Returns the index of the first occurrence of $needle in the string, and false if not found.
251
     * Accepts an optional offset from which to begin the search.
252
     *
253
     * @param  string $str The string to check the index of.
254
     * @param  string $needle The substring to look for.
255
     * @param  int $offset The offset from which to search.
256
     * @return int|bool The occurrence's index if found, otherwise false.
257
     */
258
    public static function indexOf($str, $needle, $offset = 0)
259
    {
260
        return BaseStringy::create($str)->indexOf($needle, $offset);
261
    }
262
263
    /**
264
     * Returns the index of the last occurrence of $needle in the string,and false if not found.
265
     * Accepts an optional offset from which to begin the search. Offsets may be negative to count from
266
     * the last character in the string.
267
     *
268
     * @param  string $str The string to check the last index of.
269
     * @param  string $needle The substring to look for.
270
     * @param  int $offset The offset from which to search.
271
     * @return int|bool The occurrence's last index if found, otherwise false.
272
     */
273
    public static function indexOfLast($str, $needle, $offset = 0)
274
    {
275
        return BaseStringy::create($str)->indexOfLast($needle, $offset);
276
    }
277
278
    /**
279
     * Inserts $substring into the string at the $index provided.
280
     *
281
     * @param string $str The string to insert into.
282
     * @param string $substring The string to be inserted.
283
     * @param int $index The 0-based index at which to insert the substring.
284
     * @return string The resulting string after the insertion
285
     */
286
    public static function insert(string $str, string $substring, int $index): string
287
    {
288
        return (string)BaseStringy::create($str)->insert($substring, $index);
289
    }
290
291
    /**
292
     * Returns true if the string contains only alphabetic chars, false otherwise.
293
     *
294
     * @param string $str The string to check.
295
     * @return bool Whether or not $str contains only alphabetic chars.
296
     */
297
    public static function isAlpha(string $str): bool
298
    {
299
        return BaseStringy::create($str)->isAlpha();
300
    }
301
302
    /**
303
     * Returns true if the string contains only alphabetic and numeric chars, false otherwise.
304
     *
305
     * @param string $str The string to check.
306
     * @return bool Whether or not $str contains only alphanumeric chars.
307
     */
308
    public static function isAlphanumeric(string $str): bool
309
    {
310
        return BaseStringy::create($str)->isAlphanumeric();
311
    }
312
313
    /**
314
     * Returns true if the string contains only whitespace chars, false otherwise.
315
     *
316
     * @param string $str The string to check.
317
     * @return bool Whether or not $str contains only whitespace characters.
318
     */
319
    public static function isWhitespace(string $str): bool
320
    {
321
        return BaseStringy::create($str)->isBlank();
322
    }
323
324
    /**
325
     * Returns true if the string contains only hexadecimal chars, false otherwise.
326
     *
327
     * @param string $str The string to check.
328
     * @return bool Whether or not $str contains only hexadecimal characters
329
     */
330
    public static function isHexadecimal(string $str): bool
331
    {
332
        return BaseStringy::create($str)->isHexadecimal();
333
    }
334
335
    /**
336
     * Returns true if the string contains only lowercase chars, false otherwise.
337
     *
338
     * @param string $str The string to check.
339
     * @return bool Whether or not $str contains only lowercase characters.
340
     */
341
    public static function isLowerCase(string $str): bool
342
    {
343
        return BaseStringy::create($str)->isLowerCase();
344
    }
345
346
    /**
347
     * Returns true if the string contains only uppercase chars, false otherwise.
348
     *
349
     * @param string $str The string to check.
350
     * @return bool Whether or not $str contains only uppercase characters.
351
     */
352
    public static function isUpperCase(string $str): bool
353
    {
354
        return BaseStringy::create($str)->isUpperCase();
355
    }
356
357
    /**
358
     * Returns is the given string matches a v4 UUID pattern.
359
     *
360
     * @param string $uuid The string to check.
361
     * @return bool Whether the string matches a v4 UUID pattern.
362
     */
363
    public static function isUUID(string $uuid): bool
364
    {
365
        return !empty($uuid) && preg_match('/[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}/ui', $uuid);
366
    }
367
368
    /**
369
     * Returns the last $number characters of the string.
370
     *
371
     * @param string $str The string from which to get the substring.
372
     * @param int $number The Number of chars to retrieve from the end.
373
     * @return string The last $number characters.
374
     */
375
    public static function last(string $str, int $number): string
376
    {
377
        return (string)BaseStringy::create($str)->last($number);
378
    }
379
380
    /**
381
     * Returns the length of the string. An alias for PHP's mb_strlen() function.
382
     *
383
     * @param string $str The string to get the length of.
384
     * @return int The number of characters in $str.
385
     */
386
    public static function length(string $str): int
387
    {
388
        return BaseStringy::create($str)->length();
389
    }
390
391
    /**
392
     * Splits on newlines and carriage returns, returning an array of strings corresponding to the lines in the string.
393
     *
394
     * @param string $str The string to split.
395
     * @return string[] An array of strings.
396
     */
397
    public static function lines(string $str): array
398
    {
399
        $lines = BaseStringy::create($str)->lines();
400
401
        foreach ($lines as $i => $line) {
402
            $lines[$i] = $line;
403
        }
404
405
        /** @var string[] $lines */
406
        return $lines;
407
    }
408
409
    /**
410
     * Converts the first character of the supplied string to lower case.
411
     *
412
     * @param string $str The string to modify.
413
     * @return string The string with the first character converted to lowercase.
414
     */
415
    public static function lowercaseFirst(string $str): string
416
    {
417
        return (string)BaseStringy::create($str)->lowerCaseFirst();
418
    }
419
420
    /**
421
     * kebab-cases a string.
422
     *
423
     * @param string $string The string
424
     * @param string $glue The string used to glue the words together (default is a hyphen)
425
     * @param bool $lower Whether the string should be lowercased (default is true)
426
     * @param bool $removePunctuation Whether punctuation marks should be removed (default is true)
427
     * @return string The kebab-cased string
428
     * @see toCamelCase()
429
     * @see toPascalCase()
430
     * @see toSnakeCase()
431
     */
432
    public static function toKebabCase(string $string, string $glue = '-', bool $lower = true, bool $removePunctuation = true): string
433
    {
434
        $words = self::_prepStringForCasing($string, $lower, $removePunctuation);
435
436
        return implode($glue, $words);
437
    }
438
439
    /**
440
     * camelCases a string.
441
     *
442
     * @param string $string The string
443
     * @return string
444
     * @see toKebabCase()
445
     * @see toPascalCase()
446
     * @see toSnakeCase()
447
     */
448
    public static function toCamelCase(string $string): string
449
    {
450
        $words = self::_prepStringForCasing($string);
451
452
        if (empty($words)) {
453
            return '';
454
        }
455
456
        $string = array_shift($words) . implode('', array_map([
457
                static::class,
458
                'upperCaseFirst'
459
            ], $words));
460
461
        return $string;
462
    }
463
464
    /**
465
     * PascalCases a string.
466
     *
467
     * @param string $string The string
468
     * @return string
469
     * @see toKebabCase()
470
     * @see toCamelCase()
471
     * @see toSnakeCase()
472
     */
473
    public static function toPascalCase(string $string): string
474
    {
475
        $words = self::_prepStringForCasing($string);
476
        $string = implode('', array_map([
477
            static::class,
478
            'upperCaseFirst'
479
        ], $words));
480
481
        return $string;
482
    }
483
484
    /**
485
     * snake_cases a string.
486
     *
487
     * @param string $string The string
488
     * @return string
489
     * @see toKebabCase()
490
     * @see toCamelCase()
491
     * @see toPascalCase()
492
     */
493
    public static function toSnakeCase(string $string): string
494
    {
495
        $words = self::_prepStringForCasing($string);
496
497
        return implode('_', $words);
498
    }
499
500
    /**
501
     * Splits a string into chunks on a given delimiter.
502
     *
503
     * @param string $string The string
504
     * @param string $delimiter The delimiter to split the string on (defaults to a comma)
505
     * @return string[] The segments of the string
506
     */
507
    public static function split(string $string, string $delimiter = ','): array
508
    {
509
        return preg_split('/\s*' . preg_quote($delimiter, '/') . '\s*/', $string, -1, PREG_SPLIT_NO_EMPTY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_split('/\s*'...rs\PREG_SPLIT_NO_EMPTY) could return the type false which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
510
    }
511
512
    /**
513
     * Splits a string into an array of the words in the string.
514
     *
515
     * @param string $string The string
516
     * @return string[] The words in the string
517
     */
518
    public static function splitOnWords(string $string): array
519
    {
520
        // Split on anything that is not alphanumeric, or a period, underscore, or hyphen.
521
        // Reference: http://www.regular-expressions.info/unicode.html
522
        preg_match_all('/[\p{L}\p{N}\p{M}\._-]+/u', $string, $matches);
523
        return ArrayHelper::filterEmptyStringsFromArray($matches[0]);
524
    }
525
526
    /**
527
     * Strips HTML tags out of a given string.
528
     *
529
     * @param string $str The string.
530
     * @return string The string, sans-HTML
531
     */
532
    public static function stripHtml(string $str): string
533
    {
534
        return preg_replace('/<(.*?)>/u', '', $str);
535
    }
536
537
    /**
538
     * Returns a new string of a given length such that both sides of the string are padded.
539
     *
540
     * @param  string $str The string to pad.
541
     * @param  int $length The desired string length after padding.
542
     * @param  string $padStr The string used to pad, defaults to space.
543
     * @return string The padded string.
544
     */
545
    public static function padBoth($str, $length, $padStr = ' '): string
546
    {
547
        return (string)BaseStringy::create($str)->padBoth($length, $padStr);
548
    }
549
550
    /**
551
     * Returns a new string of a given length such that the beginning of the string is padded.
552
     *
553
     * @param string $str The string to pad.
554
     * @param int $length The desired string length after padding.
555
     * @param string $padStr The string used to pad, defaults to space.
556
     * @return string The padded string.
557
     */
558
    public static function padLeft(string $str, int $length, string $padStr = ' '): string
559
    {
560
        return (string)BaseStringy::create($str)->padLeft($length, $padStr);
561
    }
562
563
    /**
564
     * Returns a new string of a given length such that the end of the string is padded.
565
     *
566
     * @param string $str The string to pad.
567
     * @param int $length The desired string length after padding.
568
     * @param string $padStr The string used to pad, defaults to space.
569
     * @return string The padded string.
570
     */
571
    public static function padRight(string $str, int $length, string $padStr = ' '): string
572
    {
573
        return (string)BaseStringy::create($str)->padRight($length, $padStr);
574
    }
575
576
    /**
577
     * Generates a random string of latin alphanumeric characters that defaults to a $length of 36. If $extendedChars is
578
     * set to true, additional symbols can be included in the string.  Note that the generated string is *not* a
579
     * cryptographically secure string.
580
     *
581
     * @param int $length The length of the random string. Defaults to 36.
582
     * @param bool $extendedChars Whether to include symbols in the random string.
583
     * @return string The randomly generated string.
584
     * @throws \Exception
585
     */
586
    public static function randomString(int $length = 36, bool $extendedChars = false): string
587
    {
588
        if ($extendedChars) {
589
            $validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()-_=+[]\{}|;:\'",./<>?"';
590
        } else {
591
            $validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
592
        }
593
594
        return static::randomStringWithChars($validChars, $length);
595
    }
596
597
    /**
598
     * Generates a random string of characters. Note that the generated string is *not* a
599
     * cryptographically secure string. If you need a cryptographically secure string, use
600
     * [[\craft\services\Security::generateRandomString()|<code>Craft::$app->security->generateRandomString()</code>]].
601
     *
602
     * @param string $validChars A string containing the valid characters
603
     * @param int $length The length of the random string
604
     * @return string The randomly generated string.
605
     * @throws \Exception
606
     */
607
    public static function randomStringWithChars(string $validChars, int $length): string
608
    {
609
        $randomString = '';
610
611
        // count the number of chars in the valid chars string so we know how many choices we have
612
        $numValidChars = static::length($validChars);
613
614
        // repeat the steps until we've created a string of the right length
615
        for ($i = 0; $i < $length; $i++) {
616
            // pick a random number from 1 up to the number of valid chars
617
            $randomPick = random_int(1, $numValidChars);
618
619
            // take the random character out of the string of valid chars
620
            $randomChar = $validChars[$randomPick - 1];
621
622
            // add the randomly-chosen char onto the end of our string
623
            $randomString .= $randomChar;
624
        }
625
626
        return $randomString;
627
    }
628
629
    /**
630
     * Replaces all occurrences of $pattern in $str by $replacement. An alias for mb_ereg_replace().
631
     *
632
     * @param string $str The haystack to search through.
633
     * @param string $pattern The regular expression pattern.
634
     * @param string $replacement The string to replace with.
635
     * @param string $options Matching conditions to be used. Defaults to 'msr'. See
636
     *                            [here](http://php.net/manual/en/function.mb-ereg-replace.php) for all options.
637
     * @return string The resulting string after the replacements.
638
     */
639
    public static function regexReplace(string $str, string $pattern, string $replacement, string $options = 'msr'): string
640
    {
641
        return (string)BaseStringy::create($str)->regexReplace($pattern, $replacement, $options);
642
    }
643
644
    /**
645
     * Returns a new string with the prefix $substring removed, if present.
646
     *
647
     * @param string $str The string from which to remove the prefix.
648
     * @param string $substring The prefix to remove.
649
     * @return string The string without the prefix $substring.
650
     */
651
    public static function removeLeft(string $str, string $substring): string
652
    {
653
        return (string)BaseStringy::create($str)->removeLeft($substring);
654
    }
655
656
    /**
657
     * Returns a new string with the suffix $substring removed, if present.
658
     *
659
     * @param string $str The string from which to remove the suffix.
660
     * @param string $substring The suffix to remove.
661
     * @return string The string without the suffix $substring.
662
     */
663
    public static function removeRight(string $str, string $substring): string
664
    {
665
        return (string)BaseStringy::create($str)->removeRight($substring);
666
    }
667
668
    /**
669
     * Replaces all occurrences of $search in $str by $replacement.
670
     *
671
     * @param string $str The haystack to search through.
672
     * @param string $search The needle to search for.
673
     * @param string $replacement The string to replace with.
674
     * @return string The resulting string after the replacements.
675
     */
676
    public static function replace(string $str, string $search, string $replacement): string
677
    {
678
        return (string)BaseStringy::create($str)->replace($search, $replacement);
679
    }
680
681
    /**
682
     * Returns a reversed string. A multibyte version of strrev().
683
     *
684
     * @param string $str The string to reverse.
685
     * @return string The reversed string.
686
     */
687
    public static function reverse(string $str): string
688
    {
689
        return (string)BaseStringy::create($str)->reverse();
690
    }
691
692
    /**
693
     * Truncates the string to a given length, while ensuring that it does not split words. If $substring is provided,
694
     * and truncating occurs, the string is further truncated so that the substring may be appended without exceeding t
695
     * he desired length.
696
     *
697
     * @param string $str The string to truncate.
698
     * @param int $length The desired length of the truncated string.
699
     * @param string $substring The substring to append if it can fit.
700
     * @return string The resulting string after truncating.
701
     */
702
    public static function safeTruncate(string $str, int $length, string $substring = ''): string
703
    {
704
        return (string)BaseStringy::create($str)->safeTruncate($length, $substring);
705
    }
706
707
    /**
708
     * Returns true if the string begins with $substring, false otherwise. By default, the comparison is case-sensitive,
709
     * but can be made insensitive by setting $caseSensitive to false.
710
     *
711
     * @param string $str The string to check the start of.
712
     * @param string $substring The substring to look for.
713
     * @param bool $caseSensitive Whether or not to enforce case-sensitivity.
714
     * @return bool Whether or not $str starts with $substring.
715
     */
716
    public static function startsWith($str, $substring, $caseSensitive = true): bool
717
    {
718
        return BaseStringy::create($str)->startsWith($substring, $caseSensitive);
719
    }
720
721
    /**
722
     * Returns the substring beginning at $start with the specified|null $length. It differs from the mb_substr() function in
723
     * that providing a|null $length of null will return the rest of the string, rather than an empty string.
724
     *
725
     * @param string $str The string to get the length of.
726
     * @param int $start Position of the first character to use.
727
     * @param int|null $length Maximum number of characters used.
728
     * @return string The substring of $str.
729
     */
730
    public static function substr(string $str, int $start, int $length = null): string
731
    {
732
        return (string)BaseStringy::create($str)->substr($start, $length);
733
    }
734
735
    /**
736
     * Returns a case swapped version of the string.
737
     *
738
     * @param string $str The string to swap case.
739
     * @return string The string with each character's case swapped.
740
     */
741
    public static function swapCase(string $str): string
742
    {
743
        return (string)BaseStringy::create($str)->swapCase();
744
    }
745
746
    /**
747
     * Returns a trimmed string with the first letter of each word capitalized. Ignores the case of other letters,
748
     * preserving any acronyms. Also accepts an array, $ignore, allowing you to list words not to be capitalized.
749
     *
750
     * @param string $str The string to titleize.
751
     * @param array|null $ignore An array of words not to capitalize.
752
     * @return string The titleized string.
753
     */
754
    public static function titleize(string $str, array $ignore = null): string
755
    {
756
        return (string)BaseStringy::create($str)->titleize($ignore);
757
    }
758
759
    /**
760
     * Converts all characters in the string to lowercase. An alias for PHP's mb_strtolower().
761
     *
762
     * @param string $str The string to convert to lowercase.
763
     * @return string The lowercase string.
764
     */
765
    public static function toLowerCase(string $str): string
766
    {
767
        return (string)BaseStringy::create($str)->toLowerCase();
768
    }
769
770
    /**
771
     * Converts an object to its string representation. If the object is an array, will glue the array elements togeter
772
     * with the $glue param. Otherwise will cast the object to a string.
773
     *
774
     * @param mixed $object The object to convert to a string.
775
     * @param string $glue The glue to use if the object is an array.
776
     * @return string The string representation of the object.
777
     */
778
    public static function toString($object, string $glue = ','): string
779
    {
780
        if (is_scalar($object) || (is_object($object) && method_exists($object, '__toString'))) {
781
            return (string)$object;
782
        }
783
784
        if (is_array($object) || $object instanceof \IteratorAggregate) {
785
            $stringValues = [];
786
787
            foreach ($object as $value) {
788
                if (($value = static::toString($value, $glue)) !== '') {
789
                    $stringValues[] = $value;
790
                }
791
            }
792
793
            return implode($glue, $stringValues);
794
        }
795
796
        return '';
797
    }
798
799
    /**
800
     * Converts the first character of each word in the string to uppercase.
801
     *
802
     * @param string $str The string to convert case.
803
     * @return string The title-cased string.
804
     */
805
    public static function toTitleCase(string $str): string
806
    {
807
        return (string)BaseStringy::create($str)->toTitleCase();
808
    }
809
810
    /**
811
     * Converts all characters in the string to uppercase. An alias for PHP's mb_strtoupper().
812
     *
813
     * @param string $str The string to convert to uppercase.
814
     * @return string The uppercase string.
815
     */
816
    public static function toUpperCase(string $str): string
817
    {
818
        return (string)BaseStringy::create($str)->toUpperCase();
819
    }
820
821
    /**
822
     * Returns the trimmed string. An alias for PHP's trim() function.
823
     *
824
     * @param string $str The string to trim.
825
     * @return string The trimmed $str.
826
     */
827
    public static function trim(string $str): string
828
    {
829
        return (string)BaseStringy::create($str)->trim();
830
    }
831
832
    /**
833
     * Converts the first character of the supplied string to uppercase.
834
     *
835
     * @param string $str The string to modify.
836
     * @return string The string with the first character being uppercase.
837
     */
838
    public static function upperCaseFirst(string $str): string
839
    {
840
        return (string)BaseStringy::create($str)->upperCaseFirst();
841
    }
842
843
    /**
844
     * Generates a valid v4 UUID string. See [http://stackoverflow.com/a/2040279/684]
845
     *
846
     * @return string The UUID.
847
     * @throws \Exception
848
     */
849
    public static function UUID(): string
850
    {
851
//        $data = Yii::$app->security->generateRandomKey(16);
852
//        assert(strlen($data) == 16);
853
//        $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
854
//        $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
855
//        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
856
857
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
858
859
            // 32 bits for "time_low"
860
            random_int(0, 0xffff), random_int(0, 0xffff),
861
862
            // 16 bits for "time_mid"
863
            random_int(0, 0xffff),
864
865
            // 16 bits for "time_hi_and_version", four most significant bits holds version number 4
866
            random_int(0, 0x0fff) | 0x4000,
867
868
            // 16 bits, 8 bits for "clk_seq_hi_res", 8 bits for "clk_seq_low", two most significant bits holds zero and
869
            // one for variant DCE1.1
870
            random_int(0, 0x3fff) | 0x8000,
871
872
            // 48 bits for "node"
873
            random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
874
        );
875
    }
876
877
    /**
878
     * Returns ASCII character mappings, merging in any custom defined mappings from the 'customAsciiCharMappings'
879
     * config setting.
880
     *
881
     * @return array The fully merged ASCII character mappings.
882
     */
883
    public static function asciiCharMap(): array
884
    {
885
        if (self::$_asciiCharMap !== null) {
886
            return self::$_asciiCharMap;
887
        }
888
889
        // Get the map from Stringy.
890
        self::$_asciiCharMap = (new Stringy(''))->getAsciiCharMap();
891
892
        return self::$_asciiCharMap;
893
    }
894
895
    /**
896
     * Returns an ASCII version of the string. A set of non-ASCII characters are replaced with their closest ASCII
897
     * counterparts, and the rest are removed.
898
     *
899
     * @param string $str The string to convert.
900
     * @return string The string that contains only ASCII characters.
901
     */
902
    public static function toAscii(string $str): string
903
    {
904
        return (string)BaseStringy::create($str)->toAscii();
905
    }
906
907
    /**
908
     * Attempts to convert a string to UTF-8 and clean any non-valid UTF-8 characters.
909
     *
910
     * @param string $string
911
     * @return string
912
     */
913
    public static function convertToUtf8(string $string): string
914
    {
915
        // If it's already a UTF8 string, just clean and return it
916
        if (static::isUtf8($string)) {
917
            return HtmlPurifier::cleanUtf8($string);
918
        }
919
920
        // Otherwise set HTMLPurifier to the actual string encoding
921
        $config = \HTMLPurifier_Config::createDefault();
922
        $config->set('Core.Encoding', static::encoding($string));
923
924
        // Clean it
925
        $string = HtmlPurifier::cleanUtf8($string);
926
927
        // Convert it to UTF8 if possible
928
        if (function_exists('iconv') && \HTMLPurifier_Encoder::testIconvTruncateBug() === \HTMLPurifier_Encoder::ICONV_OK) {
929
            $string = HtmlPurifier::convertToUtf8($string, $config);
930
        } else {
931
            $encoding = static::encoding($string);
932
            $string = mb_convert_encoding($string, 'utf-8', $encoding);
933
        }
934
935
        return $string;
936
    }
937
938
    /**
939
     * Checks if the given string is UTF-8 encoded.
940
     *
941
     * @param string $string The string to check.
942
     * @return bool
943
     */
944
    public static function isUtf8(string $string): bool
945
    {
946
        return static::encoding($string) === 'utf-8';
947
    }
948
949
    /**
950
     * Gets the current encoding of the given string.
951
     *
952
     * @param string $string
953
     * @return string
954
     */
955
    public static function encoding(string $string): string
956
    {
957
        return static::toLowerCase(mb_detect_encoding($string, mb_detect_order(), true));
958
    }
959
960
    /**
961
     * HTML-encodes any 4-byte UTF-8 characters.
962
     *
963
     * @param string $string The string
964
     * @return string The string with converted 4-byte UTF-8 characters
965
     * @see http://stackoverflow.com/a/16496730/1688568
966
     */
967
    public static function encodeMb4(string $string): string
968
    {
969
        // Does this string have any 4+ byte Unicode chars?
970
        if (max(array_map('ord', str_split($string))) >= 240) {
971
            $string = preg_replace_callback('/./u', function (array $match) {
972
                if (strlen($match[0]) >= 4) {
973
                    // (Logic pulled from WP's wp_encode_emoji() function)
974
                    // UTF-32's hex encoding is the same as HTML's hex encoding.
975
                    // So, by converting from UTF-8 to UTF-32, we magically
976
                    // get the correct hex encoding.
977
                    $unpacked = unpack('H*', mb_convert_encoding($match[0], 'UTF-32', 'UTF-8'));
978
979
                    return isset($unpacked[1]) ? '&#x' . ltrim($unpacked[1], '0') . ';' : '';
980
                }
981
982
                return $match[0];
983
            }, $string);
984
        }
985
986
        return $string;
987
    }
988
989
    /**
990
     * Prepares a string for casing routines.
991
     *
992
     * @param string $string The string
993
     * @param bool $lower
994
     * @param bool $removePunctuation Whether punctuation marks should be removed (default is true)
995
     *
996
     * @return string[] The prepped words in the string
997
     *
998
     * @see toKebabCase()
999
     * @see toCamelCase()
1000
     * @see toPascalCase()
1001
     * @see toSnakeCase()
1002
     */
1003
    private static function _prepStringForCasing(string $string, bool $lower = true, bool $removePunctuation = true): array
1004
    {
1005
        if ($lower) {
1006
            // Make it lowercase
1007
            $string = static::toLowerCase($string);
1008
        }
1009
        if ($removePunctuation) {
1010
            $string = str_replace(['.', '_', '-'], ' ', $string);
1011
        }
1012
        // Remove inner-word punctuation.
1013
        $string = preg_replace('/[\'"‘’“”\[\]\(\)\{\}:]/u', '', $string);
1014
        // Split on the words and return
1015
        return static::splitOnWords($string);
1016
    }
1017
}