Completed
Pull Request — master (#14)
by
unknown
05:56
created

Stringy   F

Complexity

Total Complexity 167

Size/Duplication

Total Lines 3111
Duplicated Lines 3.28 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 97.91%

Importance

Changes 0
Metric Value
dl 102
loc 3111
ccs 562
cts 574
cp 0.9791
rs 0.8
c 0
b 0
f 0
wmc 167
lcom 1
cbo 4

133 Methods

Rating   Name   Duplication   Size   Complexity  
A append() 0 4 1
A chars() 0 4 1
A getIterator() 0 4 1
A hasLowerCase() 0 4 1
A isBlank() 0 4 1
A isHtml() 0 4 1
A longestCommonPrefix() 0 11 1
A longestCommonSubstring() 0 11 1
A prepend() 0 4 1
A repeat() 0 7 1
A reverse() 0 4 1
A tidy() 0 7 1
A trim() 0 7 1
B __construct() 0 32 6
A __toString() 0 4 1
A afterFirst() 0 10 1
A afterFirstIgnoreCase() 0 10 1
A afterLast() 0 10 1
A afterLastIgnoreCase() 0 10 1
A appendPassword() 0 7 1
A appendRandomString() 0 6 1
A appendUniqueIdentifier() 0 6 1
A at() 0 4 1
A beforeFirst() 0 10 1
A beforeFirstIgnoreCase() 0 10 1
A beforeLast() 0 10 1
A beforeLastIgnoreCase() 0 10 1
A between() 0 13 1
A camelize() 0 7 1
A capitalizePersonalName() 0 7 1
A collapseWhitespace() 0 7 1
A contains() 0 8 1
A containsAll() 0 8 1
A containsAny() 0 8 1
A count() 0 4 1
A countSubstr() 0 9 1
A create() 0 4 1
A dasherize() 0 7 1
A delimit() 0 7 1
A endsWith() 0 8 2
A endsWithAny() 0 8 2
A ensureLeft() 0 7 1
A ensureRight() 0 7 1
A escape() 0 11 1
A extractText() 0 13 1
A first() 0 7 1
A getEncoding() 0 4 1
A hasUpperCase() 0 4 1
A htmlDecode() 11 11 1
A htmlEncode() 11 11 1
A humanize() 0 7 1
A indexOf() 0 9 1
A indexOfIgnoreCase() 0 9 1
A indexOfLast() 0 9 1
A indexOfLastIgnoreCase() 0 9 1
A insert() 0 12 1
A is() 0 11 2
A isAlpha() 0 4 1
A isAlphanumeric() 0 4 1
A isBase64() 0 4 1
A isEmail() 0 4 1
A isEmpty() 0 4 1
A isHexadecimal() 0 4 1
A isJson() 0 4 1
A isLowerCase() 0 4 1
A isSerialized() 0 4 1
A isUpperCase() 0 4 1
A last() 0 11 1
A lastSubstringOf() 7 7 1
A lastSubstringOfIgnoreCase() 7 7 1
A length() 0 4 1
A lineWrapAfterWord() 0 7 1
A lines() 0 9 2
A longestCommonSuffix() 0 11 1
A lowerCaseFirst() 0 7 1
A offsetExists() 0 8 1
A offsetGet() 0 4 1
A offsetSet() 0 6 1
A offsetUnset() 0 6 1
A pad() 0 12 1
A padBoth() 0 11 1
A padLeft() 0 11 1
A padRight() 0 11 1
A regexReplace() 0 13 1
A removeHtml() 0 7 1
A removeHtmlBreak() 0 7 1
A removeLeft() 0 7 1
A removeRight() 0 7 1
A removeXss() 0 12 2
B replace() 0 22 6
A replaceAll() 0 14 2
A replaceFirst() 0 7 1
A replaceLast() 0 7 1
A replaceBeginning() 0 7 1
A replaceEnding() 0 7 1
A safeTruncate() 0 13 1
A shortenAfterWord() 0 7 1
A shuffle() 0 4 1
A slice() 0 7 1
B slugify() 6 42 5
A urlify() 0 7 1
A snakeize() 0 7 1
A split() 0 13 3
A startsWith() 0 8 2
A startsWithAny() 0 8 2
A stripWhitespace() 0 7 1
A stripeCssMediaQueries() 0 7 1
A stripeEmptyHtmlTags() 0 7 1
A substr() 0 12 1
A substringOf() 7 7 1
A substringOfIgnoreCase() 7 7 1
A surround() 0 7 1
A swapCase() 0 7 1
A titleize() 0 7 1
A titleizeForHumans() 0 7 1
A toTransliterate() 0 7 1
A toAscii() 0 20 4
A toBoolean() 0 4 1
A toLowerCase() 9 9 1
A toSpaces() 15 15 3
A toString() 0 4 1
A toTabs() 15 15 3
A toTitleCase() 0 7 1
A toUpperCase() 7 7 1
A trimLeft() 0 7 1
A trimRight() 0 7 1
A truncate() 0 7 1
A underscored() 0 4 1
A upperCamelize() 0 7 1
A upperCaseFirst() 0 4 1
A utf8ify() 0 4 1
A matchesPattern() 0 4 1
A langSpecificCharsArray() 0 25 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Stringy often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Stringy, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stringy;
6
7
use voku\helper\AntiXSS;
8
use voku\helper\EmailCheck;
9
use voku\helper\URLify;
10
use voku\helper\UTF8;
11
12
/**
13
 * Class Stringy
14
 */
15
class Stringy implements \Countable, \IteratorAggregate, \ArrayAccess
16
{
17
    /**
18
     * An instance's string.
19
     *
20
     * @var string
21
     */
22
    protected $str;
23
24
    /**
25
     * The string's encoding, which should be one of the mbstring module's
26
     * supported encodings.
27
     *
28
     * @var string
29
     */
30
    protected $encoding;
31
32
    /**
33
     * @var UTF8
34
     */
35
    private $utf8;
36
37
    /**
38
     * Initializes a Stringy object and assigns both str and encoding properties
39
     * the supplied values. $str is cast to a string prior to assignment, and if
40
     * $encoding is not specified, it defaults to mb_internal_encoding(). Throws
41
     * an InvalidArgumentException if the first argument is an array or object
42
     * without a __toString method.
43
     *
44
     * @param mixed  $str      [optional] <p>Value to modify, after being cast to string. Default: ''</p>
45
     * @param string $encoding [optional] <p>The character encoding. Fallback: 'UTF-8'</p>
46
     *
47
     * @throws \InvalidArgumentException <p>if an array or object without a
48
     *                                   __toString method is passed as the first argument</p>
49
     */
50 2105
    public function __construct($str = '', string $encoding = null)
51
    {
52 2105
        if (\is_array($str)) {
53 2
            throw new \InvalidArgumentException(
54 2
                'Passed value cannot be an array'
55
            );
56
        }
57
58
        if (
59 2103
            \is_object($str)
60
            &&
61 2103
            !\method_exists($str, '__toString')
62
        ) {
63 2
            throw new \InvalidArgumentException(
64 2
                'Passed object must have a __toString method'
65
            );
66
        }
67
68 2101
        $this->str = (string) $str;
69
70 2101
        static $UTF8 = null;
71 2101
        if ($UTF8 === null) {
72
            $UTF8 = new UTF8();
73
        }
74 2101
        $this->utf8 = $UTF8;
75
76 2101
        if ($encoding !== 'UTF-8') {
77 1350
            $this->encoding = $this->utf8::normalize_encoding($encoding, 'UTF-8');
78
        } else {
79 1645
            $this->encoding = $encoding;
80
        }
81 2101
    }
82
83
    /**
84
     * Returns the value in $str.
85
     *
86
     * @return string <p>The current value of the $str property.</p>
87
     */
88 978
    public function __toString()
89
    {
90 978
        return (string) $this->str;
91
    }
92
93
    /**
94
     * Gets the substring after the first occurrence of a separator.
95
     * If no match is found returns new empty Stringy object.
96
     *
97
     * @param string $separator
98
     *
99
     * @return static
100
     */
101 2
    public function afterFirst(string $separator): self
102
    {
103 2
        return static::create(
104 2
            $this->utf8::str_substr_after_first_separator(
105 2
                $this->str,
106
                $separator,
107 2
                $this->encoding
108
            )
109
        );
110
    }
111
112
    /**
113
     * Gets the substring after the first occurrence of a separator.
114
     * If no match is found returns new empty Stringy object.
115
     *
116
     * @param string $separator
117
     *
118
     * @return static
119
     */
120 1
    public function afterFirstIgnoreCase(string $separator): self
121
    {
122 1
        return static::create(
123 1
            $this->utf8::str_isubstr_after_first_separator(
124 1
                $this->str,
125
                $separator,
126 1
                $this->encoding
127
            )
128
        );
129
    }
130
131
    /**
132
     * Gets the substring after the last occurrence of a separator.
133
     * If no match is found returns new empty Stringy object.
134
     *
135
     * @param string $separator
136
     *
137
     * @return static
138
     */
139 1
    public function afterLast(string $separator): self
140
    {
141 1
        return static::create(
142 1
            $this->utf8::str_substr_after_last_separator(
143 1
                $this->str,
144
                $separator,
145 1
                $this->encoding
146
            )
147
        );
148
    }
149
150
    /**
151
     * Gets the substring after the last occurrence of a separator.
152
     * If no match is found returns new empty Stringy object.
153
     *
154
     * @param string $separator
155
     *
156
     * @return static
157
     */
158 1
    public function afterLastIgnoreCase(string $separator): self
159
    {
160 1
        return static::create(
161 1
            $this->utf8::str_isubstr_after_last_separator(
162 1
                $this->str,
163
                $separator,
164 1
                $this->encoding
165
            )
166
        );
167
    }
168
169
    /**
170
     * Returns a new string with $string appended.
171
     *
172
     * @param string $string <p>The string to append.</p>
173
     *
174
     * @return static <p>Object with appended $string.</p>
175
     */
176 7
    public function append(string $string): self
177
    {
178 7
        return static::create($this->str . $string, $this->encoding);
179
    }
180
181
    /**
182
     * Append an password (limited to chars that are good readable).
183
     *
184
     * @param int $length <p>Length of the random string.</p>
185
     *
186
     * @return static <p>Object with appended password.</p>
187
     */
188 1
    public function appendPassword(int $length): self
189
    {
190 1
        return $this->appendRandomString(
191 1
            $length,
192 1
            '2346789bcdfghjkmnpqrtvwxyzBCDFGHJKLMNPQRTVWXYZ!?_#'
193
        );
194
    }
195
196
    /**
197
     * Append an random string.
198
     *
199
     * @param int    $length        <p>Length of the random string.</p>
200
     * @param string $possibleChars [optional] <p>Characters string for the random selection.</p>
201
     *
202
     * @return static <p>Object with appended random string.</p>
203
     */
204 2
    public function appendRandomString(int $length, string $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): self
205
    {
206 2
        $str = $this->utf8::get_random_string($length, $possibleChars);
207
208 2
        return $this->append($str);
209
    }
210
211
    /**
212
     * Append an unique identifier.
213
     *
214
     * @param int|string $entropyExtra [optional] <p>Extra entropy via a string or int value.</p>
215
     * @param bool       $md5          [optional] <p>Return the unique identifier as md5-hash? Default: true</p>
216
     *
217
     * @return static <p>Object with appended unique identifier as md5-hash.</p>
218
     */
219 1
    public function appendUniqueIdentifier($entropyExtra = '', bool $md5 = true): self
220
    {
221 1
        return $this->append(
222 1
            $this->utf8::get_unique_string($entropyExtra, $md5)
223
        );
224
    }
225
226
    /**
227
     * Returns the character at $index, with indexes starting at 0.
228
     *
229
     * @param int $index <p>Position of the character.</p>
230
     *
231
     * @return static <p>The character at $index.</p>
232
     */
233 16
    public function at(int $index): self
234
    {
235 16
        return static::create($this->utf8::char_at($this->str, $index), $this->encoding);
236
    }
237
238
    /**
239
     * Gets the substring before the first occurrence of a separator.
240
     * If no match is found returns new empty Stringy object.
241
     *
242
     * @param string $separator
243
     *
244
     * @return static
245
     */
246 1
    public function beforeFirst(string $separator): self
247
    {
248 1
        return static::create(
249 1
            $this->utf8::str_substr_before_first_separator(
250 1
                $this->str,
251
                $separator,
252 1
                $this->encoding
253
            )
254
        );
255
    }
256
257
    /**
258
     * Gets the substring before the first occurrence of a separator.
259
     * If no match is found returns new empty Stringy object.
260
     *
261
     * @param string $separator
262
     *
263
     * @return static
264
     */
265 1
    public function beforeFirstIgnoreCase(string $separator): self
266
    {
267 1
        return static::create(
268 1
            $this->utf8::str_isubstr_before_first_separator(
269 1
                $this->str,
270
                $separator,
271 1
                $this->encoding
272
            )
273
        );
274
    }
275
276
    /**
277
     * Gets the substring before the last occurrence of a separator.
278
     * If no match is found returns new empty Stringy object.
279
     *
280
     * @param string $separator
281
     *
282
     * @return static
283
     */
284 1
    public function beforeLast(string $separator): self
285
    {
286 1
        return static::create(
287 1
            $this->utf8::str_substr_before_last_separator(
288 1
                $this->str,
289
                $separator,
290 1
                $this->encoding
291
            )
292
        );
293
    }
294
295
    /**
296
     * Gets the substring before the last occurrence of a separator.
297
     * If no match is found returns new empty Stringy object.
298
     *
299
     * @param string $separator
300
     *
301
     * @return static
302
     */
303 1
    public function beforeLastIgnoreCase(string $separator): self
304
    {
305 1
        return static::create(
306 1
            $this->utf8::str_isubstr_before_last_separator(
307 1
                $this->str,
308
                $separator,
309 1
                $this->encoding
310
            )
311
        );
312
    }
313
314
    /**
315
     * Returns the substring between $start and $end, if found, or an empty
316
     * string. An optional offset may be supplied from which to begin the
317
     * search for the start string.
318
     *
319
     * @param string $start  <p>Delimiter marking the start of the substring.</p>
320
     * @param string $end    <p>Delimiter marking the end of the substring.</p>
321
     * @param int    $offset [optional] <p>Index from which to begin the search. Default: 0</p>
322
     *
323
     * @return static <p>Object whose $str is a substring between $start and $end.</p>
324
     */
325 32
    public function between(string $start, string $end, int $offset = null): self
326
    {
327
        /** @noinspection UnnecessaryCastingInspection */
328 32
        $str = $this->utf8::between(
329 32
            $this->str,
330
            $start,
331
            $end,
332 32
            (int) $offset,
333 32
            $this->encoding
334
        );
335
336 32
        return static::create($str, $this->encoding);
337
    }
338
339
    /**
340
     * Returns a camelCase version of the string. Trims surrounding spaces,
341
     * capitalizes letters following digits, spaces, dashes and underscores,
342
     * and removes spaces, dashes, as well as underscores.
343
     *
344
     * @return static <p>Object with $str in camelCase.</p>
345
     */
346 38
    public function camelize(): self
347
    {
348 38
        return static::create(
349 38
            $this->utf8::str_camelize($this->str, $this->encoding),
350 38
            $this->encoding
351
        );
352
    }
353
354
    /**
355
     * Returns the string with the first letter of each word capitalized,
356
     * except for when the word is a name which shouldn't be capitalized.
357
     *
358
     * @return static <p>Object with $str capitalized.</p>
359
     */
360 39
    public function capitalizePersonalName(): self
361
    {
362 39
        return static::create(
363 39
            $this->utf8::str_capitalize_name($this->str),
364 39
            $this->encoding
365
        );
366
    }
367
368
    /**
369
     * Returns an array consisting of the characters in the string.
370
     *
371
     * @return array <p>An array of string chars.</p>
372
     */
373 8
    public function chars(): array
374
    {
375 8
        return $this->utf8::str_split($this->str);
376
    }
377
378
    /**
379
     * Trims the string and replaces consecutive whitespace characters with a
380
     * single space. This includes tabs and newline characters, as well as
381
     * multibyte whitespace such as the thin space and ideographic space.
382
     *
383
     * @return static <p>Object with a trimmed $str and condensed whitespace.</p>
384
     */
385 26
    public function collapseWhitespace(): self
386
    {
387 26
        return static::create(
388 26
            $this->utf8::collapse_whitespace($this->str),
389 26
            $this->encoding
390
        );
391
    }
392
393
    /**
394
     * Returns true if the string contains $needle, false otherwise. By default
395
     * the comparison is case-sensitive, but can be made insensitive by setting
396
     * $caseSensitive to false.
397
     *
398
     * @param string $needle        <p>Substring to look for.</p>
399
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
400
     *
401
     * @return bool <p>Whether or not $str contains $needle.</p>
402
     */
403 42
    public function contains(string $needle, bool $caseSensitive = true): bool
404
    {
405 42
        return $this->utf8::str_contains(
406 42
            $this->str,
407
            $needle,
408
            $caseSensitive
409
        );
410
    }
411
412
    /**
413
     * Returns true if the string contains all $needles, false otherwise. By
414
     * default the comparison is case-sensitive, but can be made insensitive by
415
     * setting $caseSensitive to false.
416
     *
417
     * @param array $needles       <p>SubStrings to look for.</p>
418
     * @param bool  $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
419
     *
420
     * @return bool <p>Whether or not $str contains $needle.</p>
421
     */
422 87
    public function containsAll(array $needles, bool $caseSensitive = true): bool
423
    {
424 87
        return $this->utf8::str_contains_all(
425 87
            $this->str,
426
            $needles,
427
            $caseSensitive
428
        );
429
    }
430
431
    /**
432
     * Returns true if the string contains any $needles, false otherwise. By
433
     * default the comparison is case-sensitive, but can be made insensitive by
434
     * setting $caseSensitive to false.
435
     *
436
     * @param array $needles       <p>SubStrings to look for.</p>
437
     * @param bool  $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
438
     *
439
     * @return bool <p>Whether or not $str contains $needle.</p>
440
     */
441 86
    public function containsAny(array $needles, bool $caseSensitive = true): bool
442
    {
443 86
        return $this->utf8::str_contains_any(
444 86
            $this->str,
445
            $needles,
446
            $caseSensitive
447
        );
448
    }
449
450
    /**
451
     * Returns the length of the string, implementing the countable interface.
452
     *
453
     * @return int <p>The number of characters in the string, given the encoding.</p>
454
     */
455 2
    public function count(): int
456
    {
457 2
        return $this->length();
458
    }
459
460
    /**
461
     * Returns the number of occurrences of $substring in the given string.
462
     * By default, the comparison is case-sensitive, but can be made insensitive
463
     * by setting $caseSensitive to false.
464
     *
465
     * @param string $substring     <p>The substring to search for.</p>
466
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
467
     *
468
     * @return int
469
     */
470 30
    public function countSubstr(string $substring, bool $caseSensitive = true): int
471
    {
472 30
        return $this->utf8::substr_count_simple(
473 30
            $this->str,
474
            $substring,
475
            $caseSensitive,
476 30
            $this->encoding
477
        );
478
    }
479
480
    /**
481
     * Creates a Stringy object and assigns both str and encoding properties
482
     * the supplied values. $str is cast to a string prior to assignment, and if
483
     * $encoding is not specified, it defaults to mb_internal_encoding(). It
484
     * then returns the initialized object. Throws an InvalidArgumentException
485
     * if the first argument is an array or object without a __toString method.
486
     *
487
     * @param mixed  $str      [optional] <p>Value to modify, after being cast to string. Default: ''</p>
488
     * @param string $encoding [optional] <p>The character encoding. Fallback: 'UTF-8'</p>
489
     *
490
     * @throws \InvalidArgumentException <p>if an array or object without a
491
     *                                   __toString method is passed as the first argument</p>
492
     *
493
     * @return static <p>A Stringy object.</p>
494
     */
495 2085
    public static function create($str = '', string $encoding = null): self
496
    {
497 2085
        return new static($str, $encoding);
498
    }
499
500
    /**
501
     * Returns a lowercase and trimmed string separated by dashes. Dashes are
502
     * inserted before uppercase characters (with the exception of the first
503
     * character of the string), and in place of spaces as well as underscores.
504
     *
505
     * @return static <p>Object with a dasherized $str</p>
506
     */
507 38
    public function dasherize(): self
508
    {
509 38
        return static::create(
510 38
            $this->utf8::str_dasherize($this->str),
511 38
            $this->encoding
512
        );
513
    }
514
515
    /**
516
     * Returns a lowercase and trimmed string separated by the given delimiter.
517
     * Delimiters are inserted before uppercase characters (with the exception
518
     * of the first character of the string), and in place of spaces, dashes,
519
     * and underscores. Alpha delimiters are not converted to lowercase.
520
     *
521
     * @param string $delimiter <p>Sequence used to separate parts of the string.</p>
522
     *
523
     * @return static <p>Object with a delimited $str.</p>
524
     */
525 60
    public function delimit(string $delimiter): self
526
    {
527 60
        return static::create(
528 60
            $this->utf8::str_delimit($this->str, $delimiter),
529 60
            $this->encoding
530
        );
531
    }
532
533
    /**
534
     * Returns true if the string ends with $substring, false otherwise. By
535
     * default, the comparison is case-sensitive, but can be made insensitive
536
     * by setting $caseSensitive to false.
537
     *
538
     * @param string $substring     <p>The substring to look for.</p>
539
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
540
     *
541
     * @return bool <p>Whether or not $str ends with $substring.</p>
542
     */
543 22
    public function endsWith(string $substring, bool $caseSensitive = true): bool
544
    {
545 22
        if ($caseSensitive) {
546 14
            return $this->utf8::str_ends_with($this->str, $substring);
547
        }
548
549 8
        return $this->utf8::str_iends_with($this->str, $substring);
550
    }
551
552
    /**
553
     * Returns true if the string ends with any of $substrings, false otherwise.
554
     * By default, the comparison is case-sensitive, but can be made insensitive
555
     * by setting $caseSensitive to false.
556
     *
557
     * @param string[] $substrings    <p>Substrings to look for.</p>
558
     * @param bool     $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
559
     *
560
     * @return bool <p>Whether or not $str ends with $substring.</p>
561
     */
562 22
    public function endsWithAny(array $substrings, bool $caseSensitive = true): bool
563
    {
564 22
        if ($caseSensitive) {
565 14
            return $this->utf8::str_ends_with_any($this->str, $substrings);
566
        }
567
568 8
        return $this->utf8::str_iends_with_any($this->str, $substrings);
569
    }
570
571
    /**
572
     * Ensures that the string begins with $substring. If it doesn't, it's
573
     * prepended.
574
     *
575
     * @param string $substring <p>The substring to add if not present.</p>
576
     *
577
     * @return static <p>Object with its $str prefixed by the $substring.</p>
578
     */
579 20
    public function ensureLeft(string $substring): self
580
    {
581 20
        return static::create(
582 20
            $this->utf8::str_ensure_left($this->str, $substring),
583 20
            $this->encoding
584
        );
585
    }
586
587
    /**
588
     * Ensures that the string ends with $substring. If it doesn't, it's appended.
589
     *
590
     * @param string $substring <p>The substring to add if not present.</p>
591
     *
592
     * @return static <p>Object with its $str suffixed by the $substring.</p>
593
     */
594 20
    public function ensureRight(string $substring): self
595
    {
596 20
        return static::create(
597 20
            $this->utf8::str_ensure_right($this->str, $substring),
598 20
            $this->encoding
599
        );
600
    }
601
602
    /**
603
     * Create a escape html version of the string via "$this->utf8::htmlspecialchars()".
604
     *
605
     * @return static
606
     */
607 6
    public function escape(): self
608
    {
609 6
        return static::create(
610 6
            $this->utf8::htmlspecialchars(
611 6
                $this->str,
612 6
                \ENT_QUOTES | \ENT_SUBSTITUTE,
613 6
                $this->encoding
614
            ),
615 6
            $this->encoding
616
        );
617
    }
618
619
    /**
620
     * Create an extract from a sentence, so if the search-string was found, it try to centered in the output.
621
     *
622
     * @param string   $search
623
     * @param int|null $length                 [optional] <p>Default: null === text->length / 2</p>
624
     * @param string   $replacerForSkippedText [optional] <p>Default: …</p>
625
     *
626
     * @return static
627
     */
628 1
    public function extractText(string $search = '', int $length = null, string $replacerForSkippedText = '…'): self
629
    {
630 1
        return static::create(
631 1
            $this->utf8::extract_text(
632 1
                $this->str,
633
                $search,
634
                $length,
635
                $replacerForSkippedText,
636 1
                $this->encoding
637
            ),
638 1
            $this->encoding
639
        );
640
    }
641
642
    /**
643
     * Returns the first $n characters of the string.
644
     *
645
     * @param int $n <p>Number of characters to retrieve from the start.</p>
646
     *
647
     * @return static <p>Object with its $str being the first $n chars.</p>
648
     */
649 25
    public function first(int $n): self
650
    {
651 25
        return static::create(
652 25
            $this->utf8::first_char($this->str, $n, $this->encoding),
653 25
            $this->encoding
654
        );
655
    }
656
657
    /**
658
     * Returns the encoding used by the Stringy object.
659
     *
660
     * @return string <p>The current value of the $encoding property.</p>
661
     */
662 5
    public function getEncoding(): string
663
    {
664 5
        return $this->encoding;
665
    }
666
667
    /**
668
     * Returns a new ArrayIterator, thus implementing the IteratorAggregate
669
     * interface. The ArrayIterator's constructor is passed an array of chars
670
     * in the multibyte string. This enables the use of foreach with instances
671
     * of Stringy\Stringy.
672
     *
673
     * @return \ArrayIterator <p>An iterator for the characters in the string.</p>
674
     */
675 2
    public function getIterator(): \ArrayIterator
676
    {
677 2
        return new \ArrayIterator($this->chars());
678
    }
679
680
    /**
681
     * Returns true if the string contains a lower case char, false otherwise.
682
     *
683
     * @return bool <p>Whether or not the string contains a lower case character.</p>
684
     */
685 24
    public function hasLowerCase(): bool
686
    {
687 24
        return $this->utf8::has_lowercase($this->str);
688
    }
689
690
    /**
691
     * Returns true if the string contains an upper case char, false otherwise.
692
     *
693
     * @return bool <p>Whether or not the string contains an upper case character.</p>
694
     */
695 24
    public function hasUpperCase(): bool
696
    {
697 24
        return $this->utf8::has_uppercase($this->str);
698
    }
699
700
    /**
701
     * Convert all HTML entities to their applicable characters.
702
     *
703
     * @param int $flags [optional] <p>
704
     *                   A bitmask of one or more of the following flags, which specify how to handle quotes and
705
     *                   which document type to use. The default is ENT_COMPAT.
706
     *                   <table>
707
     *                   Available <i>flags</i> constants
708
     *                   <tr valign="top">
709
     *                   <td>Constant Name</td>
710
     *                   <td>Description</td>
711
     *                   </tr>
712
     *                   <tr valign="top">
713
     *                   <td><b>ENT_COMPAT</b></td>
714
     *                   <td>Will convert double-quotes and leave single-quotes alone.</td>
715
     *                   </tr>
716
     *                   <tr valign="top">
717
     *                   <td><b>ENT_QUOTES</b></td>
718
     *                   <td>Will convert both double and single quotes.</td>
719
     *                   </tr>
720
     *                   <tr valign="top">
721
     *                   <td><b>ENT_NOQUOTES</b></td>
722
     *                   <td>Will leave both double and single quotes unconverted.</td>
723
     *                   </tr>
724
     *                   <tr valign="top">
725
     *                   <td><b>ENT_HTML401</b></td>
726
     *                   <td>
727
     *                   Handle code as HTML 4.01.
728
     *                   </td>
729
     *                   </tr>
730
     *                   <tr valign="top">
731
     *                   <td><b>ENT_XML1</b></td>
732
     *                   <td>
733
     *                   Handle code as XML 1.
734
     *                   </td>
735
     *                   </tr>
736
     *                   <tr valign="top">
737
     *                   <td><b>ENT_XHTML</b></td>
738
     *                   <td>
739
     *                   Handle code as XHTML.
740
     *                   </td>
741
     *                   </tr>
742
     *                   <tr valign="top">
743
     *                   <td><b>ENT_HTML5</b></td>
744
     *                   <td>
745
     *                   Handle code as HTML 5.
746
     *                   </td>
747
     *                   </tr>
748
     *                   </table>
749
     *                   </p>
750
     *
751
     * @return static <p>Object with the resulting $str after being html decoded.</p>
752
     */
753 10 View Code Duplication
    public function htmlDecode(int $flags = \ENT_COMPAT): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
754
    {
755 10
        return static::create(
756 10
            $this->utf8::html_entity_decode(
757 10
                $this->str,
758
                $flags,
759 10
                $this->encoding
760
            ),
761 10
            $this->encoding
762
        );
763
    }
764
765
    /**
766
     * Convert all applicable characters to HTML entities.
767
     *
768
     * @param int $flags [optional] <p>
769
     *                   A bitmask of one or more of the following flags, which specify how to handle quotes and
770
     *                   which document type to use. The default is ENT_COMPAT.
771
     *                   <table>
772
     *                   Available <i>flags</i> constants
773
     *                   <tr valign="top">
774
     *                   <td>Constant Name</td>
775
     *                   <td>Description</td>
776
     *                   </tr>
777
     *                   <tr valign="top">
778
     *                   <td><b>ENT_COMPAT</b></td>
779
     *                   <td>Will convert double-quotes and leave single-quotes alone.</td>
780
     *                   </tr>
781
     *                   <tr valign="top">
782
     *                   <td><b>ENT_QUOTES</b></td>
783
     *                   <td>Will convert both double and single quotes.</td>
784
     *                   </tr>
785
     *                   <tr valign="top">
786
     *                   <td><b>ENT_NOQUOTES</b></td>
787
     *                   <td>Will leave both double and single quotes unconverted.</td>
788
     *                   </tr>
789
     *                   <tr valign="top">
790
     *                   <td><b>ENT_HTML401</b></td>
791
     *                   <td>
792
     *                   Handle code as HTML 4.01.
793
     *                   </td>
794
     *                   </tr>
795
     *                   <tr valign="top">
796
     *                   <td><b>ENT_XML1</b></td>
797
     *                   <td>
798
     *                   Handle code as XML 1.
799
     *                   </td>
800
     *                   </tr>
801
     *                   <tr valign="top">
802
     *                   <td><b>ENT_XHTML</b></td>
803
     *                   <td>
804
     *                   Handle code as XHTML.
805
     *                   </td>
806
     *                   </tr>
807
     *                   <tr valign="top">
808
     *                   <td><b>ENT_HTML5</b></td>
809
     *                   <td>
810
     *                   Handle code as HTML 5.
811
     *                   </td>
812
     *                   </tr>
813
     *                   </table>
814
     *                   </p>
815
     *
816
     * @return static <p>Object with the resulting $str after being html encoded.</p>
817
     */
818 10 View Code Duplication
    public function htmlEncode(int $flags = \ENT_COMPAT): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
819
    {
820 10
        return static::create(
821 10
            $this->utf8::htmlentities(
822 10
                $this->str,
823
                $flags,
824 10
                $this->encoding
825
            ),
826 10
            $this->encoding
827
        );
828
    }
829
830
    /**
831
     * Capitalizes the first word of the string, replaces underscores with
832
     * spaces, and strips '_id'.
833
     *
834
     * @return static <p>Object with a humanized $str.</p>
835
     */
836 6
    public function humanize(): self
837
    {
838 6
        return static::create(
839 6
            $this->utf8::str_humanize($this->str),
840 6
            $this->encoding
841
        );
842
    }
843
844
    /**
845
     * Returns the index of the first occurrence of $needle in the string,
846
     * and false if not found. Accepts an optional offset from which to begin
847
     * the search.
848
     *
849
     * @param string $needle <p>Substring to look for.</p>
850
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
851
     *
852
     * @return false|int <p>The occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
853
     */
854 20
    public function indexOf(string $needle, int $offset = 0)
855
    {
856 20
        return $this->utf8::strpos(
857 20
            $this->str,
858
            $needle,
859
            $offset,
860 20
            $this->encoding
861
        );
862
    }
863
864
    /**
865
     * Returns the index of the first occurrence of $needle in the string,
866
     * and false if not found. Accepts an optional offset from which to begin
867
     * the search.
868
     *
869
     * @param string $needle <p>Substring to look for.</p>
870
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
871
     *
872
     * @return false|int <p>The occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
873
     */
874
    public function indexOfIgnoreCase(string $needle, int $offset = 0)
875
    {
876
        return $this->utf8::stripos(
877
            $this->str,
878
            $needle,
879
            $offset,
880
            $this->encoding
881
        );
882
    }
883
884
    /**
885
     * Returns the index of the last occurrence of $needle in the string,
886
     * and false if not found. Accepts an optional offset from which to begin
887
     * the search. Offsets may be negative to count from the last character
888
     * in the string.
889
     *
890
     * @param string $needle <p>Substring to look for.</p>
891
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
892
     *
893
     * @return false|int <p>The last occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
894
     */
895 20
    public function indexOfLast(string $needle, int $offset = 0)
896
    {
897 20
        return $this->utf8::strrpos(
898 20
            $this->str,
899
            $needle,
900
            $offset,
901 20
            $this->encoding
902
        );
903
    }
904
905
    /**
906
     * Returns the index of the last occurrence of $needle in the string,
907
     * and false if not found. Accepts an optional offset from which to begin
908
     * the search. Offsets may be negative to count from the last character
909
     * in the string.
910
     *
911
     * @param string $needle <p>Substring to look for.</p>
912
     * @param int    $offset [optional] <p>Offset from which to search. Default: 0</p>
913
     *
914
     * @return false|int <p>The last occurrence's <strong>index</strong> if found, otherwise <strong>false</strong>.</p>
915
     */
916
    public function indexOfLastIgnoreCase(string $needle, int $offset = 0)
917
    {
918
        return $this->utf8::strripos(
919
            $this->str,
920
            $needle,
921
            $offset,
922
            $this->encoding
923
        );
924
    }
925
926
    /**
927
     * Inserts $substring into the string at the $index provided.
928
     *
929
     * @param string $substring <p>String to be inserted.</p>
930
     * @param int    $index     <p>The index at which to insert the substring.</p>
931
     *
932
     * @return static <p>Object with the resulting $str after the insertion.</p>
933
     */
934 16
    public function insert(string $substring, int $index): self
935
    {
936 16
        return static::create(
937 16
            $this->utf8::str_insert(
938 16
                $this->str,
939
                $substring,
940
                $index,
941 16
                $this->encoding
942
            ),
943 16
            $this->encoding
944
        );
945
    }
946
947
    /**
948
     * Returns true if the string contains the $pattern, otherwise false.
949
     *
950
     * WARNING: Asterisks ("*") are translated into (".*") zero-or-more regular
951
     * expression wildcards.
952
     *
953
     * @credit Originally from Laravel, thanks Taylor.
954
     *
955
     * @param string $pattern <p>The string or pattern to match against.</p>
956
     *
957
     * @return bool <p>Whether or not we match the provided pattern.</p>
958
     */
959 13
    public function is(string $pattern): bool
960
    {
961 13
        if ($this->toString() === $pattern) {
962 1
            return true;
963
        }
964
965 12
        $quotedPattern = \preg_quote($pattern, '/');
966 12
        $replaceWildCards = \str_replace('\*', '.*', $quotedPattern);
967
968 12
        return $this->matchesPattern('^' . $replaceWildCards . '\z');
969
    }
970
971
    /**
972
     * Returns true if the string contains only alphabetic chars, false otherwise.
973
     *
974
     * @return bool <p>Whether or not $str contains only alphabetic chars.</p>
975
     */
976 20
    public function isAlpha(): bool
977
    {
978 20
        return $this->utf8::is_alpha($this->str);
979
    }
980
981
    /**
982
     * Returns true if the string contains only alphabetic and numeric chars, false otherwise.
983
     *
984
     * @return bool <p>Whether or not $str contains only alphanumeric chars.</p>
985
     */
986 26
    public function isAlphanumeric(): bool
987
    {
988 26
        return $this->utf8::is_alphanumeric($this->str);
989
    }
990
991
    /**
992
     * Returns true if the string is base64 encoded, false otherwise.
993
     *
994
     * @param bool $emptyStringIsValid
995
     *
996
     * @return bool <p>Whether or not $str is base64 encoded.</p>
997
     */
998 14
    public function isBase64($emptyStringIsValid = true): bool
999
    {
1000 14
        return $this->utf8::is_base64($this->str, $emptyStringIsValid);
1001
    }
1002
1003
    /**
1004
     * Returns true if the string contains only whitespace chars, false otherwise.
1005
     *
1006
     * @return bool <p>Whether or not $str contains only whitespace characters.</p>
1007
     */
1008 30
    public function isBlank(): bool
1009
    {
1010 30
        return $this->utf8::is_blank($this->str);
1011
    }
1012
1013
    /**
1014
     * Returns true if the string contains a valid E-Mail address, false otherwise.
1015
     *
1016
     * @param bool $useExampleDomainCheck   [optional] <p>Default: false</p>
1017
     * @param bool $useTypoInDomainCheck    [optional] <p>Default: false</p>
1018
     * @param bool $useTemporaryDomainCheck [optional] <p>Default: false</p>
1019
     * @param bool $useDnsCheck             [optional] <p>Default: false</p>
1020
     *
1021
     * @return bool <p>Whether or not $str contains a valid E-Mail address.</p>
1022
     */
1023 1
    public function isEmail(bool $useExampleDomainCheck = false, bool $useTypoInDomainCheck = false, bool $useTemporaryDomainCheck = false, bool $useDnsCheck = false): bool
1024
    {
1025 1
        return EmailCheck::isValid($this->str, $useExampleDomainCheck, $useTypoInDomainCheck, $useTemporaryDomainCheck, $useDnsCheck);
1026
    }
1027
1028
    /**
1029
     * Determine whether the string is considered to be empty.
1030
     *
1031
     * A variable is considered empty if it does not exist or if its value equals FALSE.
1032
     * empty() does not generate a warning if the variable does not exist.
1033
     *
1034
     * @return bool <p>Whether or not $str is empty().</p>
1035
     */
1036
    public function isEmpty(): bool
1037
    {
1038
        return $this->utf8::is_empty($this->str);
1039
    }
1040
1041
    /**
1042
     * Returns true if the string contains only hexadecimal chars, false otherwise.
1043
     *
1044
     * @return bool <p>Whether or not $str contains only hexadecimal chars.</p>
1045
     */
1046 26
    public function isHexadecimal(): bool
1047
    {
1048 26
        return $this->utf8::is_hexadecimal($this->str);
1049
    }
1050
1051
    /**
1052
     * Returns true if the string contains HTML-Tags, false otherwise.
1053
     *
1054
     * @return bool <p>Whether or not $str contains HTML-Tags.</p>
1055
     */
1056 1
    public function isHtml(): bool
1057
    {
1058 1
        return $this->utf8::is_html($this->str);
1059
    }
1060
1061
    /**
1062
     * Returns true if the string is JSON, false otherwise. Unlike json_decode
1063
     * in PHP 5.x, this method is consistent with PHP 7 and other JSON parsers,
1064
     * in that an empty string is not considered valid JSON.
1065
     *
1066
     * @param bool $onlyArrayOrObjectResultsAreValid
1067
     *
1068
     * @return bool <p>Whether or not $str is JSON.</p>
1069
     */
1070 40
    public function isJson($onlyArrayOrObjectResultsAreValid = false): bool
1071
    {
1072 40
        return $this->utf8::is_json($this->str, $onlyArrayOrObjectResultsAreValid);
1073
    }
1074
1075
    /**
1076
     * Returns true if the string contains only lower case chars, false otherwise.
1077
     *
1078
     * @return bool <p>Whether or not $str contains only lower case characters.</p>
1079
     */
1080 16
    public function isLowerCase(): bool
1081
    {
1082 16
        return $this->utf8::is_lowercase($this->str);
1083
    }
1084
1085
    /**
1086
     * Returns true if the string is serialized, false otherwise.
1087
     *
1088
     * @return bool <p>Whether or not $str is serialized.</p>
1089
     */
1090 14
    public function isSerialized(): bool
1091
    {
1092 14
        return $this->utf8::is_serialized($this->str);
1093
    }
1094
1095
    /**
1096
     * Returns true if the string contains only lower case chars, false
1097
     * otherwise.
1098
     *
1099
     * @return bool <p>Whether or not $str contains only lower case characters.</p>
1100
     */
1101 16
    public function isUpperCase(): bool
1102
    {
1103 16
        return $this->utf8::is_uppercase($this->str);
1104
    }
1105
1106
    /**
1107
     * Returns the last $n characters of the string.
1108
     *
1109
     * @param int $n <p>Number of characters to retrieve from the end.</p>
1110
     *
1111
     * @return static <p>Object with its $str being the last $n chars.</p>
1112
     */
1113 24
    public function last(int $n): self
1114
    {
1115 24
        return static::create(
1116 24
            $this->utf8::str_last_char(
1117 24
                $this->str,
1118
                $n,
1119 24
                $this->encoding
1120
            ),
1121 24
            $this->encoding
1122
        );
1123
    }
1124
1125
    /**
1126
     * Gets the substring after (or before via "$beforeNeedle") the last occurrence of the "$needle".
1127
     * If no match is found returns new empty Stringy object.
1128
     *
1129
     * @param string $needle       <p>The string to look for.</p>
1130
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
1131
     *
1132
     * @return static
1133
     */
1134 2 View Code Duplication
    public function lastSubstringOf(string $needle, bool $beforeNeedle = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1135
    {
1136 2
        return static::create(
1137 2
            $this->utf8::str_substr_last($this->str, $needle, $beforeNeedle, $this->encoding),
1138 2
            $this->encoding
1139
        );
1140
    }
1141
1142
    /**
1143
     * Gets the substring after (or before via "$beforeNeedle") the last occurrence of the "$needle".
1144
     * If no match is found returns new empty Stringy object.
1145
     *
1146
     * @param string $needle       <p>The string to look for.</p>
1147
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
1148
     *
1149
     * @return static
1150
     */
1151 1 View Code Duplication
    public function lastSubstringOfIgnoreCase(string $needle, bool $beforeNeedle = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1152
    {
1153 1
        return static::create(
1154 1
            $this->utf8::str_isubstr_last($this->str, $needle, $beforeNeedle, $this->encoding),
1155 1
            $this->encoding
1156
        );
1157
    }
1158
1159
    /**
1160
     * Returns the length of the string.
1161
     *
1162
     * @return int <p>The number of characters in $str given the encoding.</p>
1163
     */
1164 11
    public function length(): int
1165
    {
1166 11
        return $this->utf8::strlen($this->str, $this->encoding);
1167
    }
1168
1169
    /**
1170
     * Line-Wrap the string after $limit, but also after the next word.
1171
     *
1172
     * @param int $limit
1173
     *
1174
     * @return static
1175
     */
1176 1
    public function lineWrapAfterWord(int $limit): self
1177
    {
1178 1
        return static::create(
1179 1
            $this->utf8::wordwrap_per_line($this->str, $limit),
1180 1
            $this->encoding
1181
        );
1182
    }
1183
1184
    /**
1185
     * Splits on newlines and carriage returns, returning an array of Stringy
1186
     * objects corresponding to the lines in the string.
1187
     *
1188
     * @return static[] <p>An array of Stringy objects.</p>
1189
     */
1190 32
    public function lines(): array
1191
    {
1192 32
        $array = $this->utf8::str_to_lines($this->str);
1193 32
        foreach ($array as $i => &$value) {
1194 32
            $value = static::create($value, $this->encoding);
1195
        }
1196
1197 32
        return $array;
1198
    }
1199
1200
    /**
1201
     * Returns the longest common prefix between the string and $otherStr.
1202
     *
1203
     * @param string $otherStr <p>Second string for comparison.</p>
1204
     *
1205
     * @return static <p>Object with its $str being the longest common prefix.</p>
1206
     */
1207 20
    public function longestCommonPrefix(string $otherStr): self
1208
    {
1209 20
        return static::create(
1210 20
            $this->utf8::str_longest_common_prefix(
1211 20
                $this->str,
1212
                $otherStr,
1213 20
                $this->encoding
1214
            ),
1215 20
            $this->encoding
1216
        );
1217
    }
1218
1219
    /**
1220
     * Returns the longest common substring between the string and $otherStr.
1221
     * In the case of ties, it returns that which occurs first.
1222
     *
1223
     * @param string $otherStr <p>Second string for comparison.</p>
1224
     *
1225
     * @return static <p>Object with its $str being the longest common substring.</p>
1226
     */
1227 20
    public function longestCommonSubstring(string $otherStr): self
1228
    {
1229 20
        return static::create(
1230 20
            $this->utf8::str_longest_common_substring(
1231 20
                $this->str,
1232
                $otherStr,
1233 20
                $this->encoding
1234
            ),
1235 20
            $this->encoding
1236
        );
1237
    }
1238
1239
    /**
1240
     * Returns the longest common suffix between the string and $otherStr.
1241
     *
1242
     * @param string $otherStr <p>Second string for comparison.</p>
1243
     *
1244
     * @return static <p>Object with its $str being the longest common suffix.</p>
1245
     */
1246 20
    public function longestCommonSuffix(string $otherStr): self
1247
    {
1248 20
        return static::create(
1249 20
            $this->utf8::str_longest_common_suffix(
1250 20
                $this->str,
1251
                $otherStr,
1252 20
                $this->encoding
1253
            ),
1254 20
            $this->encoding
1255
        );
1256
    }
1257
1258
    /**
1259
     * Converts the first character of the string to lower case.
1260
     *
1261
     * @return static <p>Object with the first character of $str being lower case.</p>
1262
     */
1263 10
    public function lowerCaseFirst(): self
1264
    {
1265 10
        return static::create(
1266 10
            $this->utf8::lcfirst($this->str, $this->encoding),
1267 10
            $this->encoding
1268
        );
1269
    }
1270
1271
    /**
1272
     * Returns whether or not a character exists at an index. Offsets may be
1273
     * negative to count from the last character in the string. Implements
1274
     * part of the ArrayAccess interface.
1275
     *
1276
     * @param int $offset <p>The index to check.</p>
1277
     *
1278
     * @return bool <p>Whether or not the index exists.</p>
1279
     */
1280 12
    public function offsetExists($offset): bool
1281
    {
1282 12
        return $this->utf8::str_offset_exists(
1283 12
            $this->str,
1284
            $offset,
1285 12
            $this->encoding
1286
        );
1287
    }
1288
1289
    /**
1290
     * Returns the character at the given index. Offsets may be negative to
1291
     * count from the last character in the string. Implements part of the
1292
     * ArrayAccess interface, and throws an OutOfBoundsException if the index
1293
     * does not exist.
1294
     *
1295
     * @param int $offset <p>The <strong>index</strong> from which to retrieve the char.</p>
1296
     *
1297
     * @throws \OutOfBoundsException <p>If the positive or negative offset does not exist.</p>
1298
     *
1299
     * @return string <p>The character at the specified index.</p>
1300
     */
1301 4
    public function offsetGet($offset): string
1302
    {
1303 4
        return $this->utf8::str_offset_get($this->str, $offset, $this->encoding);
1304
    }
1305
1306
    /**
1307
     * Implements part of the ArrayAccess interface, but throws an exception
1308
     * when called. This maintains the immutability of Stringy objects.
1309
     *
1310
     * @param int   $offset <p>The index of the character.</p>
1311
     * @param mixed $value  <p>Value to set.</p>
1312
     *
1313
     * @throws \Exception <p>When called.</p>
1314
     */
1315 2
    public function offsetSet($offset, $value)
1316
    {
1317
        // Stringy is immutable, cannot directly set char
1318
        /** @noinspection ThrowRawExceptionInspection */
1319 2
        throw new \Exception('Stringy object is immutable, cannot modify char');
1320
    }
1321
1322
    /**
1323
     * Implements part of the ArrayAccess interface, but throws an exception
1324
     * when called. This maintains the immutability of Stringy objects.
1325
     *
1326
     * @param int $offset <p>The index of the character.</p>
1327
     *
1328
     * @throws \Exception <p>When called.</p>
1329
     */
1330 2
    public function offsetUnset($offset)
1331
    {
1332
        // Don't allow directly modifying the string
1333
        /** @noinspection ThrowRawExceptionInspection */
1334 2
        throw new \Exception('Stringy object is immutable, cannot unset char');
1335
    }
1336
1337
    /**
1338
     * Pads the string to a given length with $padStr. If length is less than
1339
     * or equal to the length of the string, no padding takes places. The
1340
     * default string used for padding is a space, and the default type (one of
1341
     * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
1342
     * if $padType isn't one of those 3 values.
1343
     *
1344
     * @param int    $length  <p>Desired string length after padding.</p>
1345
     * @param string $padStr  [optional] <p>String used to pad, defaults to space. Default: ' '</p>
1346
     * @param string $padType [optional] <p>One of 'left', 'right', 'both'. Default: 'right'</p>
1347
     *
1348
     * @throws \InvalidArgumentException <p>If $padType isn't one of 'right', 'left' or 'both'.</p>
1349
     *
1350
     * @return static <p>Object with a padded $str.</p>
1351
     */
1352 26
    public function pad(int $length, string $padStr = ' ', string $padType = 'right'): self
1353
    {
1354 26
        return static::create(
1355 26
            $this->utf8::str_pad(
1356 26
                $this->str,
1357
                $length,
1358
                $padStr,
1359
                $padType,
1360 26
                $this->encoding
1361
            )
1362
        );
1363
    }
1364
1365
    /**
1366
     * Returns a new string of a given length such that both sides of the
1367
     * string are padded. Alias for pad() with a $padType of 'both'.
1368
     *
1369
     * @param int    $length <p>Desired string length after padding.</p>
1370
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
1371
     *
1372
     * @return static <p>String with padding applied.</p>
1373
     */
1374 22
    public function padBoth(int $length, string $padStr = ' '): self
1375
    {
1376 22
        return static::create(
1377 22
            $this->utf8::str_pad_both(
1378 22
                $this->str,
1379
                $length,
1380
                $padStr,
1381 22
                $this->encoding
1382
            )
1383
        );
1384
    }
1385
1386
    /**
1387
     * Returns a new string of a given length such that the beginning of the
1388
     * string is padded. Alias for pad() with a $padType of 'left'.
1389
     *
1390
     * @param int    $length <p>Desired string length after padding.</p>
1391
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
1392
     *
1393
     * @return static <p>String with left padding.</p>
1394
     */
1395 14
    public function padLeft(int $length, string $padStr = ' '): self
1396
    {
1397 14
        return static::create(
1398 14
            $this->utf8::str_pad_left(
1399 14
                $this->str,
1400
                $length,
1401
                $padStr,
1402 14
                $this->encoding
1403
            )
1404
        );
1405
    }
1406
1407
    /**
1408
     * Returns a new string of a given length such that the end of the string
1409
     * is padded. Alias for pad() with a $padType of 'right'.
1410
     *
1411
     * @param int    $length <p>Desired string length after padding.</p>
1412
     * @param string $padStr [optional] <p>String used to pad, defaults to space. Default: ' '</p>
1413
     *
1414
     * @return static <p>String with right padding.</p>
1415
     */
1416 14
    public function padRight(int $length, string $padStr = ' '): self
1417
    {
1418 14
        return static::create(
1419 14
            $this->utf8::str_pad_right(
1420 14
                $this->str,
1421
                $length,
1422
                $padStr,
1423 14
                $this->encoding
1424
            )
1425
        );
1426
    }
1427
1428
    /**
1429
     * Returns a new string starting with $string.
1430
     *
1431
     * @param string $string <p>The string to append.</p>
1432
     *
1433
     * @return static <p>Object with appended $string.</p>
1434
     */
1435 4
    public function prepend(string $string): self
1436
    {
1437 4
        return static::create($string . $this->str, $this->encoding);
1438
    }
1439
1440
    /**
1441
     * Replaces all occurrences of $pattern in $str by $replacement.
1442
     *
1443
     * @param string $pattern     <p>The regular expression pattern.</p>
1444
     * @param string $replacement <p>The string to replace with.</p>
1445
     * @param string $options     [optional] <p>Matching conditions to be used.</p>
1446
     * @param string $delimiter   [optional] <p>Delimiter the the regex. Default: '/'</p>
1447
     *
1448
     * @return static <p>Object with the result2ing $str after the replacements.</p>
1449
     */
1450 19
    public function regexReplace(string $pattern, string $replacement, string $options = '', string $delimiter = '/'): self
1451
    {
1452 19
        return static::create(
1453 19
            $this->utf8::regex_replace(
1454 19
                $this->str,
1455
                $pattern,
1456
                $replacement,
1457
                $options,
1458
                $delimiter
1459
            ),
1460 19
            $this->encoding
1461
        );
1462
    }
1463
1464
    /**
1465
     * Remove html via "strip_tags()" from the string.
1466
     *
1467
     * @param string $allowableTags [optional] <p>You can use the optional second parameter to specify tags which should
1468
     *                              not be stripped. Default: null
1469
     *                              </p>
1470
     *
1471
     * @return static
1472
     */
1473 6
    public function removeHtml(string $allowableTags = null): self
1474
    {
1475 6
        return static::create(
1476 6
            $this->utf8::remove_html($this->str, $allowableTags . ''),
1477 6
            $this->encoding
1478
        );
1479
    }
1480
1481
    /**
1482
     * Remove all breaks [<br> | \r\n | \r | \n | ...] from the string.
1483
     *
1484
     * @param string $replacement [optional] <p>Default is a empty string.</p>
1485
     *
1486
     * @return static
1487
     */
1488 6
    public function removeHtmlBreak(string $replacement = ''): self
1489
    {
1490 6
        return static::create(
1491 6
            $this->utf8::remove_html_breaks($this->str, $replacement),
1492 6
            $this->encoding
1493
        );
1494
    }
1495
1496
    /**
1497
     * Returns a new string with the prefix $substring removed, if present.
1498
     *
1499
     * @param string $substring <p>The prefix to remove.</p>
1500
     *
1501
     * @return static <p>Object having a $str without the prefix $substring.</p>
1502
     */
1503 24
    public function removeLeft(string $substring): self
1504
    {
1505 24
        return static::create(
1506 24
            $this->utf8::remove_left($this->str, $substring, $this->encoding),
1507 24
            $this->encoding
1508
        );
1509
    }
1510
1511
    /**
1512
     * Returns a new string with the suffix $substring removed, if present.
1513
     *
1514
     * @param string $substring <p>The suffix to remove.</p>
1515
     *
1516
     * @return static <p>Object having a $str without the suffix $substring.</p>
1517
     */
1518 24
    public function removeRight(string $substring): self
1519
    {
1520 24
        return static::create(
1521 24
            $this->utf8::remove_right($this->str, $substring, $this->encoding),
1522 24
            $this->encoding
1523
        );
1524
    }
1525
1526
    /**
1527
     * Try to remove all XSS-attacks from the string.
1528
     *
1529
     * @return static
1530
     */
1531 6
    public function removeXss(): self
1532
    {
1533 6
        static $antiXss = null;
1534
1535 6
        if ($antiXss === null) {
1536 1
            $antiXss = new AntiXSS();
1537
        }
1538
1539 6
        $str = $antiXss->xss_clean($this->str);
1540
1541 6
        return static::create($str, $this->encoding);
1542
    }
1543
1544
    /**
1545
     * Returns a repeated string given a multiplier.
1546
     *
1547
     * @param int $multiplier <p>The number of times to repeat the string.</p>
1548
     *
1549
     * @return static <p>Object with a repeated str.</p>
1550
     */
1551 14
    public function repeat(int $multiplier): self
1552
    {
1553 14
        return static::create(
1554 14
            \str_repeat($this->str, $multiplier),
1555 14
            $this->encoding
1556
        );
1557
    }
1558
1559
    /**
1560
     * Replaces all occurrences of $search in $str by $replacement.
1561
     *
1562
     * @param string $search        <p>The needle to search for.</p>
1563
     * @param string $replacement   <p>The string to replace with.</p>
1564
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1565
     *
1566
     * @return static <p>Object with the resulting $str after the replacements.</p>
1567
     */
1568 45
    public function replace(string $search, string $replacement, bool $caseSensitive = true): self
1569
    {
1570 45
        if ($search === '' && $replacement === '') {
1571 10
            return static::create($this->str, $this->encoding);
1572
        }
1573
1574 35
        if ($this->str === '' && $search === '') {
1575 2
            return static::create($replacement, $this->encoding);
1576
        }
1577
1578 33
        if ($caseSensitive) {
1579 28
            return static::create(
1580 28
                $this->utf8::str_replace($search, $replacement, $this->str),
1581 28
                $this->encoding
1582
            );
1583
        }
1584
1585 5
        return static::create(
1586 5
            $this->utf8::str_ireplace($search, $replacement, $this->str),
1587 5
            $this->encoding
1588
        );
1589
    }
1590
1591
    /**
1592
     * Replaces all occurrences of $search in $str by $replacement.
1593
     *
1594
     * @param array        $search        <p>The elements to search for.</p>
1595
     * @param array|string $replacement   <p>The string to replace with.</p>
1596
     * @param bool         $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1597
     *
1598
     * @return static <p>Object with the resulting $str after the replacements.</p>
1599
     */
1600 30
    public function replaceAll(array $search, $replacement, bool $caseSensitive = true): self
1601
    {
1602 30
        if ($caseSensitive) {
1603 23
            return static::create(
1604 23
                $this->utf8::str_replace($search, $replacement, $this->str),
1605 23
                $this->encoding
1606
            );
1607
        }
1608
1609 7
        return static::create(
1610 7
            $this->utf8::str_ireplace($search, $replacement, $this->str),
1611 7
            $this->encoding
1612
        );
1613
    }
1614
1615
    /**
1616
     * Replaces first occurrences of $search from the beginning of string with $replacement.
1617
     *
1618
     * @param string $search      <p>The string to search for.</p>
1619
     * @param string $replacement <p>The replacement.</p>
1620
     *
1621
     * @return static <p>Object with the resulting $str after the replacements.</p>
1622
     */
1623 16
    public function replaceFirst(string $search, string $replacement): self
1624
    {
1625 16
        return static::create(
1626 16
            $this->utf8::str_replace_first($search, $replacement, $this->str),
1627 16
            $this->encoding
1628
        );
1629
    }
1630
1631
    /**
1632
     * Replaces last occurrences of $search from the ending of string with $replacement.
1633
     *
1634
     * @param string $search      <p>The string to search for.</p>
1635
     * @param string $replacement <p>The replacement.</p>
1636
     *
1637
     * @return static <p>Object with the resulting $str after the replacements.</p>
1638
     */
1639 15
    public function replaceLast(string $search, string $replacement): self
1640
    {
1641 15
        return static::create(
1642 15
            $this->utf8::str_replace_last($search, $replacement, $this->str),
1643 15
            $this->encoding
1644
        );
1645
    }
1646
1647
    /**
1648
     * Replaces all occurrences of $search from the beginning of string with $replacement.
1649
     *
1650
     * @param string $search      <p>The string to search for.</p>
1651
     * @param string $replacement <p>The replacement.</p>
1652
     *
1653
     * @return static <p>Object with the resulting $str after the replacements.</p>
1654
     */
1655 16
    public function replaceBeginning(string $search, string $replacement): self
1656
    {
1657 16
        return static::create(
1658 16
            $this->utf8::str_replace_beginning($this->str, $search, $replacement),
1659 16
            $this->encoding
1660
        );
1661
    }
1662
1663
    /**
1664
     * Replaces all occurrences of $search from the ending of string with $replacement.
1665
     *
1666
     * @param string $search      <p>The string to search for.</p>
1667
     * @param string $replacement <p>The replacement.</p>
1668
     *
1669
     * @return static <p>Object with the resulting $str after the replacements.</p>
1670
     */
1671 16
    public function replaceEnding(string $search, string $replacement): self
1672
    {
1673 16
        return static::create(
1674 16
            $this->utf8::str_replace_ending($this->str, $search, $replacement),
1675 16
            $this->encoding
1676
        );
1677
    }
1678
1679
    /**
1680
     * Returns a reversed string. A multibyte version of strrev().
1681
     *
1682
     * @return static <p>Object with a reversed $str.</p>
1683
     */
1684 10
    public function reverse(): self
1685
    {
1686 10
        return static::create($this->utf8::strrev($this->str), $this->encoding);
1687
    }
1688
1689
    /**
1690
     * Truncates the string to a given length, while ensuring that it does not
1691
     * split words. If $substring is provided, and truncating occurs, the
1692
     * string is further truncated so that the substring may be appended without
1693
     * exceeding the desired length.
1694
     *
1695
     * @param int    $length    <p>Desired length of the truncated string.</p>
1696
     * @param string $substring [optional] <p>The substring to append if it can fit. Default: ''</p>
1697
     * @param bool   $ignoreDoNotSplitWordsForOneWord
1698
     *
1699
     * @return static <p>Object with the resulting $str after truncating.</p>
1700
     */
1701 45
    public function safeTruncate(int $length, string $substring = '', bool $ignoreDoNotSplitWordsForOneWord = true): self
1702
    {
1703 45
        return static::create(
1704 45
            $this->utf8::str_truncate_safe(
1705 45
                $this->str,
1706
                $length,
1707
                $substring,
1708 45
                $this->encoding,
1709
                $ignoreDoNotSplitWordsForOneWord
1710
            ),
1711 45
            $this->encoding
1712
        );
1713
    }
1714
1715
    /**
1716
     * Shorten the string after $length, but also after the next word.
1717
     *
1718
     * @param int    $length
1719
     * @param string $strAddOn [optional] <p>Default: '…'</p>
1720
     *
1721
     * @return static
1722
     */
1723 4
    public function shortenAfterWord(int $length, string $strAddOn = '…'): self
1724
    {
1725 4
        return static::create(
1726 4
            $this->utf8::str_limit_after_word($this->str, $length, $strAddOn),
1727 4
            $this->encoding
1728
        );
1729
    }
1730
1731
    /**
1732
     * A multibyte string shuffle function. It returns a string with its
1733
     * characters in random order.
1734
     *
1735
     * @return static <p>Object with a shuffled $str.</p>
1736
     */
1737 6
    public function shuffle(): self
1738
    {
1739 6
        return static::create($this->utf8::str_shuffle($this->str), $this->encoding);
1740
    }
1741
1742
    /**
1743
     * Returns the substring beginning at $start, and up to, but not including
1744
     * the index specified by $end. If $end is omitted, the function extracts
1745
     * the remaining string. If $end is negative, it is computed from the end
1746
     * of the string.
1747
     *
1748
     * @param int $start <p>Initial index from which to begin extraction.</p>
1749
     * @param int $end   [optional] <p>Index at which to end extraction. Default: null</p>
1750
     *
1751
     * @return static <p>Object with its $str being the extracted substring.</p>
1752
     */
1753 34
    public function slice(int $start, int $end = null): self
1754
    {
1755 34
        return static::create(
1756 34
            $this->utf8::str_slice($this->str, $start, $end, $this->encoding),
1757 34
            $this->encoding
1758
        );
1759
    }
1760
1761
    /**
1762
     * Converts the string into an URL slug. This includes replacing non-ASCII
1763
     * characters with their closest ASCII equivalents, removing remaining
1764
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
1765
     * $replacement. The replacement defaults to a single dash, and the string
1766
     * is also converted to lowercase. The language of the source string can
1767
     * also be supplied for language-specific transliteration.
1768
     *
1769
     * @param string $replacement The string used to replace whitespace
1770
     * @param string $language    Language of the source string
1771
     *
1772
     * @return static Object whose $str has been converted to an URL slug
1773
     */
1774 16
    public function slugify(string $replacement = '-', string $language = 'en'): self
1775
    {
1776 16
        $stringy = self::create($this->str);
1777
1778 16
        $split = \preg_split('/[-_]/', $language);
1779 16
        $language = \strtolower($split[0]);
1780
        $languageSpecific = [
1781 16
            'de' => [['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE']],
1782
            'bg' => [
1783
                ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
1784
                ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
1785
            ],
1786
        ];
1787 16
        if (!empty($languageSpecific[$language])) {
1788
            $stringy->str = \str_replace($languageSpecific[$language][0], $languageSpecific[$language][1], $stringy->str);
1789
        }
1790
1791 16
        foreach ($this->charsArray() as $key => $value) {
1792 16
            $stringy->str = \str_replace($value, $key, $stringy->str);
1793
        }
1794 16
        $stringy->str = \str_replace('@', $replacement, $stringy->str);
1795
1796 16
        $stringy->str = \preg_replace(
1797 16
            '/[^a-zA-Z\d\s\-_' . \preg_quote($replacement, '/') . ']/u',
1798 16
            '',
1799 16
            $stringy->str
1800
        );
1801 16
        $stringy->str = \preg_replace("/^['\s']+|['\s']+\$/", '', \strtolower($stringy->str));
1802 16
        $stringy->str = \preg_replace('/\B([A-Z])/', '/-\1/', $stringy->str);
1803 16
        $stringy->str = \preg_replace('/[-_\s]+/', $replacement, $stringy->str);
1804
1805 16
        $l = \strlen($replacement);
1806 16 View Code Duplication
        if (\strpos($stringy->str, $replacement) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1807 1
            $stringy->str = \substr($stringy->str, $l);
1808
        }
1809
1810 16 View Code Duplication
        if (\substr($stringy->str, -$l) === $replacement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1811 1
            $stringy->str = \substr($stringy->str, 0, \strlen($stringy->str) - $l);
1812
        }
1813
1814 16
        return static::create($stringy->str, $this->encoding);
1815
    }
1816
1817
    /**
1818
     * Converts the string into an URL slug. This includes replacing non-ASCII
1819
     * characters with their closest ASCII equivalents, removing remaining
1820
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
1821
     * $replacement. The replacement defaults to a single dash, and the string
1822
     * is also converted to lowercase.
1823
     *
1824
     * @param string $replacement [optional] <p>The string used to replace whitespace. Default: '-'</p>
1825
     * @param string $language    [optional] <p>The language for the url. Default: 'de'</p>
1826
     * @param bool   $strToLower  [optional] <p>string to lower. Default: true</p>
1827
     *
1828
     * @return static <p>Object whose $str has been converted to an URL slug.</p>
1829
     */
1830 15
    public function urlify(string $replacement = '-', string $language = 'de', bool $strToLower = true): self
1831
    {
1832 15
        return static::create(
1833 15
            URLify::slug($this->str, $language, $replacement, $strToLower),
1834 15
            $this->encoding
1835
        );
1836
    }
1837
1838
    /**
1839
     * Convert a string to e.g.: "snake_case"
1840
     *
1841
     * @return static <p>Object with $str in snake_case.</p>
1842
     */
1843 20
    public function snakeize(): self
1844
    {
1845 20
        return static::create(
1846 20
            $this->utf8::str_snakeize($this->str, $this->encoding),
1847 20
            $this->encoding
1848
        );
1849
    }
1850
1851
    /**
1852
     * Splits the string with the provided regular expression, returning an
1853
     * array of Stringy objects. An optional integer $limit will truncate the
1854
     * results.
1855
     *
1856
     * @param string $pattern <p>The regex with which to split the string.</p>
1857
     * @param int    $limit   [optional] <p>Maximum number of results to return. Default: -1 === no limit</p>
1858
     *
1859
     * @return static[] <p>An array of Stringy objects.</p>
1860
     */
1861 35
    public function split(string $pattern, int $limit = null): array
1862
    {
1863 35
        if ($limit === null) {
1864 7
            $limit = -1;
1865
        }
1866
1867 35
        $array = $this->utf8::str_split_pattern($this->str, $pattern, $limit);
1868 35
        foreach ($array as $i => &$value) {
1869 31
            $value = static::create($value, $this->encoding);
1870
        }
1871
1872 35
        return $array;
1873
    }
1874
1875
    /**
1876
     * Returns true if the string begins with $substring, false otherwise. By
1877
     * default, the comparison is case-sensitive, but can be made insensitive
1878
     * by setting $caseSensitive to false.
1879
     *
1880
     * @param string $substring     <p>The substring to look for.</p>
1881
     * @param bool   $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1882
     *
1883
     * @return bool <p>Whether or not $str starts with $substring.</p>
1884
     */
1885 22
    public function startsWith(string $substring, bool $caseSensitive = true): bool
1886
    {
1887 22
        if ($caseSensitive) {
1888 14
            return $this->utf8::str_starts_with($this->str, $substring);
1889
        }
1890
1891 8
        return $this->utf8::str_istarts_with($this->str, $substring);
1892
    }
1893
1894
    /**
1895
     * Returns true if the string begins with any of $substrings, false otherwise.
1896
     * By default the comparison is case-sensitive, but can be made insensitive by
1897
     * setting $caseSensitive to false.
1898
     *
1899
     * @param array $substrings    <p>Substrings to look for.</p>
1900
     * @param bool  $caseSensitive [optional] <p>Whether or not to enforce case-sensitivity. Default: true</p>
1901
     *
1902
     * @return bool <p>Whether or not $str starts with $substring.</p>
1903
     */
1904 23
    public function startsWithAny(array $substrings, bool $caseSensitive = true): bool
1905
    {
1906 23
        if ($caseSensitive) {
1907 15
            return $this->utf8::str_starts_with_any($this->str, $substrings);
1908
        }
1909
1910 8
        return $this->utf8::str_istarts_with_any($this->str, $substrings);
1911
    }
1912
1913
    /**
1914
     * Strip all whitespace characters. This includes tabs and newline characters,
1915
     * as well as multibyte whitespace such as the thin space and ideographic space.
1916
     *
1917
     * @return static
1918
     */
1919 24
    public function stripWhitespace(): self
1920
    {
1921 24
        return static::create(
1922 24
            $this->utf8::strip_whitespace($this->str),
1923 24
            $this->encoding
1924
        );
1925
    }
1926
1927
    /**
1928
     * Remove css media-queries.
1929
     *
1930
     * @return static
1931
     */
1932 1
    public function stripeCssMediaQueries(): self
1933
    {
1934 1
        return static::create(
1935 1
            $this->utf8::css_stripe_media_queries($this->str),
1936 1
            $this->encoding
1937
        );
1938
    }
1939
1940
    /**
1941
     * Remove empty html-tag.
1942
     *
1943
     * e.g.: <tag></tag>
1944
     *
1945
     * @return static
1946
     */
1947 1
    public function stripeEmptyHtmlTags(): self
1948
    {
1949 1
        return static::create(
1950 1
            $this->utf8::html_stripe_empty_tags($this->str),
1951 1
            $this->encoding
1952
        );
1953
    }
1954
1955
    /**
1956
     * Returns the substring beginning at $start with the specified $length.
1957
     * It differs from the $this->utf8::substr() function in that providing a $length of
1958
     * null will return the rest of the string, rather than an empty string.
1959
     *
1960
     * @param int $start  <p>Position of the first character to use.</p>
1961
     * @param int $length [optional] <p>Maximum number of characters used. Default: null</p>
1962
     *
1963
     * @return static <p>Object with its $str being the substring.</p>
1964
     */
1965 18
    public function substr(int $start, int $length = null): self
1966
    {
1967 18
        return static::create(
1968 18
            $this->utf8::substr(
1969 18
                $this->str,
1970
                $start,
1971
                $length,
1972 18
                $this->encoding
1973
            ),
1974 18
            $this->encoding
1975
        );
1976
    }
1977
1978
    /**
1979
     * Gets the substring after (or before via "$beforeNeedle") the first occurrence of the "$needle".
1980
     * If no match is found returns new empty Stringy object.
1981
     *
1982
     * @param string $needle       <p>The string to look for.</p>
1983
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
1984
     *
1985
     * @return static
1986
     */
1987 2 View Code Duplication
    public function substringOf(string $needle, bool $beforeNeedle = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1988
    {
1989 2
        return static::create(
1990 2
            $this->utf8::str_substr_first($this->str, $needle, $beforeNeedle, $this->encoding),
1991 2
            $this->encoding
1992
        );
1993
    }
1994
1995
    /**
1996
     * Gets the substring after (or before via "$beforeNeedle") the first occurrence of the "$needle".
1997
     * If no match is found returns new empty Stringy object.
1998
     *
1999
     * @param string $needle       <p>The string to look for.</p>
2000
     * @param bool   $beforeNeedle [optional] <p>Default: false</p>
2001
     *
2002
     * @return static
2003
     */
2004 2 View Code Duplication
    public function substringOfIgnoreCase(string $needle, bool $beforeNeedle = false): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2005
    {
2006 2
        return static::create(
2007 2
            $this->utf8::str_isubstr_first($this->str, $needle, $beforeNeedle, $this->encoding),
2008 2
            $this->encoding
2009
        );
2010
    }
2011
2012
    /**
2013
     * Surrounds $str with the given substring.
2014
     *
2015
     * @param string $substring <p>The substring to add to both sides.</P>
2016
     *
2017
     * @return static <p>Object whose $str had the substring both prepended and appended.</p>
2018
     */
2019 10
    public function surround(string $substring): self
2020
    {
2021 10
        return static::create(
2022 10
            $substring . $this->str . $substring,
2023 10
            $this->encoding
2024
        );
2025
    }
2026
2027
    /**
2028
     * Returns a case swapped version of the string.
2029
     *
2030
     * @return static <p>Object whose $str has each character's case swapped.</P>
2031
     */
2032 10
    public function swapCase(): self
2033
    {
2034 10
        return static::create(
2035 10
            $this->utf8::swapCase($this->str, $this->encoding),
2036 10
            $this->encoding
2037
        );
2038
    }
2039
2040
    /**
2041
     * Returns a string with smart quotes, ellipsis characters, and dashes from
2042
     * Windows-1252 (commonly used in Word documents) replaced by their ASCII
2043
     * equivalents.
2044
     *
2045
     * @return static <p>Object whose $str has those characters removed.</p>
2046
     */
2047 8
    public function tidy(): self
2048
    {
2049 8
        return static::create(
2050 8
            $this->utf8::normalize_msword($this->str),
2051 8
            $this->encoding
2052
        );
2053
    }
2054
2055
    /**
2056
     * Returns a trimmed string with the first letter of each word capitalized.
2057
     * Also accepts an array, $ignore, allowing you to list words not to be
2058
     * capitalized.
2059
     *
2060
     * @param array|null $ignore [optional] <p>An array of words not to capitalize or null. Default: null</p>
2061
     *
2062
     * @return static <p>Object with a titleized $str.</p>
2063
     */
2064 10
    public function titleize(array $ignore = null): self
2065
    {
2066 10
        return static::create(
2067 10
            $this->utf8::str_titleize($this->str, $ignore, $this->encoding),
2068 10
            $this->encoding
2069
        );
2070
    }
2071
2072
    /**
2073
     * Returns a trimmed string in proper title case.
2074
     *
2075
     * Also accepts an array, $ignore, allowing you to list words not to be
2076
     * capitalized.
2077
     *
2078
     * Adapted from John Gruber's script.
2079
     *
2080
     * @see https://gist.github.com/gruber/9f9e8650d68b13ce4d78
2081
     *
2082
     * @param array $ignore <p>An array of words not to capitalize.</p>
2083
     *
2084
     * @return static <p>Object with a titleized $str</p>
2085
     */
2086 35
    public function titleizeForHumans(array $ignore = []): self
2087
    {
2088 35
        return static::create(
2089 35
            $this->utf8::str_titleize_for_humans($this->str, $ignore, $this->encoding),
2090 35
            $this->encoding
2091
        );
2092
    }
2093
2094
    /**
2095
     * Returns an ASCII version of the string. A set of non-ASCII characters are
2096
     * replaced with their closest ASCII counterparts, and the rest are removed
2097
     * unless instructed otherwise.
2098
     *
2099
     * @param bool $strict [optional] <p>Use "transliterator_transliterate()" from PHP-Intl | WARNING: bad performance |
2100
     *                     Default: false</p>
2101
     *
2102
     * @return static <p>Object whose $str contains only ASCII characters.</p>
2103
     */
2104 16
    public function toTransliterate(bool $strict = false): self
2105
    {
2106 16
        return static::create(
2107 16
            $this->utf8::to_ascii($this->str, '?', $strict),
2108 16
            $this->encoding
2109
        );
2110
    }
2111
2112
    /**
2113
     * Returns an ASCII version of the string. A set of non-ASCII characters are
2114
     * replaced with their closest ASCII counterparts, and the rest are removed
2115
     * by default. The language or locale of the source string can be supplied
2116
     * for language-specific transliteration in any of the following formats:
2117
     * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping
2118
     * to "aeoeue" rather than "aou" as in other languages.
2119
     *
2120
     * @param string $language          Language of the source string
2121
     * @param bool   $removeUnsupported Whether or not to remove the
2122
     *                                  unsupported characters
2123
     *
2124
     * @return static Object whose $str contains only ASCII characters
2125
     */
2126 21
    public function toAscii(string $language = 'en', bool $removeUnsupported = true)
2127
    {
2128
        // init
2129 21
        $str = $this->str;
2130
2131 21
        $langSpecific = self::langSpecificCharsArray($language);
2132 21
        if (!empty($langSpecific)) {
2133 2
            $str = \str_replace($langSpecific[0], $langSpecific[1], $str);
2134
        }
2135
2136 21
        foreach ($this->charsArray() as $key => $value) {
2137 21
            $str = \str_replace($value, $key, $str);
2138
        }
2139
2140 21
        if ($removeUnsupported) {
2141 20
            $str = \preg_replace('/[^\x20-\x7E]/u', '', $str);
2142
        }
2143
2144 21
        return static::create($str, $this->encoding);
2145
    }
2146
2147
    /**
2148
     * Returns a boolean representation of the given logical string value.
2149
     * For example, 'true', '1', 'on' and 'yes' will return true. 'false', '0',
2150
     * 'off', and 'no' will return false. In all instances, case is ignored.
2151
     * For other numeric strings, their sign will determine the return value.
2152
     * In addition, blank strings consisting of only whitespace will return
2153
     * false. For all other strings, the return value is a result of a
2154
     * boolean cast.
2155
     *
2156
     * @return bool <p>A boolean value for the string.</p>
2157
     */
2158 30
    public function toBoolean(): bool
2159
    {
2160 30
        return $this->utf8::to_boolean($this->str);
2161
    }
2162
2163
    /**
2164
     * Converts all characters in the string to lowercase.
2165
     *
2166
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
2167
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
2168
     *
2169
     * @return static <p>Object with all characters of $str being lowercase.</p>
2170
     */
2171 12 View Code Duplication
    public function toLowerCase($tryToKeepStringLength = false, $lang = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2172
    {
2173 12
        return static::create(
2174 12
            $this->utf8::strtolower(
2175 12
                $this->str, $this->encoding, false, $lang, $tryToKeepStringLength
2176
            ),
2177 12
            $this->encoding
2178
        );
2179
    }
2180
2181
    /**
2182
     * Converts each tab in the string to some number of spaces, as defined by
2183
     * $tabLength. By default, each tab is converted to 4 consecutive spaces.
2184
     *
2185
     * @param int $tabLength [optional] <p>Number of spaces to replace each tab with. Default: 4</p>
2186
     *
2187
     * @return static <p>Object whose $str has had tabs switched to spaces.</p>
2188
     */
2189 12 View Code Duplication
    public function toSpaces(int $tabLength = 4): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2190
    {
2191 12
        if ($tabLength === 4) {
2192 6
            $tab = '    ';
2193 6
        } elseif ($tabLength === 2) {
2194 2
            $tab = '  ';
2195
        } else {
2196 4
            $tab = \str_repeat(' ', $tabLength);
2197
        }
2198
2199 12
        return static::create(
2200 12
            \str_replace("\t", $tab, $this->str),
2201 12
            $this->encoding
2202
        );
2203
    }
2204
2205
    /**
2206
     * Return Stringy object as string, but you can also use (string) for automatically casting the object into a
2207
     * string.
2208
     *
2209
     * @return string
2210
     */
2211 1071
    public function toString(): string
2212
    {
2213 1071
        return (string) $this->str;
2214
    }
2215
2216
    /**
2217
     * Converts each occurrence of some consecutive number of spaces, as
2218
     * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
2219
     * are converted to a tab.
2220
     *
2221
     * @param int $tabLength [optional] <p>Number of spaces to replace with a tab. Default: 4</p>
2222
     *
2223
     * @return static <p>Object whose $str has had spaces switched to tabs.</p>
2224
     */
2225 10 View Code Duplication
    public function toTabs(int $tabLength = 4): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2226
    {
2227 10
        if ($tabLength === 4) {
2228 6
            $tab = '    ';
2229 4
        } elseif ($tabLength === 2) {
2230 2
            $tab = '  ';
2231
        } else {
2232 2
            $tab = \str_repeat(' ', $tabLength);
2233
        }
2234
2235 10
        return static::create(
2236 10
            \str_replace($tab, "\t", $this->str),
2237 10
            $this->encoding
2238
        );
2239
    }
2240
2241
    /**
2242
     * Converts the first character of each word in the string to uppercase
2243
     * and all other chars to lowercase.
2244
     *
2245
     * @return static <p>Object with all characters of $str being title-cased.</p>
2246
     */
2247 10
    public function toTitleCase(): self
2248
    {
2249 10
        return static::create(
2250 10
            $this->utf8::titlecase($this->str, $this->encoding),
2251 10
            $this->encoding
2252
        );
2253
    }
2254
2255
    /**
2256
     * Converts all characters in the string to uppercase.
2257
     *
2258
     * @param bool        $tryToKeepStringLength [optional] <p>true === try to keep the string length: e.g. ẞ -> ß</p>
2259
     * @param string|null $lang                  [optional] <p>Set the language for special cases: az, el, lt, tr</p>
2260
     *
2261
     * @return static <p>Object with all characters of $str being uppercase.</p>
2262
     */
2263 10 View Code Duplication
    public function toUpperCase($tryToKeepStringLength = false, $lang = null): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2264
    {
2265 10
        return static::create(
2266 10
            $this->utf8::strtoupper($this->str, $this->encoding, false, $lang, $tryToKeepStringLength),
2267 10
            $this->encoding
2268
        );
2269
    }
2270
2271
    /**
2272
     * Returns a string with whitespace removed from the start and end of the
2273
     * string. Supports the removal of unicode whitespace. Accepts an optional
2274
     * string of characters to strip instead of the defaults.
2275
     *
2276
     * @param string $chars [optional] <p>String of characters to strip. Default: null</p>
2277
     *
2278
     * @return static <p>Object with a trimmed $str.</p>
2279
     */
2280 24
    public function trim(string $chars = null): self
2281
    {
2282 24
        return static::create(
2283 24
            $this->utf8::trim($this->str, $chars),
2284 24
            $this->encoding
2285
        );
2286
    }
2287
2288
    /**
2289
     * Returns a string with whitespace removed from the start of the string.
2290
     * Supports the removal of unicode whitespace. Accepts an optional
2291
     * string of characters to strip instead of the defaults.
2292
     *
2293
     * @param string $chars [optional] <p>Optional string of characters to strip. Default: null</p>
2294
     *
2295
     * @return static <p>Object with a trimmed $str.</p>
2296
     */
2297 26
    public function trimLeft(string $chars = null): self
2298
    {
2299 26
        return static::create(
2300 26
            $this->utf8::ltrim($this->str, $chars),
2301 26
            $this->encoding
2302
        );
2303
    }
2304
2305
    /**
2306
     * Returns a string with whitespace removed from the end of the string.
2307
     * Supports the removal of unicode whitespace. Accepts an optional
2308
     * string of characters to strip instead of the defaults.
2309
     *
2310
     * @param string $chars [optional] <p>Optional string of characters to strip. Default: null</p>
2311
     *
2312
     * @return static <p>Object with a trimmed $str.</p>
2313
     */
2314 26
    public function trimRight(string $chars = null): self
2315
    {
2316 26
        return static::create(
2317 26
            $this->utf8::rtrim($this->str, $chars),
2318 26
            $this->encoding
2319
        );
2320
    }
2321
2322
    /**
2323
     * Truncates the string to a given length. If $substring is provided, and
2324
     * truncating occurs, the string is further truncated so that the substring
2325
     * may be appended without exceeding the desired length.
2326
     *
2327
     * @param int    $length    <p>Desired length of the truncated string.</p>
2328
     * @param string $substring [optional] <p>The substring to append if it can fit. Default: ''</p>
2329
     *
2330
     * @return static <p>Object with the resulting $str after truncating.</p>
2331
     */
2332 44
    public function truncate(int $length, string $substring = ''): self
2333
    {
2334 44
        return static::create(
2335 44
            $this->utf8::str_truncate($this->str, $length, $substring, $this->encoding),
2336 44
            $this->encoding
2337
        );
2338
    }
2339
2340
    /**
2341
     * Returns a lowercase and trimmed string separated by underscores.
2342
     * Underscores are inserted before uppercase characters (with the exception
2343
     * of the first character of the string), and in place of spaces as well as
2344
     * dashes.
2345
     *
2346
     * @return static <p>Object with an underscored $str.</p>
2347
     */
2348 32
    public function underscored(): self
2349
    {
2350 32
        return $this->delimit('_');
2351
    }
2352
2353
    /**
2354
     * Returns an UpperCamelCase version of the supplied string. It trims
2355
     * surrounding spaces, capitalizes letters following digits, spaces, dashes
2356
     * and underscores, and removes spaces, dashes, underscores.
2357
     *
2358
     * @return static <p>Object with $str in UpperCamelCase.</p>
2359
     */
2360 26
    public function upperCamelize(): self
2361
    {
2362 26
        return static::create(
2363 26
            $this->utf8::str_upper_camelize($this->str, $this->encoding),
2364 26
            $this->encoding
2365
        );
2366
    }
2367
2368
    /**
2369
     * Converts the first character of the supplied string to upper case.
2370
     *
2371
     * @return static <p>Object with the first character of $str being upper case.</p>
2372
     */
2373 12
    public function upperCaseFirst(): self
2374
    {
2375 12
        return static::create($this->utf8::ucfirst($this->str, $this->encoding), $this->encoding);
2376
    }
2377
2378
    /**
2379
     * Converts the string into an valid UTF-8 string.
2380
     *
2381
     * @return static
2382
     */
2383 1
    public function utf8ify(): self
2384
    {
2385 1
        return static::create($this->utf8::cleanup($this->str), $this->encoding);
2386
    }
2387
2388
    /**
2389
     * Returns the replacements for the toAscii() method.
2390
     *
2391
     * @return array an array of replacements
2392
     */
2393 37
    protected function charsArray(): array
2394
    {
2395 37
        static $charsArray;
2396
2397 37
        if (isset($charsArray)) {
2398 36
            return $charsArray;
2399
        }
2400
2401
        return $charsArray = [
2402 1
            '0'    => ['°', '₀', '۰', '0'],
2403
            '1'    => ['¹', '₁', '۱', '1'],
2404
            '2'    => ['²', '₂', '۲', '2'],
2405
            '3'    => ['³', '₃', '۳', '3'],
2406
            '4'    => ['⁴', '₄', '۴', '٤', '4'],
2407
            '5'    => ['⁵', '₅', '۵', '٥', '5'],
2408
            '6'    => ['⁶', '₆', '۶', '٦', '6'],
2409
            '7'    => ['⁷', '₇', '۷', '7'],
2410
            '8'    => ['⁸', '₈', '۸', '8'],
2411
            '9'    => ['⁹', '₉', '۹', '9'],
2412
            'a'    => [
2413
                'à',
2414
                'á',
2415
                'ả',
2416
                'ã',
2417
                'ạ',
2418
                'ă',
2419
                'ắ',
2420
                'ằ',
2421
                'ẳ',
2422
                'ẵ',
2423
                'ặ',
2424
                'â',
2425
                'ấ',
2426
                'ầ',
2427
                'ẩ',
2428
                'ẫ',
2429
                'ậ',
2430
                'ā',
2431
                'ą',
2432
                'å',
2433
                'α',
2434
                'ά',
2435
                'ἀ',
2436
                'ἁ',
2437
                'ἂ',
2438
                'ἃ',
2439
                'ἄ',
2440
                'ἅ',
2441
                'ἆ',
2442
                'ἇ',
2443
                'ᾀ',
2444
                'ᾁ',
2445
                'ᾂ',
2446
                'ᾃ',
2447
                'ᾄ',
2448
                'ᾅ',
2449
                'ᾆ',
2450
                'ᾇ',
2451
                'ὰ',
2452
                'ά',
2453
                'ᾰ',
2454
                'ᾱ',
2455
                'ᾲ',
2456
                'ᾳ',
2457
                'ᾴ',
2458
                'ᾶ',
2459
                'ᾷ',
2460
                'а',
2461
                'أ',
2462
                'အ',
2463
                'ာ',
2464
                'ါ',
2465
                'ǻ',
2466
                'ǎ',
2467
                'ª',
2468
                'ა',
2469
                'अ',
2470
                'ا',
2471
                'a',
2472
                'ä',
2473
            ],
2474
            'b'    => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
2475
            'c'    => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
2476
            'd'    => [
2477
                'ď',
2478
                'ð',
2479
                'đ',
2480
                'ƌ',
2481
                'ȡ',
2482
                'ɖ',
2483
                'ɗ',
2484
                'ᵭ',
2485
                'ᶁ',
2486
                'ᶑ',
2487
                'д',
2488
                'δ',
2489
                'د',
2490
                'ض',
2491
                'ဍ',
2492
                'ဒ',
2493
                'დ',
2494
                'd',
2495
            ],
2496
            'e'    => [
2497
                'é',
2498
                'è',
2499
                'ẻ',
2500
                'ẽ',
2501
                'ẹ',
2502
                'ê',
2503
                'ế',
2504
                'ề',
2505
                'ể',
2506
                'ễ',
2507
                'ệ',
2508
                'ë',
2509
                'ē',
2510
                'ę',
2511
                'ě',
2512
                'ĕ',
2513
                'ė',
2514
                'ε',
2515
                'έ',
2516
                'ἐ',
2517
                'ἑ',
2518
                'ἒ',
2519
                'ἓ',
2520
                'ἔ',
2521
                'ἕ',
2522
                'ὲ',
2523
                'έ',
2524
                'е',
2525
                'ё',
2526
                'э',
2527
                'є',
2528
                'ə',
2529
                'ဧ',
2530
                'ေ',
2531
                'ဲ',
2532
                'ე',
2533
                'ए',
2534
                'إ',
2535
                'ئ',
2536
                'e',
2537
            ],
2538
            'f'    => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
2539
            'g'    => [
2540
                'ĝ',
2541
                'ğ',
2542
                'ġ',
2543
                'ģ',
2544
                'г',
2545
                'ґ',
2546
                'γ',
2547
                'ဂ',
2548
                'გ',
2549
                'گ',
2550
                'g',
2551
            ],
2552
            'h'    => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
2553
            'i'    => [
2554
                'í',
2555
                'ì',
2556
                'ỉ',
2557
                'ĩ',
2558
                'ị',
2559
                'î',
2560
                'ï',
2561
                'ī',
2562
                'ĭ',
2563
                'į',
2564
                'ı',
2565
                'ι',
2566
                'ί',
2567
                'ϊ',
2568
                'ΐ',
2569
                'ἰ',
2570
                'ἱ',
2571
                'ἲ',
2572
                'ἳ',
2573
                'ἴ',
2574
                'ἵ',
2575
                'ἶ',
2576
                'ἷ',
2577
                'ὶ',
2578
                'ί',
2579
                'ῐ',
2580
                'ῑ',
2581
                'ῒ',
2582
                'ΐ',
2583
                'ῖ',
2584
                'ῗ',
2585
                'і',
2586
                'ї',
2587
                'и',
2588
                'ဣ',
2589
                'ိ',
2590
                'ီ',
2591
                'ည်',
2592
                'ǐ',
2593
                'ი',
2594
                'इ',
2595
                'ی',
2596
                'i',
2597
            ],
2598
            'j'    => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
2599
            'k'    => [
2600
                'ķ',
2601
                'ĸ',
2602
                'к',
2603
                'κ',
2604
                'Ķ',
2605
                'ق',
2606
                'ك',
2607
                'က',
2608
                'კ',
2609
                'ქ',
2610
                'ک',
2611
                'k',
2612
            ],
2613
            'l'    => [
2614
                'ł',
2615
                'ľ',
2616
                'ĺ',
2617
                'ļ',
2618
                'ŀ',
2619
                'л',
2620
                'λ',
2621
                'ل',
2622
                'လ',
2623
                'ლ',
2624
                'l',
2625
            ],
2626
            'm'    => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
2627
            'n'    => [
2628
                'ñ',
2629
                'ń',
2630
                'ň',
2631
                'ņ',
2632
                'ʼn',
2633
                'ŋ',
2634
                'ν',
2635
                'н',
2636
                'ن',
2637
                'န',
2638
                'ნ',
2639
                'n',
2640
            ],
2641
            'o'    => [
2642
                'ó',
2643
                'ò',
2644
                'ỏ',
2645
                'õ',
2646
                'ọ',
2647
                'ô',
2648
                'ố',
2649
                'ồ',
2650
                'ổ',
2651
                'ỗ',
2652
                'ộ',
2653
                'ơ',
2654
                'ớ',
2655
                'ờ',
2656
                'ở',
2657
                'ỡ',
2658
                'ợ',
2659
                'ø',
2660
                'ō',
2661
                'ő',
2662
                'ŏ',
2663
                'ο',
2664
                'ὀ',
2665
                'ὁ',
2666
                'ὂ',
2667
                'ὃ',
2668
                'ὄ',
2669
                'ὅ',
2670
                'ὸ',
2671
                'ό',
2672
                'о',
2673
                'و',
2674
                'θ',
2675
                'ို',
2676
                'ǒ',
2677
                'ǿ',
2678
                'º',
2679
                'ო',
2680
                'ओ',
2681
                'o',
2682
                'ö',
2683
            ],
2684
            'p'    => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
2685
            'q'    => ['ყ', 'q'],
2686
            'r'    => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
2687
            's'    => [
2688
                'ś',
2689
                'š',
2690
                'ş',
2691
                'с',
2692
                'σ',
2693
                'ș',
2694
                'ς',
2695
                'س',
2696
                'ص',
2697
                'စ',
2698
                'ſ',
2699
                'ს',
2700
                's',
2701
            ],
2702
            't'    => [
2703
                'ť',
2704
                'ţ',
2705
                'т',
2706
                'τ',
2707
                'ț',
2708
                'ت',
2709
                'ط',
2710
                'ဋ',
2711
                'တ',
2712
                'ŧ',
2713
                'თ',
2714
                'ტ',
2715
                't',
2716
            ],
2717
            'u'    => [
2718
                'ú',
2719
                'ù',
2720
                'ủ',
2721
                'ũ',
2722
                'ụ',
2723
                'ư',
2724
                'ứ',
2725
                'ừ',
2726
                'ử',
2727
                'ữ',
2728
                'ự',
2729
                'û',
2730
                'ū',
2731
                'ů',
2732
                'ű',
2733
                'ŭ',
2734
                'ų',
2735
                'µ',
2736
                'у',
2737
                'ဉ',
2738
                'ု',
2739
                'ူ',
2740
                'ǔ',
2741
                'ǖ',
2742
                'ǘ',
2743
                'ǚ',
2744
                'ǜ',
2745
                'უ',
2746
                'उ',
2747
                'u',
2748
                'ў',
2749
                'ü',
2750
            ],
2751
            'v'    => ['в', 'ვ', 'ϐ', 'v'],
2752
            'w'    => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
2753
            'x'    => ['χ', 'ξ', 'x'],
2754
            'y'    => [
2755
                'ý',
2756
                'ỳ',
2757
                'ỷ',
2758
                'ỹ',
2759
                'ỵ',
2760
                'ÿ',
2761
                'ŷ',
2762
                'й',
2763
                'ы',
2764
                'υ',
2765
                'ϋ',
2766
                'ύ',
2767
                'ΰ',
2768
                'ي',
2769
                'ယ',
2770
                'y',
2771
            ],
2772
            'z'    => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
2773
            'aa'   => ['ع', 'आ', 'آ'],
2774
            'ae'   => ['æ', 'ǽ'],
2775
            'ai'   => ['ऐ'],
2776
            'ch'   => ['ч', 'ჩ', 'ჭ', 'چ'],
2777
            'dj'   => ['ђ', 'đ'],
2778
            'dz'   => ['џ', 'ძ'],
2779
            'ei'   => ['ऍ'],
2780
            'gh'   => ['غ', 'ღ'],
2781
            'ii'   => ['ई'],
2782
            'ij'   => ['ij'],
2783
            'kh'   => ['х', 'خ', 'ხ'],
2784
            'lj'   => ['љ'],
2785
            'nj'   => ['њ'],
2786
            'oe'   => ['œ', 'ؤ'],
2787
            'oi'   => ['ऑ'],
2788
            'oii'  => ['ऒ'],
2789
            'ps'   => ['ψ'],
2790
            'sh'   => ['ш', 'შ', 'ش'],
2791
            'shch' => ['щ'],
2792
            'ss'   => ['ß'],
2793
            'sx'   => ['ŝ'],
2794
            'th'   => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
2795
            'ts'   => ['ц', 'ც', 'წ'],
2796
            'uu'   => ['ऊ'],
2797
            'ya'   => ['я'],
2798
            'yu'   => ['ю'],
2799
            'zh'   => ['ж', 'ჟ', 'ژ'],
2800
            '(c)'  => ['©'],
2801
            'A'    => [
2802
                'Á',
2803
                'À',
2804
                'Ả',
2805
                'Ã',
2806
                'Ạ',
2807
                'Ă',
2808
                'Ắ',
2809
                'Ằ',
2810
                'Ẳ',
2811
                'Ẵ',
2812
                'Ặ',
2813
                'Â',
2814
                'Ấ',
2815
                'Ầ',
2816
                'Ẩ',
2817
                'Ẫ',
2818
                'Ậ',
2819
                'Å',
2820
                'Ā',
2821
                'Ą',
2822
                'Α',
2823
                'Ά',
2824
                'Ἀ',
2825
                'Ἁ',
2826
                'Ἂ',
2827
                'Ἃ',
2828
                'Ἄ',
2829
                'Ἅ',
2830
                'Ἆ',
2831
                'Ἇ',
2832
                'ᾈ',
2833
                'ᾉ',
2834
                'ᾊ',
2835
                'ᾋ',
2836
                'ᾌ',
2837
                'ᾍ',
2838
                'ᾎ',
2839
                'ᾏ',
2840
                'Ᾰ',
2841
                'Ᾱ',
2842
                'Ὰ',
2843
                'Ά',
2844
                'ᾼ',
2845
                'А',
2846
                'Ǻ',
2847
                'Ǎ',
2848
                'A',
2849
                'Ä',
2850
            ],
2851
            'B'    => ['Б', 'Β', 'ब', 'B'],
2852
            'C'    => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
2853
            'D'    => [
2854
                'Ď',
2855
                'Ð',
2856
                'Đ',
2857
                'Ɖ',
2858
                'Ɗ',
2859
                'Ƌ',
2860
                'ᴅ',
2861
                'ᴆ',
2862
                'Д',
2863
                'Δ',
2864
                'D',
2865
            ],
2866
            'E'    => [
2867
                'É',
2868
                'È',
2869
                'Ẻ',
2870
                'Ẽ',
2871
                'Ẹ',
2872
                'Ê',
2873
                'Ế',
2874
                'Ề',
2875
                'Ể',
2876
                'Ễ',
2877
                'Ệ',
2878
                'Ë',
2879
                'Ē',
2880
                'Ę',
2881
                'Ě',
2882
                'Ĕ',
2883
                'Ė',
2884
                'Ε',
2885
                'Έ',
2886
                'Ἐ',
2887
                'Ἑ',
2888
                'Ἒ',
2889
                'Ἓ',
2890
                'Ἔ',
2891
                'Ἕ',
2892
                'Έ',
2893
                'Ὲ',
2894
                'Е',
2895
                'Ё',
2896
                'Э',
2897
                'Є',
2898
                'Ə',
2899
                'E',
2900
            ],
2901
            'F'    => ['Ф', 'Φ', 'F'],
2902
            'G'    => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
2903
            'H'    => ['Η', 'Ή', 'Ħ', 'H'],
2904
            'I'    => [
2905
                'Í',
2906
                'Ì',
2907
                'Ỉ',
2908
                'Ĩ',
2909
                'Ị',
2910
                'Î',
2911
                'Ï',
2912
                'Ī',
2913
                'Ĭ',
2914
                'Į',
2915
                'İ',
2916
                'Ι',
2917
                'Ί',
2918
                'Ϊ',
2919
                'Ἰ',
2920
                'Ἱ',
2921
                'Ἳ',
2922
                'Ἴ',
2923
                'Ἵ',
2924
                'Ἶ',
2925
                'Ἷ',
2926
                'Ῐ',
2927
                'Ῑ',
2928
                'Ὶ',
2929
                'Ί',
2930
                'И',
2931
                'І',
2932
                'Ї',
2933
                'Ǐ',
2934
                'ϒ',
2935
                'I',
2936
            ],
2937
            'J'    => ['J'],
2938
            'K'    => ['К', 'Κ', 'K'],
2939
            'L'    => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
2940
            'M'    => ['М', 'Μ', 'M'],
2941
            'N'    => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
2942
            'O'    => [
2943
                'Ó',
2944
                'Ò',
2945
                'Ỏ',
2946
                'Õ',
2947
                'Ọ',
2948
                'Ô',
2949
                'Ố',
2950
                'Ồ',
2951
                'Ổ',
2952
                'Ỗ',
2953
                'Ộ',
2954
                'Ơ',
2955
                'Ớ',
2956
                'Ờ',
2957
                'Ở',
2958
                'Ỡ',
2959
                'Ợ',
2960
                'Ø',
2961
                'Ō',
2962
                'Ő',
2963
                'Ŏ',
2964
                'Ο',
2965
                'Ό',
2966
                'Ὀ',
2967
                'Ὁ',
2968
                'Ὂ',
2969
                'Ὃ',
2970
                'Ὄ',
2971
                'Ὅ',
2972
                'Ὸ',
2973
                'Ό',
2974
                'О',
2975
                'Θ',
2976
                'Ө',
2977
                'Ǒ',
2978
                'Ǿ',
2979
                'O',
2980
                'Ö',
2981
            ],
2982
            'P'    => ['П', 'Π', 'P'],
2983
            'Q'    => ['Q'],
2984
            'R'    => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
2985
            'S'    => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
2986
            'T'    => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
2987
            'U'    => [
2988
                'Ú',
2989
                'Ù',
2990
                'Ủ',
2991
                'Ũ',
2992
                'Ụ',
2993
                'Ư',
2994
                'Ứ',
2995
                'Ừ',
2996
                'Ử',
2997
                'Ữ',
2998
                'Ự',
2999
                'Û',
3000
                'Ū',
3001
                'Ů',
3002
                'Ű',
3003
                'Ŭ',
3004
                'Ų',
3005
                'У',
3006
                'Ǔ',
3007
                'Ǖ',
3008
                'Ǘ',
3009
                'Ǚ',
3010
                'Ǜ',
3011
                'U',
3012
                'Ў',
3013
                'Ü',
3014
            ],
3015
            'V'    => ['В', 'V'],
3016
            'W'    => ['Ω', 'Ώ', 'Ŵ', 'W'],
3017
            'X'    => ['Χ', 'Ξ', 'X'],
3018
            'Y'    => [
3019
                'Ý',
3020
                'Ỳ',
3021
                'Ỷ',
3022
                'Ỹ',
3023
                'Ỵ',
3024
                'Ÿ',
3025
                'Ῠ',
3026
                'Ῡ',
3027
                'Ὺ',
3028
                'Ύ',
3029
                'Ы',
3030
                'Й',
3031
                'Υ',
3032
                'Ϋ',
3033
                'Ŷ',
3034
                'Y',
3035
            ],
3036
            'Z'    => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
3037
            'AE'   => ['Æ', 'Ǽ'],
3038
            'Ch'   => ['Ч'],
3039
            'Dj'   => ['Ђ'],
3040
            'Dz'   => ['Џ'],
3041
            'Gx'   => ['Ĝ'],
3042
            'Hx'   => ['Ĥ'],
3043
            'Ij'   => ['IJ'],
3044
            'Jx'   => ['Ĵ'],
3045
            'Kh'   => ['Х'],
3046
            'Lj'   => ['Љ'],
3047
            'Nj'   => ['Њ'],
3048
            'Oe'   => ['Œ'],
3049
            'Ps'   => ['Ψ'],
3050
            'Sh'   => ['Ш'],
3051
            'Shch' => ['Щ'],
3052
            'Ss'   => ['ẞ'],
3053
            'Th'   => ['Þ'],
3054
            'Ts'   => ['Ц'],
3055
            'Ya'   => ['Я'],
3056
            'Yu'   => ['Ю'],
3057
            'Zh'   => ['Ж'],
3058
            ' '    => [
3059
                "\xC2\xA0",
3060
                "\xE2\x80\x80",
3061
                "\xE2\x80\x81",
3062
                "\xE2\x80\x82",
3063
                "\xE2\x80\x83",
3064
                "\xE2\x80\x84",
3065
                "\xE2\x80\x85",
3066
                "\xE2\x80\x86",
3067
                "\xE2\x80\x87",
3068
                "\xE2\x80\x88",
3069
                "\xE2\x80\x89",
3070
                "\xE2\x80\x8A",
3071
                "\xE2\x80\xAF",
3072
                "\xE2\x81\x9F",
3073
                "\xE3\x80\x80",
3074
                "\xEF\xBE\xA0",
3075
            ],
3076
        ];
3077
    }
3078
3079
    /**
3080
     * Returns true if $str matches the supplied pattern, false otherwise.
3081
     *
3082
     * @param string $pattern <p>Regex pattern to match against.</p>
3083
     *
3084
     * @return bool <p>Whether or not $str matches the pattern.</p>
3085
     */
3086 12
    protected function matchesPattern(string $pattern): bool
3087
    {
3088 12
        return $this->utf8::str_matches_pattern($this->str, $pattern);
3089
    }
3090
3091
    /**
3092
     * Returns language-specific replacements for the toAscii() method.
3093
     * For example, German will map 'ä' to 'ae', while other languages
3094
     * will simply return 'a'.
3095
     *
3096
     * @param string $language Language of the source string
3097
     *
3098
     * @return array an array of replacements
3099
     */
3100 21
    protected static function langSpecificCharsArray(string $language = 'en'): array
3101
    {
3102 21
        $split = \preg_split('/[-_]/', $language);
3103 21
        $language = \strtolower($split[0]);
3104 21
        static $charsArray = [];
3105
3106 21
        if (isset($charsArray[$language])) {
3107 19
            return $charsArray[$language];
3108
        }
3109
3110
        $languageSpecific = [
3111
            'de' => [
3112 2
                ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
3113
                ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
3114
            ],
3115
            'bg' => [
3116
                ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
3117
                ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
3118
            ],
3119
        ];
3120
3121 2
        $charsArray[$language] = $languageSpecific[$language] ?? [];
3122
3123 2
        return $charsArray[$language];
3124
    }
3125
}
3126